|
|
@@ -2,6 +2,7 @@ package main
|
|
|
|
|
|
import (
|
|
|
"time"
|
|
|
+ "sync"
|
|
|
)
|
|
|
|
|
|
const (
|
|
|
@@ -16,12 +17,20 @@ type Update struct {
|
|
|
Timestamp int64
|
|
|
Next *Update
|
|
|
}
|
|
|
+type Listener struct {
|
|
|
+ L chan bool
|
|
|
+ Next *Listener
|
|
|
+}
|
|
|
+
|
|
|
var headUpdates map[int]*Update
|
|
|
var tailUpdates map[int]*Update
|
|
|
+var listeners map[int]*Listener
|
|
|
+var updateLock sync.Mutex
|
|
|
|
|
|
func init() {
|
|
|
headUpdates = make(map[int]*Update)
|
|
|
tailUpdates = make(map[int]*Update)
|
|
|
+ listeners = make(map[int]*Listener)
|
|
|
}
|
|
|
|
|
|
func addUpdate(pid int, action uint, song *Song) {
|
|
|
@@ -29,13 +38,36 @@ func addUpdate(pid int, action uint, song *Song) {
|
|
|
update.Song = song
|
|
|
update.Action = action
|
|
|
update.Timestamp = time.Nanoseconds()
|
|
|
- pup, ok := tailUpdates[pid]
|
|
|
- if ok {
|
|
|
+
|
|
|
+ updateLock.Lock()
|
|
|
+ defer updateLock.Unlock()
|
|
|
+
|
|
|
+ pup := tailUpdates[pid]
|
|
|
+ if pup != nil {
|
|
|
pup.Next = update
|
|
|
} else {
|
|
|
headUpdates[pid] = update
|
|
|
}
|
|
|
tailUpdates[pid] = update
|
|
|
+
|
|
|
+ listener := listeners[pid]
|
|
|
+ for listener != nil {
|
|
|
+ listener.L <- true
|
|
|
+ listener = listener.Next
|
|
|
+ }
|
|
|
+ listeners[pid] = nil
|
|
|
+}
|
|
|
+
|
|
|
+// assumes locked
|
|
|
+func checkUpdates(pid int, timestamp int64) *Update {
|
|
|
+ pup, _ := headUpdates[pid]
|
|
|
+ for pup != nil {
|
|
|
+ if pup.Timestamp > timestamp {
|
|
|
+ return pup
|
|
|
+ }
|
|
|
+ pup = pup.Next
|
|
|
+ }
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
func getUpdates(id string, timestamp int64) *Update {
|
|
|
@@ -45,15 +77,27 @@ func getUpdates(id string, timestamp int64) *Update {
|
|
|
if pid == -1 {
|
|
|
return nil
|
|
|
}
|
|
|
- pup, ok := headUpdates[pid]
|
|
|
- if !ok {
|
|
|
- return nil
|
|
|
+
|
|
|
+ updateLock.Lock()
|
|
|
+ pup := checkUpdates(pid, timestamp)
|
|
|
+ if pup != nil {
|
|
|
+ updateLock.Unlock()
|
|
|
+ return pup
|
|
|
}
|
|
|
- for pup != nil {
|
|
|
- if pup.Timestamp > timestamp {
|
|
|
- return pup
|
|
|
- }
|
|
|
- pup = pup.Next
|
|
|
+
|
|
|
+ // didn't get updates
|
|
|
+ listener := new(Listener)
|
|
|
+ listener.L = make(chan bool)
|
|
|
+ lhead := listeners[pid]
|
|
|
+ if lhead != nil {
|
|
|
+ listener.Next = lhead
|
|
|
}
|
|
|
- return nil
|
|
|
+ listeners[pid] = listener
|
|
|
+ updateLock.Unlock()
|
|
|
+ <-listener.L
|
|
|
+
|
|
|
+ updateLock.Lock()
|
|
|
+ pup = checkUpdates(pid, timestamp)
|
|
|
+ updateLock.Unlock()
|
|
|
+ return pup
|
|
|
}
|