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