xmpp/status.go
changeset 153 bbd4166df95d
child 155 fdd637733628
equal deleted inserted replaced
152:69c5b4382e39 153:bbd4166df95d
       
     1 // Track the current status of the connection to the server.
       
     2 
       
     3 package xmpp
       
     4 
       
     5 import (
       
     6 	"fmt"
       
     7 )
       
     8 
       
     9 type statmgr struct {
       
    10 	newStatus     chan Status
       
    11 	newlistener chan chan Status
       
    12 }
       
    13 
       
    14 func newStatmgr(client chan<- Status) *statmgr {
       
    15 	s := statmgr{}
       
    16 	s.newStatus = make(chan Status)
       
    17 	s.newlistener = make(chan chan Status)
       
    18 	go s.manager(client)
       
    19 	return &s
       
    20 }
       
    21 
       
    22 func (s *statmgr) manager(client chan<- Status) {
       
    23 	// We handle this specially, in case the client doesn't read
       
    24 	// our final status message.
       
    25 	defer func() {
       
    26 		if client != nil {
       
    27 			select {
       
    28 			case client <- StatusShutdown:
       
    29 			default:
       
    30 			}
       
    31 			close(client)
       
    32 		}
       
    33 	}()
       
    34 
       
    35 	stat := StatusUnconnected
       
    36 	listeners := []chan Status{}
       
    37 	for {
       
    38 		select {
       
    39 		case stat = <-s.newStatus:
       
    40 			for _, l := range listeners {
       
    41 				sendToListener(l, stat)
       
    42 			}
       
    43 			if client != nil && stat != StatusShutdown {
       
    44 				client <- stat
       
    45 			}
       
    46 		case l, ok := <-s.newlistener:
       
    47 			if !ok {
       
    48 				return
       
    49 			}
       
    50 			defer close(l)
       
    51 			sendToListener(l, stat)
       
    52 			listeners = append(listeners, l)
       
    53 		}
       
    54 	}
       
    55 }
       
    56 
       
    57 func sendToListener(listen chan Status, stat Status) {
       
    58 	for {
       
    59 		select {
       
    60 		case <-listen:
       
    61 		case listen <- stat:
       
    62 			return
       
    63 		}
       
    64 	}
       
    65 }
       
    66 
       
    67 func (cl *Client) setStatus(stat Status) {
       
    68 	cl.statmgr.setStatus(stat)
       
    69 }
       
    70 
       
    71 func (s *statmgr) setStatus(stat Status) {
       
    72 	s.newStatus <- stat
       
    73 }
       
    74 
       
    75 func (s *statmgr) newListener() <-chan Status {
       
    76 	l := make(chan Status, 1)
       
    77 	s.newlistener <- l
       
    78 	return l
       
    79 }
       
    80 
       
    81 func (s *statmgr) close() {
       
    82 	close(s.newlistener)
       
    83 }
       
    84 
       
    85 func (s *statmgr) awaitStatus(waitFor Status) error {
       
    86 	// BUG(chris): This routine leaks one channel each time it's
       
    87 	// called. Listeners are never removed.
       
    88 	l := s.newListener()
       
    89 	for current := range l {
       
    90 		if current == waitFor {
       
    91 			return nil
       
    92 		}
       
    93 		if current == StatusShutdown {
       
    94 			break
       
    95 		}
       
    96 		if current > waitFor {
       
    97 			return nil
       
    98 		}
       
    99 	}
       
   100 	return fmt.Errorf("shut down waiting for status change")
       
   101 }