gpool.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // Copyright GoFrame gf Author(https://github.com/gogf/gf). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. // Package gpool provides object-reusable concurrent-safe pool.
  7. package gpool
  8. import (
  9. "errors"
  10. "time"
  11. "github.com/gogf/gf/container/glist"
  12. "github.com/gogf/gf/container/gtype"
  13. "github.com/gogf/gf/os/gtime"
  14. "github.com/gogf/gf/os/gtimer"
  15. )
  16. // Pool is an Object-Reusable Pool.
  17. type Pool struct {
  18. list *glist.List // Available/idle items list.
  19. closed *gtype.Bool // Whether the pool is closed.
  20. TTL time.Duration // Time To Live for pool items.
  21. NewFunc func() (interface{}, error) // Callback function to create pool item.
  22. // ExpireFunc is the for expired items destruction.
  23. // This function needs to be defined when the pool items
  24. // need to perform additional destruction operations.
  25. // Eg: net.Conn, os.File, etc.
  26. ExpireFunc func(interface{})
  27. }
  28. // Pool item.
  29. type poolItem struct {
  30. value interface{} // Item value.
  31. expireAt int64 // Expire timestamp in milliseconds.
  32. }
  33. // Creation function for object.
  34. type NewFunc func() (interface{}, error)
  35. // Destruction function for object.
  36. type ExpireFunc func(interface{})
  37. // New creates and returns a new object pool.
  38. // To ensure execution efficiency, the expiration time cannot be modified once it is set.
  39. //
  40. // Note the expiration logic:
  41. // ttl = 0 : not expired;
  42. // ttl < 0 : immediate expired after use;
  43. // ttl > 0 : timeout expired;
  44. func New(ttl time.Duration, newFunc NewFunc, expireFunc ...ExpireFunc) *Pool {
  45. r := &Pool{
  46. list: glist.New(true),
  47. closed: gtype.NewBool(),
  48. TTL: ttl,
  49. NewFunc: newFunc,
  50. }
  51. if len(expireFunc) > 0 {
  52. r.ExpireFunc = expireFunc[0]
  53. }
  54. gtimer.AddSingleton(time.Second, r.checkExpireItems)
  55. return r
  56. }
  57. // Put puts an item to pool.
  58. func (p *Pool) Put(value interface{}) error {
  59. if p.closed.Val() {
  60. return errors.New("pool is closed")
  61. }
  62. item := &poolItem{
  63. value: value,
  64. }
  65. if p.TTL == 0 {
  66. item.expireAt = 0
  67. } else {
  68. // As for Golang version < 1.13, there's no method Milliseconds for time.Duration.
  69. // So we need calculate the milliseconds using its nanoseconds value.
  70. item.expireAt = gtime.TimestampMilli() + p.TTL.Nanoseconds()/1000000
  71. }
  72. p.list.PushBack(item)
  73. return nil
  74. }
  75. // Clear clears pool, which means it will remove all items from pool.
  76. func (p *Pool) Clear() {
  77. if p.ExpireFunc != nil {
  78. for {
  79. if r := p.list.PopFront(); r != nil {
  80. p.ExpireFunc(r.(*poolItem).value)
  81. } else {
  82. break
  83. }
  84. }
  85. } else {
  86. p.list.RemoveAll()
  87. }
  88. }
  89. // Get picks and returns an item from pool. If the pool is empty and NewFunc is defined,
  90. // it creates and returns one from NewFunc.
  91. func (p *Pool) Get() (interface{}, error) {
  92. for !p.closed.Val() {
  93. if r := p.list.PopFront(); r != nil {
  94. f := r.(*poolItem)
  95. if f.expireAt == 0 || f.expireAt > gtime.TimestampMilli() {
  96. return f.value, nil
  97. } else if p.ExpireFunc != nil {
  98. // TODO: move expire function calling asynchronously from `Get` operation.
  99. p.ExpireFunc(f.value)
  100. }
  101. } else {
  102. break
  103. }
  104. }
  105. if p.NewFunc != nil {
  106. return p.NewFunc()
  107. }
  108. return nil, errors.New("pool is empty")
  109. }
  110. // Size returns the count of available items of pool.
  111. func (p *Pool) Size() int {
  112. return p.list.Len()
  113. }
  114. // Close closes the pool. If <p> has ExpireFunc,
  115. // then it automatically closes all items using this function before it's closed.
  116. // Commonly you do not need call this function manually.
  117. func (p *Pool) Close() {
  118. p.closed.Set(true)
  119. }
  120. // checkExpire removes expired items from pool in every second.
  121. func (p *Pool) checkExpireItems() {
  122. if p.closed.Val() {
  123. // If p has ExpireFunc,
  124. // then it must close all items using this function.
  125. if p.ExpireFunc != nil {
  126. for {
  127. if r := p.list.PopFront(); r != nil {
  128. p.ExpireFunc(r.(*poolItem).value)
  129. } else {
  130. break
  131. }
  132. }
  133. }
  134. gtimer.Exit()
  135. }
  136. // All items do not expire.
  137. if p.TTL == 0 {
  138. return
  139. }
  140. // The latest item expire timestamp in milliseconds.
  141. var latestExpire int64 = -1
  142. // Retrieve the current timestamp in milliseconds, it expires the items
  143. // by comparing with this timestamp. It is not accurate comparison for
  144. // every items expired, but high performance.
  145. var timestampMilli = gtime.TimestampMilli()
  146. for {
  147. if latestExpire > timestampMilli {
  148. break
  149. }
  150. if r := p.list.PopFront(); r != nil {
  151. item := r.(*poolItem)
  152. latestExpire = item.expireAt
  153. // TODO improve the auto-expiration mechanism of the pool.
  154. if item.expireAt > timestampMilli {
  155. p.list.PushFront(item)
  156. break
  157. }
  158. if p.ExpireFunc != nil {
  159. p.ExpireFunc(item.value)
  160. }
  161. } else {
  162. break
  163. }
  164. }
  165. }