updates.go 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. package main
  2. import (
  3. "time"
  4. "sync"
  5. )
  6. const (
  7. addAction = 0
  8. removeAction = 1
  9. moveUpAction = 2
  10. moveDownAction = 3
  11. )
  12. type Update struct {
  13. Song *Song
  14. Action uint
  15. Timestamp int64
  16. Next *Update
  17. }
  18. type Listener struct {
  19. L chan bool
  20. Next *Listener
  21. }
  22. var headUpdates map[int]*Update
  23. var tailUpdates map[int]*Update
  24. var listeners map[int]*Listener
  25. var updateLock sync.Mutex
  26. func init() {
  27. headUpdates = make(map[int]*Update)
  28. tailUpdates = make(map[int]*Update)
  29. listeners = make(map[int]*Listener)
  30. }
  31. func addUpdate(pid int, action uint, song *Song) {
  32. update := new(Update)
  33. update.Song = song
  34. update.Action = action
  35. update.Timestamp = time.Nanoseconds()
  36. updateLock.Lock()
  37. defer updateLock.Unlock()
  38. // write new update
  39. pup := tailUpdates[pid]
  40. if pup != nil {
  41. pup.Next = update
  42. } else {
  43. headUpdates[pid] = update
  44. }
  45. tailUpdates[pid] = update
  46. // expire old updates
  47. const expiryTime = 1e9 * 3 * 60 // 3 minutes
  48. pup = headUpdates[pid]
  49. for pup != nil && pup.Timestamp < update.Timestamp - expiryTime {
  50. pup = pup.Next
  51. headUpdates[pid] = pup
  52. }
  53. // notify listeners
  54. listener := listeners[pid]
  55. for listener != nil {
  56. listener.L <- true
  57. listener = listener.Next
  58. }
  59. listeners[pid] = nil
  60. }
  61. // assumes caller has updateLock
  62. func checkUpdates(pid int, timestamp int64) *Update {
  63. pup, _ := headUpdates[pid]
  64. for pup != nil {
  65. if pup.Timestamp > timestamp {
  66. return pup
  67. }
  68. pup = pup.Next
  69. }
  70. return nil
  71. }
  72. func getUpdates(id string, timestamp int64) *Update {
  73. db := <-dbPool
  74. pid := getpid(db, id)
  75. dbPool <- db
  76. if pid == -1 {
  77. return nil
  78. }
  79. updateLock.Lock()
  80. pup := checkUpdates(pid, timestamp)
  81. if pup != nil {
  82. updateLock.Unlock()
  83. return pup
  84. }
  85. // didn't get updates
  86. listener := new(Listener)
  87. listener.L = make(chan bool)
  88. lhead := listeners[pid]
  89. if lhead != nil {
  90. listener.Next = lhead
  91. }
  92. listeners[pid] = listener
  93. updateLock.Unlock()
  94. <-listener.L
  95. updateLock.Lock()
  96. pup = checkUpdates(pid, timestamp)
  97. updateLock.Unlock()
  98. return pup
  99. }