gfpool_pool.go 3.2 KB

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