gfpool_pool.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. // Copyright GoFrame Author(https://goframe.org). 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 gfpool
  7. import (
  8. "os"
  9. "time"
  10. "github.com/gogf/gf/v2/container/gpool"
  11. "github.com/gogf/gf/v2/container/gtype"
  12. "github.com/gogf/gf/v2/errors/gerror"
  13. "github.com/gogf/gf/v2/os/gfsnotify"
  14. )
  15. // New creates and returns a file pointer pool with given file path, flag and opening permission.
  16. //
  17. // Note the expiration logic:
  18. // ttl = 0 : not expired;
  19. // ttl < 0 : immediate expired after use;
  20. // ttl > 0 : timeout expired;
  21. // It is not expired in default.
  22. func New(path string, flag int, perm os.FileMode, ttl ...time.Duration) *Pool {
  23. var fpTTL time.Duration
  24. if len(ttl) > 0 {
  25. fpTTL = ttl[0]
  26. }
  27. p := &Pool{
  28. id: gtype.NewInt(),
  29. ttl: fpTTL,
  30. init: gtype.NewBool(),
  31. }
  32. p.pool = newFilePool(p, path, flag, perm, fpTTL)
  33. return p
  34. }
  35. // newFilePool creates and returns a file pointer pool with given file path, flag and opening permission.
  36. func newFilePool(p *Pool, path string, flag int, perm os.FileMode, ttl time.Duration) *gpool.Pool {
  37. pool := gpool.New(ttl, func() (interface{}, error) {
  38. file, err := os.OpenFile(path, flag, perm)
  39. if err != nil {
  40. err = gerror.Wrapf(err, `os.OpenFile failed for file "%s", flag "%d", perm "%s"`, path, flag, perm)
  41. return nil, err
  42. }
  43. return &File{
  44. File: file,
  45. pid: p.id.Val(),
  46. pool: p,
  47. flag: flag,
  48. perm: perm,
  49. path: path,
  50. }, nil
  51. }, func(i interface{}) {
  52. _ = i.(*File).File.Close()
  53. })
  54. return pool
  55. }
  56. // File retrieves file item from the file pointer pool and returns it. It creates one if
  57. // the file pointer pool is empty.
  58. // Note that it should be closed when it will never be used. When it's closed, it is not
  59. // really closed the underlying file pointer but put back to the file pointer pool.
  60. func (p *Pool) File() (*File, error) {
  61. if v, err := p.pool.Get(); err != nil {
  62. return nil, err
  63. } else {
  64. f := v.(*File)
  65. f.stat, err = os.Stat(f.path)
  66. if f.flag&os.O_CREATE > 0 {
  67. if os.IsNotExist(err) {
  68. if f.File, err = os.OpenFile(f.path, f.flag, f.perm); err != nil {
  69. return nil, err
  70. } else {
  71. // Retrieve the state of the new created file.
  72. if f.stat, err = f.File.Stat(); err != nil {
  73. return nil, err
  74. }
  75. }
  76. }
  77. }
  78. if f.flag&os.O_TRUNC > 0 {
  79. if f.stat.Size() > 0 {
  80. if err = f.Truncate(0); err != nil {
  81. return nil, err
  82. }
  83. }
  84. }
  85. if f.flag&os.O_APPEND > 0 {
  86. if _, err = f.Seek(0, 2); err != nil {
  87. return nil, err
  88. }
  89. } else {
  90. if _, err = f.Seek(0, 0); err != nil {
  91. return nil, err
  92. }
  93. }
  94. // It firstly checks using !p.init.Val() for performance purpose.
  95. if !p.init.Val() && p.init.Cas(false, true) {
  96. _, _ = gfsnotify.Add(f.path, func(event *gfsnotify.Event) {
  97. // If the file is removed or renamed, recreates the pool by increasing the pool id.
  98. if event.IsRemove() || event.IsRename() {
  99. // It drops the old pool.
  100. p.id.Add(1)
  101. // Clears the pool items staying in the pool.
  102. p.pool.Clear()
  103. // It uses another adding to drop the file items between the two adding.
  104. // Whenever the pool id changes, the pool will be recreated.
  105. p.id.Add(1)
  106. }
  107. }, false)
  108. }
  109. return f, nil
  110. }
  111. }
  112. // Close closes current file pointer pool.
  113. func (p *Pool) Close() {
  114. p.pool.Close()
  115. }