summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ui/status/ninja.go109
1 files changed, 76 insertions, 33 deletions
diff --git a/ui/status/ninja.go b/ui/status/ninja.go
index fb760ac8f..7b25d50aa 100644
--- a/ui/status/ninja.go
+++ b/ui/status/ninja.go
@@ -40,10 +40,11 @@ func NewNinjaReader(ctx logger.Logger, status ToolStatus, fifo string) *NinjaRea
}
n := &NinjaReader{
- status: status,
- fifo: fifo,
- done: make(chan bool),
- cancel: make(chan bool),
+ status: status,
+ fifo: fifo,
+ forceClose: make(chan bool),
+ done: make(chan bool),
+ cancelOpen: make(chan bool),
}
go n.run()
@@ -52,10 +53,11 @@ func NewNinjaReader(ctx logger.Logger, status ToolStatus, fifo string) *NinjaRea
}
type NinjaReader struct {
- status ToolStatus
- fifo string
- done chan bool
- cancel chan bool
+ status ToolStatus
+ fifo string
+ forceClose chan bool
+ done chan bool
+ cancelOpen chan bool
}
const NINJA_READER_CLOSE_TIMEOUT = 5 * time.Second
@@ -63,18 +65,34 @@ const NINJA_READER_CLOSE_TIMEOUT = 5 * time.Second
// Close waits for NinjaReader to finish reading from the fifo, or 5 seconds.
func (n *NinjaReader) Close() {
// Signal the goroutine to stop if it is blocking opening the fifo.
- close(n.cancel)
+ close(n.cancelOpen)
+ // Ninja should already have exited or been killed, wait 5 seconds for the FIFO to be closed and any
+ // remaining messages to be processed through the NinjaReader.run goroutine.
timeoutCh := time.After(NINJA_READER_CLOSE_TIMEOUT)
+ select {
+ case <-n.done:
+ return
+ case <-timeoutCh:
+ // Channel is not closed yet
+ }
+
+ n.status.Error(fmt.Sprintf("ninja fifo didn't finish after %s", NINJA_READER_CLOSE_TIMEOUT.String()))
+
+ // Force close the reader even if the FIFO didn't close.
+ close(n.forceClose)
+ // Wait again for the reader thread to acknowledge the close before giving up and assuming it isn't going
+ // to send anything else.
+ timeoutCh = time.After(NINJA_READER_CLOSE_TIMEOUT)
select {
case <-n.done:
- // Nothing
+ return
case <-timeoutCh:
- n.status.Error(fmt.Sprintf("ninja fifo didn't finish after %s", NINJA_READER_CLOSE_TIMEOUT.String()))
+ // Channel is not closed yet
}
- return
+ n.status.Verbose(fmt.Sprintf("ninja fifo didn't finish even after force closing after %s", NINJA_READER_CLOSE_TIMEOUT.String()))
}
func (n *NinjaReader) run() {
@@ -98,7 +116,7 @@ func (n *NinjaReader) run() {
select {
case f = <-fileCh:
// Nothing
- case <-n.cancel:
+ case <-n.cancelOpen:
return
}
@@ -108,33 +126,58 @@ func (n *NinjaReader) run() {
running := map[uint32]*Action{}
- for {
- size, err := readVarInt(r)
- if err != nil {
- if err != io.EOF {
- n.status.Error(fmt.Sprintf("Got error reading from ninja: %s", err))
+ msgChan := make(chan *ninja_frontend.Status)
+
+ // Read from the ninja fifo and decode the protobuf in a goroutine so the main NinjaReader.run goroutine
+ // can listen
+ go func() {
+ defer close(msgChan)
+ for {
+ size, err := readVarInt(r)
+ if err != nil {
+ if err != io.EOF {
+ n.status.Error(fmt.Sprintf("Got error reading from ninja: %s", err))
+ }
+ return
}
- return
- }
- buf := make([]byte, size)
- _, err = io.ReadFull(r, buf)
- if err != nil {
- if err == io.EOF {
- n.status.Print(fmt.Sprintf("Missing message of size %d from ninja\n", size))
- } else {
- n.status.Error(fmt.Sprintf("Got error reading from ninja: %s", err))
+ buf := make([]byte, size)
+ _, err = io.ReadFull(r, buf)
+ if err != nil {
+ if err == io.EOF {
+ n.status.Print(fmt.Sprintf("Missing message of size %d from ninja\n", size))
+ } else {
+ n.status.Error(fmt.Sprintf("Got error reading from ninja: %s", err))
+ }
+ return
}
- return
+
+ msg := &ninja_frontend.Status{}
+ err = proto.Unmarshal(buf, msg)
+ if err != nil {
+ n.status.Print(fmt.Sprintf("Error reading message from ninja: %v", err))
+ continue
+ }
+
+ msgChan <- msg
}
+ }()
- msg := &ninja_frontend.Status{}
- err = proto.Unmarshal(buf, msg)
- if err != nil {
- n.status.Print(fmt.Sprintf("Error reading message from ninja: %v", err))
- continue
+ for {
+ var msg *ninja_frontend.Status
+ var msgOk bool
+ select {
+ case <-n.forceClose:
+ // Close() has been called, but the reader goroutine didn't get EOF after 5 seconds
+ break
+ case msg, msgOk = <-msgChan:
+ // msg is ready or closed
}
+ if !msgOk {
+ // msgChan is closed
+ break
+ }
// Ignore msg.BuildStarted
if msg.TotalEdges != nil {
n.status.SetTotalActions(int(msg.TotalEdges.GetTotalEdges()))