queue.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package workqueue
  14. import (
  15. "sync"
  16. "time"
  17. "k8s.io/apimachinery/pkg/util/clock"
  18. )
  19. type Interface interface {
  20. Add(item interface{})
  21. Len() int
  22. Get() (item interface{}, shutdown bool)
  23. Done(item interface{})
  24. ShutDown()
  25. ShuttingDown() bool
  26. }
  27. // New constructs a new work queue (see the package comment).
  28. func New() *Type {
  29. return NewNamed("")
  30. }
  31. func NewNamed(name string) *Type {
  32. rc := clock.RealClock{}
  33. return newQueue(
  34. rc,
  35. globalMetricsFactory.newQueueMetrics(name, rc),
  36. defaultUnfinishedWorkUpdatePeriod,
  37. )
  38. }
  39. func newQueue(c clock.Clock, metrics queueMetrics, updatePeriod time.Duration) *Type {
  40. t := &Type{
  41. clock: c,
  42. dirty: set{},
  43. processing: set{},
  44. cond: sync.NewCond(&sync.Mutex{}),
  45. metrics: metrics,
  46. unfinishedWorkUpdatePeriod: updatePeriod,
  47. }
  48. // Don't start the goroutine for a type of noMetrics so we don't consume
  49. // resources unnecessarily
  50. if _, ok := metrics.(noMetrics); !ok {
  51. go t.updateUnfinishedWorkLoop()
  52. }
  53. return t
  54. }
  55. const defaultUnfinishedWorkUpdatePeriod = 500 * time.Millisecond
  56. // Type is a work queue (see the package comment).
  57. type Type struct {
  58. // queue defines the order in which we will work on items. Every
  59. // element of queue should be in the dirty set and not in the
  60. // processing set.
  61. queue []t
  62. // dirty defines all of the items that need to be processed.
  63. dirty set
  64. // Things that are currently being processed are in the processing set.
  65. // These things may be simultaneously in the dirty set. When we finish
  66. // processing something and remove it from this set, we'll check if
  67. // it's in the dirty set, and if so, add it to the queue.
  68. processing set
  69. cond *sync.Cond
  70. shuttingDown bool
  71. metrics queueMetrics
  72. unfinishedWorkUpdatePeriod time.Duration
  73. clock clock.Clock
  74. }
  75. type empty struct{}
  76. type t interface{}
  77. type set map[t]empty
  78. func (s set) has(item t) bool {
  79. _, exists := s[item]
  80. return exists
  81. }
  82. func (s set) insert(item t) {
  83. s[item] = empty{}
  84. }
  85. func (s set) delete(item t) {
  86. delete(s, item)
  87. }
  88. // Add marks item as needing processing.
  89. func (q *Type) Add(item interface{}) {
  90. q.cond.L.Lock()
  91. defer q.cond.L.Unlock()
  92. if q.shuttingDown {
  93. return
  94. }
  95. if q.dirty.has(item) {
  96. return
  97. }
  98. q.metrics.add(item)
  99. q.dirty.insert(item)
  100. if q.processing.has(item) {
  101. return
  102. }
  103. q.queue = append(q.queue, item)
  104. q.cond.Signal()
  105. }
  106. // Len returns the current queue length, for informational purposes only. You
  107. // shouldn't e.g. gate a call to Add() or Get() on Len() being a particular
  108. // value, that can't be synchronized properly.
  109. func (q *Type) Len() int {
  110. q.cond.L.Lock()
  111. defer q.cond.L.Unlock()
  112. return len(q.queue)
  113. }
  114. // Get blocks until it can return an item to be processed. If shutdown = true,
  115. // the caller should end their goroutine. You must call Done with item when you
  116. // have finished processing it.
  117. func (q *Type) Get() (item interface{}, shutdown bool) {
  118. q.cond.L.Lock()
  119. defer q.cond.L.Unlock()
  120. for len(q.queue) == 0 && !q.shuttingDown {
  121. q.cond.Wait()
  122. }
  123. if len(q.queue) == 0 {
  124. // We must be shutting down.
  125. return nil, true
  126. }
  127. item, q.queue = q.queue[0], q.queue[1:]
  128. q.metrics.get(item)
  129. q.processing.insert(item)
  130. q.dirty.delete(item)
  131. return item, false
  132. }
  133. // Done marks item as done processing, and if it has been marked as dirty again
  134. // while it was being processed, it will be re-added to the queue for
  135. // re-processing.
  136. func (q *Type) Done(item interface{}) {
  137. q.cond.L.Lock()
  138. defer q.cond.L.Unlock()
  139. q.metrics.done(item)
  140. q.processing.delete(item)
  141. if q.dirty.has(item) {
  142. q.queue = append(q.queue, item)
  143. q.cond.Signal()
  144. }
  145. }
  146. // ShutDown will cause q to ignore all new items added to it. As soon as the
  147. // worker goroutines have drained the existing items in the queue, they will be
  148. // instructed to exit.
  149. func (q *Type) ShutDown() {
  150. q.cond.L.Lock()
  151. defer q.cond.L.Unlock()
  152. q.shuttingDown = true
  153. q.cond.Broadcast()
  154. }
  155. func (q *Type) ShuttingDown() bool {
  156. q.cond.L.Lock()
  157. defer q.cond.L.Unlock()
  158. return q.shuttingDown
  159. }
  160. func (q *Type) updateUnfinishedWorkLoop() {
  161. t := q.clock.NewTicker(q.unfinishedWorkUpdatePeriod)
  162. defer t.Stop()
  163. for range t.C() {
  164. if !func() bool {
  165. q.cond.L.Lock()
  166. defer q.cond.L.Unlock()
  167. if !q.shuttingDown {
  168. q.metrics.updateUnfinishedWork()
  169. return true
  170. }
  171. return false
  172. }() {
  173. return
  174. }
  175. }
  176. }