112 lines
2.7 KiB
Go
112 lines
2.7 KiB
Go
|
package xgb
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
)
|
||
|
|
||
|
type cookie struct {
|
||
|
Sequence uint16
|
||
|
replyChan chan []byte
|
||
|
errorChan chan error
|
||
|
pingChan chan bool
|
||
|
}
|
||
|
|
||
|
func (c *Conn) newCookie(checked, reply bool) cookie {
|
||
|
cookie := cookie{
|
||
|
Sequence: c.newSequenceId(),
|
||
|
replyChan: nil,
|
||
|
errorChan: nil,
|
||
|
pingChan: nil,
|
||
|
}
|
||
|
|
||
|
// There are four different kinds of cookies:
|
||
|
// Checked requests with replies get a reply channel and an error channel.
|
||
|
// Unchecked requests with replies get a reply channel and a ping channel.
|
||
|
// Checked requests w/o replies get a ping channel and an error channel.
|
||
|
// Unchecked requests w/o replies get no channels.
|
||
|
// The reply channel is used to send reply data.
|
||
|
// The error channel is used to send error data.
|
||
|
// The ping channel is used when one of the 'reply' or 'error' channels
|
||
|
// is missing but the other is present. The ping channel is way to force
|
||
|
// the blocking to stop and basically say "the error has been received
|
||
|
// in the main event loop" (when the ping channel is coupled with a reply
|
||
|
// channel) or "the request you made that has no reply was successful"
|
||
|
// (when the ping channel is coupled with an error channel).
|
||
|
if checked {
|
||
|
cookie.errorChan = make(chan error, 1)
|
||
|
if !reply {
|
||
|
cookie.pingChan = make(chan bool, 1)
|
||
|
}
|
||
|
}
|
||
|
if reply {
|
||
|
cookie.replyChan = make(chan []byte, 1)
|
||
|
if !checked {
|
||
|
cookie.pingChan = make(chan bool, 1)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return cookie
|
||
|
}
|
||
|
|
||
|
func (c cookie) reply() ([]byte, error) {
|
||
|
// checked
|
||
|
if c.errorChan != nil {
|
||
|
return c.replyChecked()
|
||
|
}
|
||
|
return c.replyUnchecked()
|
||
|
}
|
||
|
|
||
|
func (c cookie) replyChecked() ([]byte, error) {
|
||
|
if c.replyChan == nil {
|
||
|
return nil, errors.New("Cannot call 'replyChecked' on a cookie that " +
|
||
|
"is not expecting a *reply* or an error.")
|
||
|
}
|
||
|
if c.errorChan == nil {
|
||
|
return nil, errors.New("Cannot call 'replyChecked' on a cookie that " +
|
||
|
"is not expecting a reply or an *error*.")
|
||
|
}
|
||
|
|
||
|
select {
|
||
|
case reply := <-c.replyChan:
|
||
|
return reply, nil
|
||
|
case err := <-c.errorChan:
|
||
|
return nil, err
|
||
|
}
|
||
|
panic("unreachable")
|
||
|
}
|
||
|
|
||
|
func (c cookie) replyUnchecked() ([]byte, error) {
|
||
|
if c.replyChan == nil {
|
||
|
return nil, errors.New("Cannot call 'replyUnchecked' on a cookie " +
|
||
|
"that is not expecting a *reply*.")
|
||
|
}
|
||
|
|
||
|
select {
|
||
|
case reply := <-c.replyChan:
|
||
|
return reply, nil
|
||
|
case <-c.pingChan:
|
||
|
return nil, nil
|
||
|
}
|
||
|
panic("unreachable")
|
||
|
}
|
||
|
|
||
|
func (c cookie) Check() error {
|
||
|
if c.replyChan != nil {
|
||
|
return errors.New("Cannot call 'Check' on a cookie that is " +
|
||
|
"expecting a *reply*. Use 'Reply' instead.")
|
||
|
}
|
||
|
if c.errorChan == nil {
|
||
|
return errors.New("Cannot call 'Check' on a cookie that is " +
|
||
|
"not expecting a possible *error*.")
|
||
|
}
|
||
|
|
||
|
select {
|
||
|
case err := <-c.errorChan:
|
||
|
return err
|
||
|
case <-c.pingChan:
|
||
|
return nil
|
||
|
}
|
||
|
panic("unreachable")
|
||
|
}
|
||
|
|