github.com/kayoticsully/syncthing@v0.8.9-0.20140724133906-c45a2fdc03f8/events/events.go (about)

     1  // Package events provides event subscription and polling functionality.
     2  package events
     3  
     4  import (
     5  	"errors"
     6  	"sync"
     7  	"time"
     8  )
     9  
    10  type EventType uint64
    11  
    12  const (
    13  	Ping = 1 << iota
    14  	Starting
    15  	StartupComplete
    16  	NodeDiscovered
    17  	NodeConnected
    18  	NodeDisconnected
    19  	LocalIndexUpdated
    20  	RemoteIndexUpdated
    21  	ItemStarted
    22  	ItemCompleted
    23  	StateChanged
    24  
    25  	AllEvents = ^EventType(0)
    26  )
    27  
    28  func (t EventType) String() string {
    29  	switch t {
    30  	case Ping:
    31  		return "Ping"
    32  	case Starting:
    33  		return "Starting"
    34  	case StartupComplete:
    35  		return "StartupComplete"
    36  	case NodeDiscovered:
    37  		return "NodeDiscovered"
    38  	case NodeConnected:
    39  		return "NodeConnected"
    40  	case NodeDisconnected:
    41  		return "NodeDisconnected"
    42  	case LocalIndexUpdated:
    43  		return "LocalIndexUpdated"
    44  	case RemoteIndexUpdated:
    45  		return "RemoteIndexUpdated"
    46  	case ItemStarted:
    47  		return "ItemStarted"
    48  	case StateChanged:
    49  		return "StateChanged"
    50  	default:
    51  		return "Unknown"
    52  	}
    53  }
    54  
    55  func (t EventType) MarshalText() ([]byte, error) {
    56  	return []byte(t.String()), nil
    57  }
    58  
    59  const BufferSize = 64
    60  
    61  type Logger struct {
    62  	subs   map[int]*Subscription
    63  	nextId int
    64  	mutex  sync.Mutex
    65  }
    66  
    67  type Event struct {
    68  	ID   int         `json:"id"`
    69  	Time time.Time   `json:"time"`
    70  	Type EventType   `json:"type"`
    71  	Data interface{} `json:"data"`
    72  }
    73  
    74  type Subscription struct {
    75  	mask   EventType
    76  	id     int
    77  	events chan Event
    78  	mutex  sync.Mutex
    79  }
    80  
    81  var Default = NewLogger()
    82  
    83  var (
    84  	ErrTimeout = errors.New("timeout")
    85  	ErrClosed  = errors.New("closed")
    86  )
    87  
    88  func NewLogger() *Logger {
    89  	return &Logger{
    90  		subs: make(map[int]*Subscription),
    91  	}
    92  }
    93  
    94  func (l *Logger) Log(t EventType, data interface{}) {
    95  	l.mutex.Lock()
    96  	e := Event{
    97  		ID:   l.nextId,
    98  		Time: time.Now(),
    99  		Type: t,
   100  		Data: data,
   101  	}
   102  	l.nextId++
   103  	for _, s := range l.subs {
   104  		if s.mask&t != 0 {
   105  			select {
   106  			case s.events <- e:
   107  			default:
   108  				//log.Println("Dropping event:", e)
   109  			}
   110  		}
   111  	}
   112  	l.mutex.Unlock()
   113  }
   114  
   115  func (l *Logger) Subscribe(mask EventType) *Subscription {
   116  	l.mutex.Lock()
   117  	s := &Subscription{
   118  		mask:   mask,
   119  		id:     l.nextId,
   120  		events: make(chan Event, BufferSize),
   121  	}
   122  	l.nextId++
   123  	l.subs[s.id] = s
   124  	l.mutex.Unlock()
   125  	return s
   126  }
   127  
   128  func (l *Logger) Unsubscribe(s *Subscription) {
   129  	l.mutex.Lock()
   130  	delete(l.subs, s.id)
   131  	close(s.events)
   132  	l.mutex.Unlock()
   133  }
   134  
   135  func (s *Subscription) Poll(timeout time.Duration) (Event, error) {
   136  	s.mutex.Lock()
   137  	defer s.mutex.Unlock()
   138  
   139  	to := time.After(timeout)
   140  	select {
   141  	case e, ok := <-s.events:
   142  		if !ok {
   143  			return e, ErrClosed
   144  		}
   145  		return e, nil
   146  	case <-to:
   147  		return Event{}, ErrTimeout
   148  	}
   149  }
   150  
   151  type BufferedSubscription struct {
   152  	sub  *Subscription
   153  	buf  []Event
   154  	next int
   155  	cur  int
   156  	mut  sync.Mutex
   157  	cond *sync.Cond
   158  }
   159  
   160  func NewBufferedSubscription(s *Subscription, size int) *BufferedSubscription {
   161  	bs := &BufferedSubscription{
   162  		sub: s,
   163  		buf: make([]Event, size),
   164  	}
   165  	bs.cond = sync.NewCond(&bs.mut)
   166  	go bs.pollingLoop()
   167  	return bs
   168  }
   169  
   170  func (s *BufferedSubscription) pollingLoop() {
   171  	for {
   172  		ev, err := s.sub.Poll(60 * time.Second)
   173  		if err == ErrTimeout {
   174  			continue
   175  		}
   176  		if err == ErrClosed {
   177  			return
   178  		}
   179  		if err != nil {
   180  			panic("unexpected error: " + err.Error())
   181  		}
   182  
   183  		s.mut.Lock()
   184  		s.buf[s.next] = ev
   185  		s.next = (s.next + 1) % len(s.buf)
   186  		s.cur = ev.ID
   187  		s.cond.Broadcast()
   188  		s.mut.Unlock()
   189  	}
   190  }
   191  
   192  func (s *BufferedSubscription) Since(id int, into []Event) []Event {
   193  	s.mut.Lock()
   194  	defer s.mut.Unlock()
   195  
   196  	for id >= s.cur {
   197  		s.cond.Wait()
   198  	}
   199  
   200  	for i := s.next; i < len(s.buf); i++ {
   201  		if s.buf[i].ID > id {
   202  			into = append(into, s.buf[i])
   203  		}
   204  	}
   205  	for i := 0; i < s.next; i++ {
   206  		if s.buf[i].ID > id {
   207  			into = append(into, s.buf[i])
   208  		}
   209  	}
   210  
   211  	return into
   212  }