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 }