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:
parent
dd00568d44
commit
5451e59f88
|
@ -99,7 +99,6 @@ func (c Cookie) replyChecked() ([]byte, error) {
|
||||||
case err := <-c.errorChan:
|
case err := <-c.errorChan:
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// replyUnchecked waits for a response on either the replyChan or pingChan
|
// replyUnchecked waits for a response on either the replyChan or pingChan
|
||||||
|
@ -123,7 +122,6 @@ func (c Cookie) replyUnchecked() ([]byte, error) {
|
||||||
case <-c.pingChan:
|
case <-c.pingChan:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check is used for checked requests that have no replies. It is a mechanism
|
// 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:
|
case <-c.pingChan:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
|
||||||
}
|
}
|
||||||
|
|
65
nexgb/xgb.go
65
nexgb/xgb.go
|
@ -331,7 +331,10 @@ func (c *Conn) sendRequests() {
|
||||||
// Note that we circumvent the request channel, because we're *in*
|
// Note that we circumvent the request channel, because we're *in*
|
||||||
// the request channel.
|
// the request channel.
|
||||||
if len(c.cookieChan) == cookieBuffer-1 {
|
if len(c.cookieChan) == cookieBuffer-1 {
|
||||||
c.noop()
|
if err := c.noop(); err != nil {
|
||||||
|
// Shut everything down.
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
req.cookie.Sequence = c.newSequenceId()
|
req.cookie.Sequence = c.newSequenceId()
|
||||||
close(req.seq)
|
close(req.seq)
|
||||||
|
@ -340,26 +343,31 @@ func (c *Conn) sendRequests() {
|
||||||
}
|
}
|
||||||
response := make(chan struct{})
|
response := make(chan struct{})
|
||||||
c.closing <- response
|
c.closing <- response
|
||||||
c.noop() // Flush the response reading goroutine.
|
c.noop() // Flush the response reading goroutine, ignore error.
|
||||||
<-response
|
<-response
|
||||||
c.conn.Close()
|
c.conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// noop circumvents the usual request sending goroutines and forces a round
|
// noop circumvents the usual request sending goroutines and forces a round
|
||||||
// trip request manually.
|
// trip request manually.
|
||||||
func (c *Conn) noop() {
|
func (c *Conn) noop() error {
|
||||||
cookie := c.NewCookie(true, true)
|
cookie := c.NewCookie(true, true)
|
||||||
cookie.Sequence = c.newSequenceId()
|
cookie.Sequence = c.newSequenceId()
|
||||||
c.cookieChan <- cookie
|
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
|
cookie.Reply() // wait for the buffer to clear
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeBuffer is a convenience function for writing a byte slice to the wire.
|
// 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 {
|
if _, err := c.conn.Write(buf); err != nil {
|
||||||
Logger.Printf("Write error: %s", err)
|
Logger.Printf("A write error is unrecoverable: %s", err)
|
||||||
Logger.Fatal("A write error is unrecoverable. Exiting...")
|
return err
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,7 +385,6 @@ func (c *Conn) readResponses() {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
err Error
|
err Error
|
||||||
event Event
|
|
||||||
seq uint16
|
seq uint16
|
||||||
replyBytes []byte
|
replyBytes []byte
|
||||||
)
|
)
|
||||||
|
@ -391,13 +398,13 @@ func (c *Conn) readResponses() {
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := make([]byte, 32)
|
buf := make([]byte, 32)
|
||||||
err, event, seq = nil, nil, 0
|
err, seq = nil, 0
|
||||||
|
|
||||||
if _, err := io.ReadFull(c.conn, buf); err != nil {
|
if _, err := io.ReadFull(c.conn, buf); err != nil {
|
||||||
Logger.Println("A read error is unrecoverable.")
|
Logger.Printf("A read error is unrecoverable: %s", err)
|
||||||
panic(err)
|
c.eventChan <- err
|
||||||
|
c.Close()
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
switch buf[0] {
|
switch buf[0] {
|
||||||
case 0: // This is an error
|
case 0: // This is an error
|
||||||
// Use the constructor function for this error (that is auto
|
// Use the constructor function for this error (that is auto
|
||||||
|
@ -423,8 +430,10 @@ func (c *Conn) readResponses() {
|
||||||
biggerBuf := make([]byte, byteCount)
|
biggerBuf := make([]byte, byteCount)
|
||||||
copy(biggerBuf[:32], buf)
|
copy(biggerBuf[:32], buf)
|
||||||
if _, err := io.ReadFull(c.conn, biggerBuf[32:]); err != nil {
|
if _, err := io.ReadFull(c.conn, biggerBuf[32:]); err != nil {
|
||||||
Logger.Printf("Read error: %s", err)
|
Logger.Printf("A read error is unrecoverable: %s", err)
|
||||||
Logger.Fatal("A read error is unrecoverable. Exiting...")
|
c.eventChan <- err
|
||||||
|
c.Close()
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
replyBytes = biggerBuf
|
replyBytes = biggerBuf
|
||||||
} else {
|
} else {
|
||||||
|
@ -445,25 +454,7 @@ func (c *Conn) readResponses() {
|
||||||
"for event with number %d.", evNum)
|
"for event with number %d.", evNum)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
c.eventChan <- newEventFun(buf)
|
||||||
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.
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,15 +526,16 @@ func processEventOrError(everr eventOrError) (Event, Error) {
|
||||||
Logger.Printf("Invalid event/error type: %T", everr)
|
Logger.Printf("Invalid event/error type: %T", everr)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitForEvent returns the next event from the server.
|
// WaitForEvent returns the next event from the server.
|
||||||
// It will block until an event is available.
|
// 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 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
|
// is, X errors are sometimes completely expected (and you may want to ignore
|
||||||
// them in some cases).
|
// them in some cases).
|
||||||
|
//
|
||||||
|
// If both the event and error are nil, then the connection has been closed.
|
||||||
func (c *Conn) WaitForEvent() (Event, Error) {
|
func (c *Conn) WaitForEvent() (Event, Error) {
|
||||||
return processEventOrError(<-c.eventChan)
|
return processEventOrError(<-c.eventChan)
|
||||||
}
|
}
|
||||||
|
@ -559,5 +551,4 @@ func (c *Conn) PollForEvent() (Event, Error) {
|
||||||
default:
|
default:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue