diff options
author | Andrew Gallant <jamslam@gmail.com> | 2015-04-26 19:04:54 -0400 |
---|---|---|
committer | Přemysl Janouch <p@janouch.name> | 2018-09-08 16:49:24 +0200 |
commit | 5451e59f884298fdf97ef4420a7bc1001093c3b7 (patch) | |
tree | d205e50500bb56003ce3116b15942d9853d4884a | |
parent | dd00568d44383890cff03d566c014252e096fbfc (diff) | |
download | haven-5451e59f884298fdf97ef4420a7bc1001093c3b7.tar.gz haven-5451e59f884298fdf97ef4420a7bc1001093c3b7.tar.xz haven-5451e59f884298fdf97ef4420a7bc1001093c3b7.zip |
Remove panics/fatal errors.
Fixes #9.
This makes shutdown a little more graceful, but there's more
work to be done here. Namely, all outstanding cookies need to
be given the error, otherwise they will block forever.
-rw-r--r-- | nexgb/cookie.go | 3 | ||||
-rw-r--r-- | nexgb/xgb.go | 65 |
2 files changed, 28 insertions, 40 deletions
diff --git a/nexgb/cookie.go b/nexgb/cookie.go index 6b1e4fb..d5cdb29 100644 --- a/nexgb/cookie.go +++ b/nexgb/cookie.go @@ -99,7 +99,6 @@ func (c Cookie) replyChecked() ([]byte, error) { case err := <-c.errorChan: return nil, err } - panic("unreachable") } // replyUnchecked waits for a response on either the replyChan or pingChan @@ -123,7 +122,6 @@ func (c Cookie) replyUnchecked() ([]byte, error) { case <-c.pingChan: return nil, nil } - panic("unreachable") } // Check is used for checked requests that have no replies. It is a mechanism @@ -164,5 +162,4 @@ func (c Cookie) Check() error { case <-c.pingChan: return nil } - panic("unreachable") } diff --git a/nexgb/xgb.go b/nexgb/xgb.go index e7f2411..487ae16 100644 --- a/nexgb/xgb.go +++ b/nexgb/xgb.go @@ -331,7 +331,10 @@ func (c *Conn) sendRequests() { // Note that we circumvent the request channel, because we're *in* // the request channel. if len(c.cookieChan) == cookieBuffer-1 { - c.noop() + if err := c.noop(); err != nil { + // Shut everything down. + break + } } req.cookie.Sequence = c.newSequenceId() close(req.seq) @@ -340,26 +343,31 @@ func (c *Conn) sendRequests() { } response := make(chan struct{}) c.closing <- response - c.noop() // Flush the response reading goroutine. + c.noop() // Flush the response reading goroutine, ignore error. <-response c.conn.Close() } // noop circumvents the usual request sending goroutines and forces a round // trip request manually. -func (c *Conn) noop() { +func (c *Conn) noop() error { cookie := c.NewCookie(true, true) cookie.Sequence = c.newSequenceId() c.cookieChan <- cookie - c.writeBuffer(c.getInputFocusRequest()) + if err := c.writeBuffer(c.getInputFocusRequest()); err != nil { + return err + } cookie.Reply() // wait for the buffer to clear + return nil } // writeBuffer is a convenience function for writing a byte slice to the wire. -func (c *Conn) writeBuffer(buf []byte) { +func (c *Conn) writeBuffer(buf []byte) error { if _, err := c.conn.Write(buf); err != nil { - Logger.Printf("Write error: %s", err) - Logger.Fatal("A write error is unrecoverable. Exiting...") + Logger.Printf("A write error is unrecoverable: %s", err) + return err + } else { + return nil } } @@ -377,7 +385,6 @@ func (c *Conn) readResponses() { var ( err Error - event Event seq uint16 replyBytes []byte ) @@ -391,13 +398,13 @@ func (c *Conn) readResponses() { } buf := make([]byte, 32) - err, event, seq = nil, nil, 0 - + err, seq = nil, 0 if _, err := io.ReadFull(c.conn, buf); err != nil { - Logger.Println("A read error is unrecoverable.") - panic(err) + Logger.Printf("A read error is unrecoverable: %s", err) + c.eventChan <- err + c.Close() + continue } - switch buf[0] { case 0: // This is an error // Use the constructor function for this error (that is auto @@ -423,8 +430,10 @@ func (c *Conn) readResponses() { biggerBuf := make([]byte, byteCount) copy(biggerBuf[:32], buf) if _, err := io.ReadFull(c.conn, biggerBuf[32:]); err != nil { - Logger.Printf("Read error: %s", err) - Logger.Fatal("A read error is unrecoverable. Exiting...") + Logger.Printf("A read error is unrecoverable: %s", err) + c.eventChan <- err + c.Close() + continue } replyBytes = biggerBuf } else { @@ -445,25 +454,7 @@ func (c *Conn) readResponses() { "for event with number %d.", evNum) continue } - - event = newEventFun(buf) - - // Put the event into the queue. - // FIXME: I'm not sure if using a goroutine here to guarantee - // a non-blocking send is the right way to go. I should implement - // a proper dynamic queue. - // I am pretty sure this also loses a guarantee of events being - // processed in order of being received. - select { - case c.eventChan <- event: - default: - go func() { - println("overflowing...") - c.eventChan <- event - }() - } - - // No more processing for events. + c.eventChan <- newEventFun(buf) continue } @@ -535,15 +526,16 @@ func processEventOrError(everr eventOrError) (Event, Error) { Logger.Printf("Invalid event/error type: %T", everr) return nil, nil } - panic("unreachable") } // WaitForEvent returns the next event from the server. // It will block until an event is available. -// WaitForEvent returns either an Event or an Error. (Returning neither or both +// WaitForEvent returns either an Event or an Error. (Returning both // is a bug.) Note than an Error here is an X error and not an XGB error. That // is, X errors are sometimes completely expected (and you may want to ignore // them in some cases). +// +// If both the event and error are nil, then the connection has been closed. func (c *Conn) WaitForEvent() (Event, Error) { return processEventOrError(<-c.eventChan) } @@ -559,5 +551,4 @@ func (c *Conn) PollForEvent() (Event, Error) { default: return nil, nil } - panic("unreachable") } |