github.com/zhongdalu/gf@v1.0.0/g/os/gfpool/gfpool.go (about) 1 // Copyright 2017-2019 gf Author(https://github.com/zhongdalu/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/zhongdalu/gf. 6 7 // Package gfpool provides io-reusable pool for file pointer. 8 package gfpool 9 10 import ( 11 "fmt" 12 "github.com/zhongdalu/gf/g/container/gmap" 13 "github.com/zhongdalu/gf/g/container/gpool" 14 "github.com/zhongdalu/gf/g/container/gtype" 15 "github.com/zhongdalu/gf/g/os/gfsnotify" 16 "os" 17 "sync" 18 ) 19 20 // File pointer pool. 21 type Pool struct { 22 id *gtype.Int // 指针池ID,用以识别指针池是否需要重建 23 pool *gpool.Pool // 底层对象池 24 inited *gtype.Bool // 是否初始化(在执行第一次执行File方法后初始化,主要用于文件监听的添加,但是只能添加一次) 25 expire int // 过期时间 26 } 27 28 // 文件指针池指针 29 type File struct { 30 *os.File // 底层文件指针 31 mu sync.RWMutex // 互斥锁 32 pool *Pool // 所属池 33 poolid int // 所属池ID,如果池ID不同表示池已经重建,那么该文件指针也应当销毁,不能重新丢到原有的池中 34 flag int // 打开标志 35 perm os.FileMode // 打开权限 36 path string // 绝对路径 37 } 38 39 var ( 40 // 全局文件指针池Map, 不过期 41 pools = gmap.NewStrAnyMap() 42 ) 43 44 // 获得文件对象,并自动创建指针池(过期时间单位:毫秒) 45 func Open(path string, flag int, perm os.FileMode, expire ...int) (file *File, err error) { 46 fpExpire := 0 47 if len(expire) > 0 { 48 fpExpire = expire[0] 49 } 50 pool := pools.GetOrSetFuncLock(fmt.Sprintf("%s&%d&%d&%d", path, flag, expire, perm), func() interface{} { 51 return New(path, flag, perm, fpExpire) 52 }).(*Pool) 53 54 return pool.File() 55 } 56 57 // Deprecated. 58 // See Open. 59 func OpenFile(path string, flag int, perm os.FileMode, expire ...int) (file *File, err error) { 60 return Open(path, flag, perm, expire...) 61 } 62 63 // 创建一个文件指针池,expire = 0表示不过期,expire < 0表示使用完立即回收,expire > 0表示超时回收,默认值为0表示不过期。 64 // 注意过期时间单位为:毫秒。 65 func New(path string, flag int, perm os.FileMode, expire ...int) *Pool { 66 fpExpire := 0 67 if len(expire) > 0 { 68 fpExpire = expire[0] 69 } 70 p := &Pool{ 71 id: gtype.NewInt(), 72 expire: fpExpire, 73 inited: gtype.NewBool(), 74 } 75 p.pool = newFilePool(p, path, flag, perm, fpExpire) 76 return p 77 } 78 79 // 创建文件指针池 80 func newFilePool(p *Pool, path string, flag int, perm os.FileMode, expire int) *gpool.Pool { 81 pool := gpool.New(expire, func() (interface{}, error) { 82 file, err := os.OpenFile(path, flag, perm) 83 if err != nil { 84 return nil, err 85 } 86 return &File{ 87 File: file, 88 pool: p, 89 poolid: p.id.Val(), 90 flag: flag, 91 perm: perm, 92 path: path, 93 }, nil 94 }, func(i interface{}) { 95 _ = i.(*File).File.Close() 96 }) 97 return pool 98 } 99 100 // 获得一个文件打开指针 101 func (p *Pool) File() (*File, error) { 102 if v, err := p.pool.Get(); err != nil { 103 return nil, err 104 } else { 105 f := v.(*File) 106 stat, err := os.Stat(f.path) 107 if f.flag&os.O_CREATE > 0 { 108 if os.IsNotExist(err) { 109 if file, err := os.OpenFile(f.path, f.flag, f.perm); err != nil { 110 return nil, err 111 } else { 112 f.File = file 113 if stat, err = f.Stat(); err != nil { 114 return nil, err 115 } 116 } 117 } 118 } 119 if f.flag&os.O_TRUNC > 0 { 120 if stat.Size() > 0 { 121 if err := f.Truncate(0); err != nil { 122 return nil, err 123 } 124 } 125 } 126 if f.flag&os.O_APPEND > 0 { 127 if _, err := f.Seek(0, 2); err != nil { 128 return nil, err 129 } 130 } else { 131 if _, err := f.Seek(0, 0); err != nil { 132 return nil, err 133 } 134 } 135 // 优先使用 !p.inited.Val() 原子读取操作判断,保证判断操作的效率; 136 // p.inited.Set(true) == false 使用原子写入操作,保证该操作的原子性; 137 if !p.inited.Val() && p.inited.Set(true) == false { 138 _, _ = gfsnotify.Add(f.path, func(event *gfsnotify.Event) { 139 // 如果文件被删除或者重命名,立即重建指针池 140 if event.IsRemove() || event.IsRename() { 141 // 原有的指针都不要了 142 p.id.Add(1) 143 // Clear相当于重建指针池 144 p.pool.Clear() 145 // 为保证原子操作,但又不想加锁, 146 // 这里再执行一次原子Add,将在两次Add中间可能分配出去的文件指针丢弃掉 147 p.id.Add(1) 148 } 149 }, false) 150 } 151 return f, nil 152 } 153 } 154 155 // 关闭指针池 156 func (p *Pool) Close() { 157 p.pool.Close() 158 } 159 160 // 获得底层文件指针(返回error是标准库io.ReadWriteCloser接口实现) 161 func (f *File) Close() error { 162 if f.poolid == f.pool.id.Val() { 163 f.pool.pool.Put(f) 164 } 165 return nil 166 }