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.

214 lines
5.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. // RoundRobin is deprecated. Use ResourcePool instead.
  11. // RoundRobin allows you to use a pool of resources in a round robin fashion.
  12. type RoundRobin struct {
  13. mu sync.Mutex
  14. available *sync.Cond
  15. resources chan fifoWrapper
  16. size int64
  17. factory Factory
  18. idleTimeout time.Duration
  19. // stats
  20. waitCount int64
  21. waitTime time.Duration
  22. }
  23. type fifoWrapper struct {
  24. resource Resource
  25. timeUsed time.Time
  26. }
  27. // NewRoundRobin creates a new RoundRobin pool.
  28. // capacity is the maximum number of resources RoundRobin will create.
  29. // factory will be the function used to create resources.
  30. // If a resource is unused beyond idleTimeout, it's discarded.
  31. func NewRoundRobin(capacity int, idleTimeout time.Duration) *RoundRobin {
  32. r := &RoundRobin{
  33. resources: make(chan fifoWrapper, capacity),
  34. size: 0,
  35. idleTimeout: idleTimeout,
  36. }
  37. r.available = sync.NewCond(&r.mu)
  38. return r
  39. }
  40. // Open starts allowing the creation of resources
  41. func (rr *RoundRobin) Open(factory Factory) {
  42. rr.mu.Lock()
  43. defer rr.mu.Unlock()
  44. rr.factory = factory
  45. }
  46. // Close empties the pool calling Close on all its resources.
  47. // It waits for all resources to be returned (Put).
  48. func (rr *RoundRobin) Close() {
  49. rr.mu.Lock()
  50. defer rr.mu.Unlock()
  51. for rr.size > 0 {
  52. select {
  53. case fw := <-rr.resources:
  54. go fw.resource.Close()
  55. rr.size--
  56. default:
  57. rr.available.Wait()
  58. }
  59. }
  60. rr.factory = nil
  61. }
  62. func (rr *RoundRobin) IsClosed() bool {
  63. return rr.factory == nil
  64. }
  65. // Get will return the next available resource. If none is available, and capacity
  66. // has not been reached, it will create a new one using the factory. Otherwise,
  67. // it will indefinitely wait till the next resource becomes available.
  68. func (rr *RoundRobin) Get() (resource Resource, err error) {
  69. return rr.get(true)
  70. }
  71. // TryGet will return the next available resource. If none is available, and capacity
  72. // has not been reached, it will create a new one using the factory. Otherwise,
  73. // it will return nil with no error.
  74. func (rr *RoundRobin) TryGet() (resource Resource, err error) {
  75. return rr.get(false)
  76. }
  77. func (rr *RoundRobin) get(wait bool) (resource Resource, err error) {
  78. rr.mu.Lock()
  79. defer rr.mu.Unlock()
  80. // Any waits in this loop will release the lock, and it will be
  81. // reacquired before the waits return.
  82. for {
  83. select {
  84. case fw := <-rr.resources:
  85. // Found a free resource in the channel
  86. if rr.idleTimeout > 0 && fw.timeUsed.Add(rr.idleTimeout).Sub(time.Now()) < 0 {
  87. // resource has been idle for too long. Discard & go for next.
  88. go fw.resource.Close()
  89. rr.size--
  90. // Nobody else should be waiting, but signal anyway.
  91. rr.available.Signal()
  92. continue
  93. }
  94. return fw.resource, nil
  95. default:
  96. // resource channel is empty
  97. if rr.size >= int64(cap(rr.resources)) {
  98. // The pool is full
  99. if wait {
  100. start := time.Now()
  101. rr.available.Wait()
  102. rr.recordWait(start)
  103. continue
  104. }
  105. return nil, nil
  106. }
  107. // Pool is not full. Create a resource.
  108. if resource, err = rr.waitForCreate(); err != nil {
  109. // size was decremented, and somebody could be waiting.
  110. rr.available.Signal()
  111. return nil, err
  112. }
  113. // Creation successful. Account for this by incrementing size.
  114. rr.size++
  115. return resource, err
  116. }
  117. }
  118. }
  119. func (rr *RoundRobin) recordWait(start time.Time) {
  120. rr.waitCount++
  121. rr.waitTime += time.Now().Sub(start)
  122. }
  123. func (rr *RoundRobin) waitForCreate() (resource Resource, err error) {
  124. // Prevent thundering herd: increment size before creating resource, and decrement after.
  125. rr.size++
  126. rr.mu.Unlock()
  127. defer func() {
  128. rr.mu.Lock()
  129. rr.size--
  130. }()
  131. return rr.factory()
  132. }
  133. // Put will return a resource to the pool. You MUST return every resource to the pool,
  134. // even if it's closed. If a resource is closed, you should call Put(nil).
  135. func (rr *RoundRobin) Put(resource Resource) {
  136. rr.mu.Lock()
  137. defer rr.available.Signal()
  138. defer rr.mu.Unlock()
  139. if rr.size > int64(cap(rr.resources)) {
  140. if resource != nil {
  141. go resource.Close()
  142. }
  143. rr.size--
  144. } else if resource == nil {
  145. rr.size--
  146. } else {
  147. if len(rr.resources) == cap(rr.resources) {
  148. panic("unexpected")
  149. }
  150. rr.resources <- fifoWrapper{resource, time.Now()}
  151. }
  152. }
  153. // Set capacity changes the capacity of the pool.
  154. // You can use it to expand or shrink.
  155. func (rr *RoundRobin) SetCapacity(capacity int) error {
  156. rr.mu.Lock()
  157. defer rr.available.Broadcast()
  158. defer rr.mu.Unlock()
  159. nr := make(chan fifoWrapper, capacity)
  160. // This loop transfers resources from the old channel
  161. // to the new one, until it fills up or runs out.
  162. // It discards extras, if any.
  163. for {
  164. select {
  165. case fw := <-rr.resources:
  166. if len(nr) < cap(nr) {
  167. nr <- fw
  168. } else {
  169. go fw.resource.Close()
  170. rr.size--
  171. }
  172. continue
  173. default:
  174. }
  175. break
  176. }
  177. rr.resources = nr
  178. return nil
  179. }
  180. func (rr *RoundRobin) SetIdleTimeout(idleTimeout time.Duration) {
  181. rr.mu.Lock()
  182. defer rr.mu.Unlock()
  183. rr.idleTimeout = idleTimeout
  184. }
  185. func (rr *RoundRobin) StatsJSON() string {
  186. s, c, a, wc, wt, it := rr.Stats()
  187. return fmt.Sprintf("{\"Size\": %v, \"Capacity\": %v, \"Available\": %v, \"WaitCount\": %v, \"WaitTime\": %v, \"IdleTimeout\": %v}", s, c, a, wc, int64(wt), int64(it))
  188. }
  189. func (rr *RoundRobin) Stats() (size, capacity, available, waitCount int64, waitTime, idleTimeout time.Duration) {
  190. rr.mu.Lock()
  191. defer rr.mu.Unlock()
  192. return rr.size, int64(cap(rr.resources)), int64(len(rr.resources)), rr.waitCount, rr.waitTime, rr.idleTimeout
  193. }