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.
This commit is contained in:
Andrew Gallant 2015-04-26 19:04:54 -04:00 committed by Přemysl Janouch
parent dd00568d44
commit 5451e59f88
Signed by: p
GPG Key ID: A0420B94F92B9493
2 changed files with 28 additions and 40 deletions

View File

@ -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")
}

View File

@ -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")
}