You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

149 lines
3.5 KiB

  1. // Copyright 2012, Google Inc. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package pools
  5. import (
  6. "fmt"
  7. "sync"
  8. "time"
  9. )
  10. // Numbered allows you to manage resources by tracking them with numbers.
  11. // There are no interface restrictions on what you can track.
  12. type Numbered struct {
  13. mu sync.Mutex
  14. empty *sync.Cond // Broadcast when pool becomes empty
  15. resources map[int64]*numberedWrapper
  16. }
  17. type numberedWrapper struct {
  18. val interface{}
  19. inUse bool
  20. purpose string
  21. timeCreated time.Time
  22. timeUsed time.Time
  23. }
  24. func NewNumbered() *Numbered {
  25. n := &Numbered{resources: make(map[int64]*numberedWrapper)}
  26. n.empty = sync.NewCond(&n.mu)
  27. return n
  28. }
  29. // Register starts tracking a resource by the supplied id.
  30. // It does not lock the object.
  31. // It returns an error if the id already exists.
  32. func (nu *Numbered) Register(id int64, val interface{}) error {
  33. nu.mu.Lock()
  34. defer nu.mu.Unlock()
  35. if _, ok := nu.resources[id]; ok {
  36. return fmt.Errorf("already present")
  37. }
  38. now := time.Now()
  39. nu.resources[id] = &numberedWrapper{
  40. val: val,
  41. timeCreated: now,
  42. timeUsed: now,
  43. }
  44. return nil
  45. }
  46. // Unregiester forgets the specified resource.
  47. // If the resource is not present, it's ignored.
  48. func (nu *Numbered) Unregister(id int64) {
  49. nu.mu.Lock()
  50. defer nu.mu.Unlock()
  51. delete(nu.resources, id)
  52. if len(nu.resources) == 0 {
  53. nu.empty.Broadcast()
  54. }
  55. }
  56. // Get locks the resource for use. It accepts a purpose as a string.
  57. // If it cannot be found, it returns a "not found" error. If in use,
  58. // it returns a "in use: purpose" error.
  59. func (nu *Numbered) Get(id int64, purpose string) (val interface{}, err error) {
  60. nu.mu.Lock()
  61. defer nu.mu.Unlock()
  62. nw, ok := nu.resources[id]
  63. if !ok {
  64. return nil, fmt.Errorf("not found")
  65. }
  66. if nw.inUse {
  67. return nil, fmt.Errorf("in use: %s", nw.purpose)
  68. }
  69. nw.inUse = true
  70. nw.purpose = purpose
  71. return nw.val, nil
  72. }
  73. // Put unlocks a resource for someone else to use.
  74. func (nu *Numbered) Put(id int64) {
  75. nu.mu.Lock()
  76. defer nu.mu.Unlock()
  77. if nw, ok := nu.resources[id]; ok {
  78. nw.inUse = false
  79. nw.purpose = ""
  80. nw.timeUsed = time.Now()
  81. }
  82. }
  83. // GetOutdated returns a list of resources that are older than age, and locks them.
  84. // It does not return any resources that are already locked.
  85. func (nu *Numbered) GetOutdated(age time.Duration, purpose string) (vals []interface{}) {
  86. nu.mu.Lock()
  87. defer nu.mu.Unlock()
  88. now := time.Now()
  89. for _, nw := range nu.resources {
  90. if nw.inUse {
  91. continue
  92. }
  93. if nw.timeCreated.Add(age).Sub(now) <= 0 {
  94. nw.inUse = true
  95. nw.purpose = purpose
  96. vals = append(vals, nw.val)
  97. }
  98. }
  99. return vals
  100. }
  101. // GetIdle returns a list of resurces that have been idle for longer
  102. // than timeout, and locks them. It does not return any resources that
  103. // are already locked.
  104. func (nu *Numbered) GetIdle(timeout time.Duration, purpose string) (vals []interface{}) {
  105. nu.mu.Lock()
  106. defer nu.mu.Unlock()
  107. now := time.Now()
  108. for _, nw := range nu.resources {
  109. if nw.inUse {
  110. continue
  111. }
  112. if nw.timeUsed.Add(timeout).Sub(now) <= 0 {
  113. nw.inUse = true
  114. nw.purpose = purpose
  115. vals = append(vals, nw.val)
  116. }
  117. }
  118. return vals
  119. }
  120. // WaitForEmpty returns as soon as the pool becomes empty
  121. func (nu *Numbered) WaitForEmpty() {
  122. nu.mu.Lock()
  123. defer nu.mu.Unlock()
  124. for len(nu.resources) != 0 {
  125. nu.empty.Wait()
  126. }
  127. }
  128. func (nu *Numbered) StatsJSON() string {
  129. return fmt.Sprintf("{\"Size\": %v}", nu.Size())
  130. }
  131. func (nu *Numbered) Size() (size int64) {
  132. nu.mu.Lock()
  133. defer nu.mu.Unlock()
  134. return int64(len(nu.resources))
  135. }