Fixed a nasty bug where closing could cause ReadFull to crash
the program. Close #4.
This commit is contained in:
parent
3658686aee
commit
5d96993ee1
40
nexgb/xgb.go
40
nexgb/xgb.go
|
@ -61,6 +61,7 @@ type Conn struct {
|
||||||
xidChan chan xid
|
xidChan chan xid
|
||||||
seqChan chan uint16
|
seqChan chan uint16
|
||||||
reqChan chan *request
|
reqChan chan *request
|
||||||
|
closing chan chan struct{}
|
||||||
|
|
||||||
// Extensions is a map from extension name to major opcode. It should
|
// Extensions is a map from extension name to major opcode. It should
|
||||||
// not be used. It is exported for use in the extension sub-packages.
|
// not be used. It is exported for use in the extension sub-packages.
|
||||||
|
@ -100,6 +101,7 @@ func NewConnDisplay(display string) (*Conn, error) {
|
||||||
conn.seqChan = make(chan uint16, seqBuffer)
|
conn.seqChan = make(chan uint16, seqBuffer)
|
||||||
conn.reqChan = make(chan *request, reqBuffer)
|
conn.reqChan = make(chan *request, reqBuffer)
|
||||||
conn.eventChan = make(chan eventOrError, eventBuffer)
|
conn.eventChan = make(chan eventOrError, eventBuffer)
|
||||||
|
conn.closing = make(chan chan struct{}, 1)
|
||||||
|
|
||||||
go conn.generateXIds()
|
go conn.generateXIds()
|
||||||
go conn.generateSeqIds()
|
go conn.generateSeqIds()
|
||||||
|
@ -109,9 +111,9 @@ func NewConnDisplay(display string) (*Conn, error) {
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the connection to the X server.
|
// Close gracefully closes the connection to the X server.
|
||||||
func (c *Conn) Close() {
|
func (c *Conn) Close() {
|
||||||
c.conn.Close()
|
close(c.reqChan)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event is an interface that can contain any of the events returned by the
|
// Event is an interface that can contain any of the events returned by the
|
||||||
|
@ -292,7 +294,6 @@ func (c *Conn) NewRequest(buf []byte, cookie *Cookie) {
|
||||||
// the bytes to the wire and adds the cookie to the cookie queue.
|
// the bytes to the wire and adds the cookie to the cookie queue.
|
||||||
// It is meant to be run as its own goroutine.
|
// It is meant to be run as its own goroutine.
|
||||||
func (c *Conn) sendRequests() {
|
func (c *Conn) sendRequests() {
|
||||||
defer close(c.reqChan)
|
|
||||||
defer close(c.cookieChan)
|
defer close(c.cookieChan)
|
||||||
|
|
||||||
for req := range c.reqChan {
|
for req := range c.reqChan {
|
||||||
|
@ -301,6 +302,22 @@ 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()
|
||||||
|
}
|
||||||
|
req.cookie.Sequence = c.newSequenceId()
|
||||||
|
c.cookieChan <- req.cookie
|
||||||
|
c.writeBuffer(req.buf)
|
||||||
|
}
|
||||||
|
response := make(chan struct{})
|
||||||
|
c.closing <- response
|
||||||
|
c.noop() // Flush the response reading goroutine.
|
||||||
|
<-response
|
||||||
|
c.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// noop circumvents the usual request sending goroutines and forces a round
|
||||||
|
// trip request manually.
|
||||||
|
func (c *Conn) noop() {
|
||||||
cookie := c.NewCookie(true, true)
|
cookie := c.NewCookie(true, true)
|
||||||
cookie.Sequence = c.newSequenceId()
|
cookie.Sequence = c.newSequenceId()
|
||||||
c.cookieChan <- cookie
|
c.cookieChan <- cookie
|
||||||
|
@ -308,12 +325,6 @@ func (c *Conn) sendRequests() {
|
||||||
cookie.Reply() // wait for the buffer to clear
|
cookie.Reply() // wait for the buffer to clear
|
||||||
}
|
}
|
||||||
|
|
||||||
req.cookie.Sequence = c.newSequenceId()
|
|
||||||
c.cookieChan <- req.cookie
|
|
||||||
c.writeBuffer(req.buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
||||||
if _, err := c.conn.Write(buf); err != nil {
|
if _, err := c.conn.Write(buf); err != nil {
|
||||||
|
@ -342,12 +353,19 @@ func (c *Conn) readResponses() {
|
||||||
)
|
)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case respond := <-c.closing:
|
||||||
|
respond <- struct{}{}
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
buf := make([]byte, 32)
|
buf := make([]byte, 32)
|
||||||
err, event, seq = nil, nil, 0
|
err, event, seq = nil, nil, 0
|
||||||
|
|
||||||
if _, err := io.ReadFull(c.conn, buf); err != nil {
|
if _, err := io.ReadFull(c.conn, buf); err != nil {
|
||||||
logger.Printf("Read error: %s", err)
|
logger.Println("A read error is unrecoverable.")
|
||||||
logger.Fatal("A read error is unrecoverable. Exiting...")
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch buf[0] {
|
switch buf[0] {
|
||||||
|
|
Loading…
Reference in New Issue