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