github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/lib/pq/notify.go (about)

     1  package pq
     2  
     3  // Package pq is a pure Go Postgres driver for the database/sql package.
     4  // This module contains support for Postgres LISTEN/NOTIFY.
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"sync"
    10  	"sync/atomic"
    11  	"time"
    12  )
    13  
    14  // Notification represents a single notification from the database.
    15  type Notification struct {
    16  	// Process ID (PID) of the notifying postgres backend.
    17  	BePid int
    18  	// Name of the channel the notification was sent on.
    19  	Channel string
    20  	// Payload, or the empty string if unspecified.
    21  	Extra string
    22  }
    23  
    24  func recvNotification(r *readBuf) *Notification {
    25  	bePid := r.int32()
    26  	channel := r.string()
    27  	extra := r.string()
    28  
    29  	return &Notification{bePid, channel, extra}
    30  }
    31  
    32  const (
    33  	connStateIdle int32 = iota
    34  	connStateExpectResponse
    35  	connStateExpectReadyForQuery
    36  )
    37  
    38  type message struct {
    39  	typ byte
    40  	err error
    41  }
    42  
    43  var errListenerConnClosed = errors.New("pq: ListenerConn has been closed")
    44  
    45  // ListenerConn is a low-level interface for waiting for notifications.  You
    46  // should use Listener instead.
    47  type ListenerConn struct {
    48  	// guards cn and err
    49  	connectionLock sync.Mutex
    50  	cn             *conn
    51  	err            error
    52  
    53  	connState int32
    54  
    55  	// the sending goroutine will be holding this lock
    56  	senderLock sync.Mutex
    57  
    58  	notificationChan chan<- *Notification
    59  
    60  	replyChan chan message
    61  }
    62  
    63  // Creates a new ListenerConn.  Use NewListener instead.
    64  func NewListenerConn(name string, notificationChan chan<- *Notification) (*ListenerConn, error) {
    65  	cn, err := Open(name)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	l := &ListenerConn{
    71  		cn:               cn.(*conn),
    72  		notificationChan: notificationChan,
    73  		connState:        connStateIdle,
    74  		replyChan:        make(chan message, 2),
    75  	}
    76  
    77  	go l.listenerConnMain()
    78  
    79  	return l, nil
    80  }
    81  
    82  // We can only allow one goroutine at a time to be running a query on the
    83  // connection for various reasons, so the goroutine sending on the connection
    84  // must be holding senderLock.
    85  //
    86  // Returns an error if an unrecoverable error has occurred and the ListenerConn
    87  // should be abandoned.
    88  func (l *ListenerConn) acquireSenderLock() error {
    89  	// we must acquire senderLock first to avoid deadlocks; see ExecSimpleQuery
    90  	l.senderLock.Lock()
    91  
    92  	l.connectionLock.Lock()
    93  	err := l.err
    94  	l.connectionLock.Unlock()
    95  	if err != nil {
    96  		l.senderLock.Unlock()
    97  		return err
    98  	}
    99  	return nil
   100  }
   101  
   102  func (l *ListenerConn) releaseSenderLock() {
   103  	l.senderLock.Unlock()
   104  }
   105  
   106  // setState advances the protocol state to newState.  Returns false if moving
   107  // to that state from the current state is not allowed.
   108  func (l *ListenerConn) setState(newState int32) bool {
   109  	var expectedState int32
   110  
   111  	switch newState {
   112  	case connStateIdle:
   113  		expectedState = connStateExpectReadyForQuery
   114  	case connStateExpectResponse:
   115  		expectedState = connStateIdle
   116  	case connStateExpectReadyForQuery:
   117  		expectedState = connStateExpectResponse
   118  	default:
   119  		panic(fmt.Sprintf("unexpected listenerConnState %d", newState))
   120  	}
   121  
   122  	return atomic.CompareAndSwapInt32(&l.connState, expectedState, newState)
   123  }
   124  
   125  // Main logic is here: receive messages from the postgres backend, forward
   126  // notifications and query replies and keep the internal state in sync with the
   127  // protocol state.  Returns when the connection has been lost, is about to go
   128  // away or should be discarded because we couldn't agree on the state with the
   129  // server backend.
   130  func (l *ListenerConn) listenerConnLoop() (err error) {
   131  	defer errRecoverNoErrBadConn(&err)
   132  
   133  	r := &readBuf{}
   134  	for {
   135  		t, err := l.cn.recvMessage(r)
   136  		if err != nil {
   137  			return err
   138  		}
   139  
   140  		switch t {
   141  		case 'A':
   142  			// recvNotification copies all the data so we don't need to worry
   143  			// about the scratch buffer being overwritten.
   144  			l.notificationChan <- recvNotification(r)
   145  
   146  		case 'T', 'D':
   147  			// only used by tests; ignore
   148  
   149  		case 'E':
   150  			// We might receive an ErrorResponse even when not in a query; it
   151  			// is expected that the server will close the connection after
   152  			// that, but we should make sure that the error we display is the
   153  			// one from the stray ErrorResponse, not io.ErrUnexpectedEOF.
   154  			if !l.setState(connStateExpectReadyForQuery) {
   155  				return parseError(r)
   156  			}
   157  			l.replyChan <- message{t, parseError(r)}
   158  
   159  		case 'C', 'I':
   160  			if !l.setState(connStateExpectReadyForQuery) {
   161  				// protocol out of sync
   162  				return fmt.Errorf("unexpected CommandComplete")
   163  			}
   164  			// ExecSimpleQuery doesn't need to know about this message
   165  
   166  		case 'Z':
   167  			if !l.setState(connStateIdle) {
   168  				// protocol out of sync
   169  				return fmt.Errorf("unexpected ReadyForQuery")
   170  			}
   171  			l.replyChan <- message{t, nil}
   172  
   173  		case 'N', 'S':
   174  			// ignore
   175  		default:
   176  			return fmt.Errorf("unexpected message %q from server in listenerConnLoop", t)
   177  		}
   178  	}
   179  }
   180  
   181  // This is the main routine for the goroutine receiving on the database
   182  // connection.  Most of the main logic is in listenerConnLoop.
   183  func (l *ListenerConn) listenerConnMain() {
   184  	err := l.listenerConnLoop()
   185  
   186  	// listenerConnLoop terminated; we're done, but we still have to clean up.
   187  	// Make sure nobody tries to start any new queries by making sure the err
   188  	// pointer is set.  It is important that we do not overwrite its value; a
   189  	// connection could be closed by either this goroutine or one sending on
   190  	// the connection -- whoever closes the connection is assumed to have the
   191  	// more meaningful error message (as the other one will probably get
   192  	// net.errClosed), so that goroutine sets the error we expose while the
   193  	// other error is discarded.  If the connection is lost while two
   194  	// goroutines are operating on the socket, it probably doesn't matter which
   195  	// error we expose so we don't try to do anything more complex.
   196  	l.connectionLock.Lock()
   197  	if l.err == nil {
   198  		l.err = err
   199  	}
   200  	l.cn.Close()
   201  	l.connectionLock.Unlock()
   202  
   203  	// There might be a query in-flight; make sure nobody's waiting for a
   204  	// response to it, since there's not going to be one.
   205  	close(l.replyChan)
   206  
   207  	// let the listener know we're done
   208  	close(l.notificationChan)
   209  
   210  	// this ListenerConn is done
   211  }
   212  
   213  // Send a LISTEN query to the server.  See ExecSimpleQuery.
   214  func (l *ListenerConn) Listen(channel string) (bool, error) {
   215  	return l.ExecSimpleQuery("LISTEN " + QuoteIdentifier(channel))
   216  }
   217  
   218  // Send an UNLISTEN query to the server.  See ExecSimpleQuery.
   219  func (l *ListenerConn) Unlisten(channel string) (bool, error) {
   220  	return l.ExecSimpleQuery("UNLISTEN " + QuoteIdentifier(channel))
   221  }
   222  
   223  // Send `UNLISTEN *` to the server.  See ExecSimpleQuery.
   224  func (l *ListenerConn) UnlistenAll() (bool, error) {
   225  	return l.ExecSimpleQuery("UNLISTEN *")
   226  }
   227  
   228  // Ping the remote server to make sure it's alive.  Non-nil error means the
   229  // connection has failed and should be abandoned.
   230  func (l *ListenerConn) Ping() error {
   231  	sent, err := l.ExecSimpleQuery("")
   232  	if !sent {
   233  		return err
   234  	}
   235  	if err != nil {
   236  		// shouldn't happen
   237  		panic(err)
   238  	}
   239  	return nil
   240  }
   241  
   242  // Attempt to send a query on the connection.  Returns an error if sending the
   243  // query failed, and the caller should initiate closure of this connection.
   244  // The caller must be holding senderLock (see acquireSenderLock and
   245  // releaseSenderLock).
   246  func (l *ListenerConn) sendSimpleQuery(q string) (err error) {
   247  	defer errRecoverNoErrBadConn(&err)
   248  
   249  	// must set connection state before sending the query
   250  	if !l.setState(connStateExpectResponse) {
   251  		panic("two queries running at the same time")
   252  	}
   253  
   254  	// Can't use l.cn.writeBuf here because it uses the scratch buffer which
   255  	// might get overwritten by listenerConnLoop.
   256  	b := &writeBuf{
   257  		buf: []byte("Q\x00\x00\x00\x00"),
   258  		pos: 1,
   259  	}
   260  	b.string(q)
   261  	l.cn.send(b)
   262  
   263  	return nil
   264  }
   265  
   266  // Execute a "simple query" (i.e. one with no bindable parameters) on the
   267  // connection.  The possible return values are:
   268  //   1) "executed" is true; the query was executed to completion on the
   269  //      database server.  If the query failed, err will be set to the error
   270  //      returned by the database, otherwise err will be nil.
   271  //   2) If "executed" is false, the query could not be executed on the remote
   272  //      server.  err will be non-nil.
   273  //
   274  // After a call to ExecSimpleQuery has returned an executed=false value, the
   275  // connection has either been closed or will be closed shortly thereafter, and
   276  // all subsequently executed queries will return an error.
   277  func (l *ListenerConn) ExecSimpleQuery(q string) (executed bool, err error) {
   278  	if err = l.acquireSenderLock(); err != nil {
   279  		return false, err
   280  	}
   281  	defer l.releaseSenderLock()
   282  
   283  	err = l.sendSimpleQuery(q)
   284  	if err != nil {
   285  		// We can't know what state the protocol is in, so we need to abandon
   286  		// this connection.
   287  		l.connectionLock.Lock()
   288  		// Set the error pointer if it hasn't been set already; see
   289  		// listenerConnMain.
   290  		if l.err == nil {
   291  			l.err = err
   292  		}
   293  		l.connectionLock.Unlock()
   294  		l.cn.c.Close()
   295  		return false, err
   296  	}
   297  
   298  	// now we just wait for a reply..
   299  	for {
   300  		m, ok := <-l.replyChan
   301  		if !ok {
   302  			// We lost the connection to server, don't bother waiting for a
   303  			// a response.  err should have been set already.
   304  			l.connectionLock.Lock()
   305  			err := l.err
   306  			l.connectionLock.Unlock()
   307  			return false, err
   308  		}
   309  		switch m.typ {
   310  		case 'Z':
   311  			// sanity check
   312  			if m.err != nil {
   313  				panic("m.err != nil")
   314  			}
   315  			// done; err might or might not be set
   316  			return true, err
   317  
   318  		case 'E':
   319  			// sanity check
   320  			if m.err == nil {
   321  				panic("m.err == nil")
   322  			}
   323  			// server responded with an error; ReadyForQuery to follow
   324  			err = m.err
   325  
   326  		default:
   327  			return false, fmt.Errorf("unknown response for simple query: %q", m.typ)
   328  		}
   329  	}
   330  }
   331  
   332  func (l *ListenerConn) Close() error {
   333  	l.connectionLock.Lock()
   334  	if l.err != nil {
   335  		l.connectionLock.Unlock()
   336  		return errListenerConnClosed
   337  	}
   338  	l.err = errListenerConnClosed
   339  	l.connectionLock.Unlock()
   340  	// We can't send anything on the connection without holding senderLock.
   341  	// Simply close the net.Conn to wake up everyone operating on it.
   342  	return l.cn.c.Close()
   343  }
   344  
   345  // Err() returns the reason the connection was closed.  It is not safe to call
   346  // this function until l.Notify has been closed.
   347  func (l *ListenerConn) Err() error {
   348  	return l.err
   349  }
   350  
   351  var errListenerClosed = errors.New("pq: Listener has been closed")
   352  
   353  var ErrChannelAlreadyOpen = errors.New("pq: channel is already open")
   354  var ErrChannelNotOpen = errors.New("pq: channel is not open")
   355  
   356  type ListenerEventType int
   357  
   358  const (
   359  	// Emitted only when the database connection has been initially
   360  	// initialized.  err will always be nil.
   361  	ListenerEventConnected ListenerEventType = iota
   362  
   363  	// Emitted after a database connection has been lost, either because of an
   364  	// error or because Close has been called.  err will be set to the reason
   365  	// the database connection was lost.
   366  	ListenerEventDisconnected
   367  
   368  	// Emitted after a database connection has been re-established after
   369  	// connection loss.  err will always be nil.  After this event has been
   370  	// emitted, a nil pq.Notification is sent on the Listener.Notify channel.
   371  	ListenerEventReconnected
   372  
   373  	// Emitted after a connection to the database was attempted, but failed.
   374  	// err will be set to an error describing why the connection attempt did
   375  	// not succeed.
   376  	ListenerEventConnectionAttemptFailed
   377  )
   378  
   379  type EventCallbackType func(event ListenerEventType, err error)
   380  
   381  // Listener provides an interface for listening to notifications from a
   382  // PostgreSQL database.  For general usage information, see section
   383  // "Notifications".
   384  //
   385  // Listener can safely be used from concurrently running goroutines.
   386  type Listener struct {
   387  	// Channel for receiving notifications from the database.  In some cases a
   388  	// nil value will be sent.  See section "Notifications" above.
   389  	Notify chan *Notification
   390  
   391  	name                 string
   392  	minReconnectInterval time.Duration
   393  	maxReconnectInterval time.Duration
   394  	eventCallback        EventCallbackType
   395  
   396  	lock                 sync.Mutex
   397  	isClosed             bool
   398  	reconnectCond        *sync.Cond
   399  	cn                   *ListenerConn
   400  	connNotificationChan <-chan *Notification
   401  	channels             map[string]struct{}
   402  }
   403  
   404  // NewListener creates a new database connection dedicated to LISTEN / NOTIFY.
   405  //
   406  // name should be set to a connection string to be used to establish the
   407  // database connection (see section "Connection String Parameters" above).
   408  //
   409  // minReconnectInterval controls the duration to wait before trying to
   410  // re-establish the database connection after connection loss.  After each
   411  // consecutive failure this interval is doubled, until maxReconnectInterval is
   412  // reached.  Successfully completing the connection establishment procedure
   413  // resets the interval back to minReconnectInterval.
   414  //
   415  // The last parameter eventCallback can be set to a function which will be
   416  // called by the Listener when the state of the underlying database connection
   417  // changes.  This callback will be called by the goroutine which dispatches the
   418  // notifications over the Notify channel, so you should try to avoid doing
   419  // potentially time-consuming operations from the callback.
   420  func NewListener(name string,
   421  	minReconnectInterval time.Duration,
   422  	maxReconnectInterval time.Duration,
   423  	eventCallback EventCallbackType) *Listener {
   424  	l := &Listener{
   425  		name:                 name,
   426  		minReconnectInterval: minReconnectInterval,
   427  		maxReconnectInterval: maxReconnectInterval,
   428  		eventCallback:        eventCallback,
   429  
   430  		channels: make(map[string]struct{}),
   431  
   432  		Notify: make(chan *Notification, 32),
   433  	}
   434  	l.reconnectCond = sync.NewCond(&l.lock)
   435  
   436  	go l.listenerMain()
   437  
   438  	return l
   439  }
   440  
   441  // Returns the notification channel for this listener.  This is the same
   442  // channel as Notify, and will not be recreated during the life time of the
   443  // Listener.
   444  func (l *Listener) NotificationChannel() <-chan *Notification {
   445  	return l.Notify
   446  }
   447  
   448  // Listen starts listening for notifications on a channel.  Calls to this
   449  // function will block until an acknowledgement has been received from the
   450  // server.  Note that Listener automatically re-establishes the connection
   451  // after connection loss, so this function may block indefinitely if the
   452  // connection can not be re-established.
   453  //
   454  // Listen will only fail in three conditions:
   455  //   1) The channel is already open.  The returned error will be
   456  //      ErrChannelAlreadyOpen.
   457  //   2) The query was executed on the remote server, but PostgreSQL returned an
   458  //      error message in response to the query.  The returned error will be a
   459  //      pq.Error containing the information the server supplied.
   460  //   3) Close is called on the Listener before the request could be completed.
   461  //
   462  // The channel name is case-sensitive.
   463  func (l *Listener) Listen(channel string) error {
   464  	l.lock.Lock()
   465  	defer l.lock.Unlock()
   466  
   467  	if l.isClosed {
   468  		return errListenerClosed
   469  	}
   470  
   471  	// The server allows you to issue a LISTEN on a channel which is already
   472  	// open, but it seems useful to be able to detect this case to spot for
   473  	// mistakes in application logic.  If the application genuinely does't
   474  	// care, it can check the exported error and ignore it.
   475  	_, exists := l.channels[channel]
   476  	if exists {
   477  		return ErrChannelAlreadyOpen
   478  	}
   479  
   480  	if l.cn != nil {
   481  		// If gotResponse is true but error is set, the query was executed on
   482  		// the remote server, but resulted in an error.  This should be
   483  		// relatively rare, so it's fine if we just pass the error to our
   484  		// caller.  However, if gotResponse is false, we could not complete the
   485  		// query on the remote server and our underlying connection is about
   486  		// to go away, so we only add relname to l.channels, and wait for
   487  		// resync() to take care of the rest.
   488  		gotResponse, err := l.cn.Listen(channel)
   489  		if gotResponse && err != nil {
   490  			return err
   491  		}
   492  	}
   493  
   494  	l.channels[channel] = struct{}{}
   495  	for l.cn == nil {
   496  		l.reconnectCond.Wait()
   497  		// we let go of the mutex for a while
   498  		if l.isClosed {
   499  			return errListenerClosed
   500  		}
   501  	}
   502  
   503  	return nil
   504  }
   505  
   506  // Unlisten removes a channel from the Listener's channel list.  Returns
   507  // ErrChannelNotOpen if the Listener is not listening on the specified channel.
   508  // Returns immediately with no error if there is no connection.  Note that you
   509  // might still get notifications for this channel even after Unlisten has
   510  // returned.
   511  //
   512  // The channel name is case-sensitive.
   513  func (l *Listener) Unlisten(channel string) error {
   514  	l.lock.Lock()
   515  	defer l.lock.Unlock()
   516  
   517  	if l.isClosed {
   518  		return errListenerClosed
   519  	}
   520  
   521  	// Similarly to LISTEN, this is not an error in Postgres, but it seems
   522  	// useful to distinguish from the normal conditions.
   523  	_, exists := l.channels[channel]
   524  	if !exists {
   525  		return ErrChannelNotOpen
   526  	}
   527  
   528  	if l.cn != nil {
   529  		// Similarly to Listen (see comment in that function), the caller
   530  		// should only be bothered with an error if it came from the backend as
   531  		// a response to our query.
   532  		gotResponse, err := l.cn.Unlisten(channel)
   533  		if gotResponse && err != nil {
   534  			return err
   535  		}
   536  	}
   537  
   538  	// Don't bother waiting for resync if there's no connection.
   539  	delete(l.channels, channel)
   540  	return nil
   541  }
   542  
   543  // UnlistenAll removes all channels from the Listener's channel list.  Returns
   544  // immediately with no error if there is no connection.  Note that you might
   545  // still get notifications for any of the deleted channels even after
   546  // UnlistenAll has returned.
   547  func (l *Listener) UnlistenAll() error {
   548  	l.lock.Lock()
   549  	defer l.lock.Unlock()
   550  
   551  	if l.isClosed {
   552  		return errListenerClosed
   553  	}
   554  
   555  	if l.cn != nil {
   556  		// Similarly to Listen (see comment in that function), the caller
   557  		// should only be bothered with an error if it came from the backend as
   558  		// a response to our query.
   559  		gotResponse, err := l.cn.UnlistenAll()
   560  		if gotResponse && err != nil {
   561  			return err
   562  		}
   563  	}
   564  
   565  	// Don't bother waiting for resync if there's no connection.
   566  	l.channels = make(map[string]struct{})
   567  	return nil
   568  }
   569  
   570  // Ping the remote server to make sure it's alive.  Non-nil return value means
   571  // that there is no active connection.
   572  func (l *Listener) Ping() error {
   573  	l.lock.Lock()
   574  	defer l.lock.Unlock()
   575  
   576  	if l.isClosed {
   577  		return errListenerClosed
   578  	}
   579  	if l.cn == nil {
   580  		return errors.New("no connection")
   581  	}
   582  
   583  	return l.cn.Ping()
   584  }
   585  
   586  // Clean up after losing the server connection.  Returns l.cn.Err(), which
   587  // should have the reason the connection was lost.
   588  func (l *Listener) disconnectCleanup() error {
   589  	l.lock.Lock()
   590  	defer l.lock.Unlock()
   591  
   592  	// sanity check; can't look at Err() until the channel has been closed
   593  	select {
   594  	case _, ok := <-l.connNotificationChan:
   595  		if ok {
   596  			panic("connNotificationChan not closed")
   597  		}
   598  	default:
   599  		panic("connNotificationChan not closed")
   600  	}
   601  
   602  	err := l.cn.Err()
   603  	l.cn.Close()
   604  	l.cn = nil
   605  	return err
   606  }
   607  
   608  // Synchronize the list of channels we want to be listening on with the server
   609  // after the connection has been established.
   610  func (l *Listener) resync(cn *ListenerConn, notificationChan <-chan *Notification) error {
   611  	doneChan := make(chan error)
   612  	go func() {
   613  		for channel := range l.channels {
   614  			// If we got a response, return that error to our caller as it's
   615  			// going to be more descriptive than cn.Err().
   616  			gotResponse, err := cn.Listen(channel)
   617  			if gotResponse && err != nil {
   618  				doneChan <- err
   619  				return
   620  			}
   621  
   622  			// If we couldn't reach the server, wait for notificationChan to
   623  			// close and then return the error message from the connection, as
   624  			// per ListenerConn's interface.
   625  			if err != nil {
   626  				for _ = range notificationChan {
   627  				}
   628  				doneChan <- cn.Err()
   629  				return
   630  			}
   631  		}
   632  		doneChan <- nil
   633  	}()
   634  
   635  	// Ignore notifications while synchronization is going on to avoid
   636  	// deadlocks.  We have to send a nil notification over Notify anyway as
   637  	// we can't possibly know which notifications (if any) were lost while
   638  	// the connection was down, so there's no reason to try and process
   639  	// these messages at all.
   640  	for {
   641  		select {
   642  		case _, ok := <-notificationChan:
   643  			if !ok {
   644  				notificationChan = nil
   645  			}
   646  
   647  		case err := <-doneChan:
   648  			return err
   649  		}
   650  	}
   651  }
   652  
   653  // caller should NOT be holding l.lock
   654  func (l *Listener) closed() bool {
   655  	l.lock.Lock()
   656  	defer l.lock.Unlock()
   657  
   658  	return l.isClosed
   659  }
   660  
   661  func (l *Listener) connect() error {
   662  	notificationChan := make(chan *Notification, 32)
   663  	cn, err := NewListenerConn(l.name, notificationChan)
   664  	if err != nil {
   665  		return err
   666  	}
   667  
   668  	l.lock.Lock()
   669  	defer l.lock.Unlock()
   670  
   671  	err = l.resync(cn, notificationChan)
   672  	if err != nil {
   673  		cn.Close()
   674  		return err
   675  	}
   676  
   677  	l.cn = cn
   678  	l.connNotificationChan = notificationChan
   679  	l.reconnectCond.Broadcast()
   680  
   681  	return nil
   682  }
   683  
   684  // Close disconnects the Listener from the database and shuts it down.
   685  // Subsequent calls to its methods will return an error.  Close returns an
   686  // error if the connection has already been closed.
   687  func (l *Listener) Close() error {
   688  	l.lock.Lock()
   689  	defer l.lock.Unlock()
   690  
   691  	if l.isClosed {
   692  		return errListenerClosed
   693  	}
   694  
   695  	if l.cn != nil {
   696  		l.cn.Close()
   697  	}
   698  	l.isClosed = true
   699  
   700  	return nil
   701  }
   702  
   703  func (l *Listener) emitEvent(event ListenerEventType, err error) {
   704  	if l.eventCallback != nil {
   705  		l.eventCallback(event, err)
   706  	}
   707  }
   708  
   709  // Main logic here: maintain a connection to the server when possible, wait
   710  // for notifications and emit events.
   711  func (l *Listener) listenerConnLoop() {
   712  	var nextReconnect time.Time
   713  
   714  	reconnectInterval := l.minReconnectInterval
   715  	for {
   716  		for {
   717  			err := l.connect()
   718  			if err == nil {
   719  				break
   720  			}
   721  
   722  			if l.closed() {
   723  				return
   724  			}
   725  			l.emitEvent(ListenerEventConnectionAttemptFailed, err)
   726  
   727  			time.Sleep(reconnectInterval)
   728  			reconnectInterval *= 2
   729  			if reconnectInterval > l.maxReconnectInterval {
   730  				reconnectInterval = l.maxReconnectInterval
   731  			}
   732  		}
   733  
   734  		if nextReconnect.IsZero() {
   735  			l.emitEvent(ListenerEventConnected, nil)
   736  		} else {
   737  			l.emitEvent(ListenerEventReconnected, nil)
   738  			l.Notify <- nil
   739  		}
   740  
   741  		reconnectInterval = l.minReconnectInterval
   742  		nextReconnect = time.Now().Add(reconnectInterval)
   743  
   744  		for {
   745  			notification, ok := <-l.connNotificationChan
   746  			if !ok {
   747  				// lost connection, loop again
   748  				break
   749  			}
   750  			l.Notify <- notification
   751  		}
   752  
   753  		err := l.disconnectCleanup()
   754  		if l.closed() {
   755  			return
   756  		}
   757  		l.emitEvent(ListenerEventDisconnected, err)
   758  
   759  		time.Sleep(nextReconnect.Sub(time.Now()))
   760  	}
   761  }
   762  
   763  func (l *Listener) listenerMain() {
   764  	l.listenerConnLoop()
   765  	close(l.Notify)
   766  }