aboutsummaryrefslogtreecommitdiff
path: root/nexgb/cookie.go
diff options
context:
space:
mode:
Diffstat (limited to 'nexgb/cookie.go')
-rw-r--r--nexgb/cookie.go51
1 files changed, 47 insertions, 4 deletions
diff --git a/nexgb/cookie.go b/nexgb/cookie.go
index 502ccbf..7f54a22 100644
--- a/nexgb/cookie.go
+++ b/nexgb/cookie.go
@@ -4,16 +4,28 @@ import (
"errors"
)
+// cookie is the internal representation of a cookie, where one is generated
+// for *every* request sent by XGB.
+// 'cookie' is most frequently used by embedding it into a more specific
+// kind of cookie, i.e., 'GetInputFocusCookie'.
type cookie struct {
+ conn *Conn
Sequence uint16
replyChan chan []byte
errorChan chan error
pingChan chan bool
}
-func (c *Conn) newCookie(checked, reply bool) cookie {
- cookie := cookie{
- Sequence: c.newSequenceId(),
+// newCookie creates a new cookie with the correct channels initialized
+// depending upon the values of 'checked' and 'reply'. Together, there are
+// four different kinds of cookies. (See more detailed comments in the
+// function for more info on those.)
+// Note that a sequence number is not set until just before the request
+// corresponding to this cookie is sent over the wire.
+func (c *Conn) newCookie(checked, reply bool) *cookie {
+ cookie := &cookie{
+ conn: c,
+ Sequence: 0, // we add the sequence id just before sending a request
replyChan: nil,
errorChan: nil,
pingChan: nil,
@@ -48,6 +60,8 @@ func (c *Conn) newCookie(checked, reply bool) cookie {
return cookie
}
+// reply detects whether this is a checked or unchecked cookie, and calls
+// 'replyChecked' or 'replyUnchecked' appropriately.
func (c cookie) reply() ([]byte, error) {
// checked
if c.errorChan != nil {
@@ -56,6 +70,10 @@ func (c cookie) reply() ([]byte, error) {
return c.replyUnchecked()
}
+// replyChecked waits for a response on either the replyChan or errorChan
+// channels. If the former arrives, the bytes are returned with a nil error.
+// If the latter arrives, no bytes are returned (nil) and the error received
+// is returned.
func (c cookie) replyChecked() ([]byte, error) {
if c.replyChan == nil {
return nil, errors.New("Cannot call 'replyChecked' on a cookie that " +
@@ -75,6 +93,12 @@ func (c cookie) replyChecked() ([]byte, error) {
panic("unreachable")
}
+// replyChecked waits for a response on either the replyChan or pingChan
+// channels. If the former arrives, the bytes are returned with a nil error.
+// If the latter arrives, no bytes are returned (nil) and a nil error
+// is returned. (In the latter case, the corresponding error can be retrieved
+// from (Wait|Poll)ForEvent asynchronously.)
+// In all honesty, you *probably* don't want to use this method.
func (c cookie) replyUnchecked() ([]byte, error) {
if c.replyChan == nil {
return nil, errors.New("Cannot call 'replyUnchecked' on a cookie " +
@@ -90,7 +114,15 @@ func (c cookie) replyUnchecked() ([]byte, error) {
panic("unreachable")
}
-func (c cookie) Check() error {
+// check is used for checked requests that have no replies. It is a mechanism
+// by which to report "success" or "error" in a synchronous fashion. (Therefore,
+// unchecked requests without replies cannot use this method.)
+// If the request causes an error, it is sent to this cookie's errorChan.
+// If the request was successful, there is no response from the server.
+// Thus, pingChan is sent a value when the *next* reply is read.
+// If no more replies are being processed, we force a round trip request with
+// GetInputFocus.
+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.")
@@ -100,6 +132,17 @@ func (c cookie) Check() error {
"not expecting a possible *error*.")
}
+ // First do a quick non-blocking check to see if we've been pinged.
+ select {
+ case err := <-c.errorChan:
+ return err
+ case <-c.pingChan:
+ return nil
+ default:
+ }
+
+ // Now force a round trip and try again, but block this time.
+ c.conn.GetInputFocus().Reply()
select {
case err := <-c.errorChan:
return err