2019-02-02 16:18:25 +08:00
|
|
|
|
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
2018-09-17 18:43:59 +08:00
|
|
|
|
//
|
|
|
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
|
|
|
// If a copy of the MIT was not distributed with this file,
|
2019-02-02 16:18:25 +08:00
|
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
2018-09-17 18:43:59 +08:00
|
|
|
|
|
2019-01-16 13:02:59 +08:00
|
|
|
|
// Package gfpool provides io-reusable pool for file pointer.
|
2019-01-16 13:35:16 +08:00
|
|
|
|
//
|
2019-01-15 23:27:47 +08:00
|
|
|
|
// 文件指针池.
|
2018-09-17 18:43:59 +08:00
|
|
|
|
package gfpool
|
|
|
|
|
|
|
|
|
|
import (
|
2018-10-31 19:00:28 +08:00
|
|
|
|
"fmt"
|
2019-02-02 16:18:25 +08:00
|
|
|
|
"github.com/gogf/gf/g/container/gmap"
|
|
|
|
|
"github.com/gogf/gf/g/container/gpool"
|
|
|
|
|
"github.com/gogf/gf/g/container/gtype"
|
|
|
|
|
"github.com/gogf/gf/g/os/gfsnotify"
|
2018-10-31 19:00:28 +08:00
|
|
|
|
"os"
|
|
|
|
|
"sync"
|
2018-09-17 18:43:59 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 文件指针池
|
|
|
|
|
type Pool struct {
|
2018-11-01 17:22:55 +08:00
|
|
|
|
id *gtype.Int // 指针池ID,用以识别指针池是否重建
|
2018-10-31 19:00:28 +08:00
|
|
|
|
pool *gpool.Pool // 底层对象池
|
2018-11-03 17:50:00 +08:00
|
|
|
|
inited *gtype.Bool // 是否初始化(在执行第一次File方法后初始化,主要用于监听的添加,但是只能添加一次)
|
2018-10-31 19:00:28 +08:00
|
|
|
|
expire int // 过期时间
|
2018-09-17 18:43:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 文件指针池指针
|
|
|
|
|
type File struct {
|
2018-11-08 14:00:28 +08:00
|
|
|
|
*os.File // 底层文件指针
|
2018-09-17 18:43:59 +08:00
|
|
|
|
mu sync.RWMutex // 互斥锁
|
|
|
|
|
pool *Pool // 所属池
|
2018-11-01 17:22:55 +08:00
|
|
|
|
poolid int // 所属池ID,如果池ID不同表示池已经重建,那么该文件指针也应当销毁,不能重新丢到原有的池中
|
2018-09-17 18:43:59 +08:00
|
|
|
|
flag int // 打开标志
|
|
|
|
|
perm os.FileMode // 打开权限
|
|
|
|
|
path string // 绝对路径
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 全局指针池,expire < 0表示不过期,expire = 0表示使用完立即回收,expire > 0表示超时回收
|
|
|
|
|
var pools = gmap.NewStringInterfaceMap()
|
|
|
|
|
|
2018-10-23 14:55:52 +08:00
|
|
|
|
// 获得文件对象,并自动创建指针池(过期时间单位:毫秒)
|
2018-11-02 19:21:57 +08:00
|
|
|
|
func Open(path string, flag int, perm os.FileMode, expire...int) (file *File, err error) {
|
2018-09-17 18:43:59 +08:00
|
|
|
|
fpExpire := 0
|
|
|
|
|
if len(expire) > 0 {
|
|
|
|
|
fpExpire = expire[0]
|
|
|
|
|
}
|
2018-10-31 19:00:28 +08:00
|
|
|
|
pool := pools.GetOrSetFuncLock(fmt.Sprintf("%s&%d&%d&%d", path, flag, expire, perm), func() interface{} {
|
2018-11-03 17:50:00 +08:00
|
|
|
|
return New(path, flag, perm, fpExpire)
|
2018-10-31 19:00:28 +08:00
|
|
|
|
}).(*Pool)
|
2018-11-03 17:50:00 +08:00
|
|
|
|
|
2018-09-17 18:43:59 +08:00
|
|
|
|
return pool.File()
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-02 19:21:57 +08:00
|
|
|
|
func OpenFile(path string, flag int, perm os.FileMode, expire...int) (file *File, err error) {
|
2018-09-17 18:43:59 +08:00
|
|
|
|
return Open(path, flag, perm, expire...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 创建一个文件指针池,expire = 0表示不过期,expire < 0表示使用完立即回收,expire > 0表示超时回收,默认值为0不过期
|
2018-10-23 14:55:52 +08:00
|
|
|
|
// 过期时间单位:毫秒
|
2018-11-03 17:50:00 +08:00
|
|
|
|
func New(path string, flag int, perm os.FileMode, expire...int) *Pool {
|
2018-09-17 18:43:59 +08:00
|
|
|
|
fpExpire := 0
|
|
|
|
|
if len(expire) > 0 {
|
|
|
|
|
fpExpire = expire[0]
|
|
|
|
|
}
|
2018-10-31 19:00:28 +08:00
|
|
|
|
p := &Pool {
|
2019-01-24 20:15:11 +08:00
|
|
|
|
id : gtype.NewInt(),
|
|
|
|
|
expire : fpExpire,
|
|
|
|
|
inited : gtype.NewBool(),
|
2018-10-31 19:00:28 +08:00
|
|
|
|
}
|
|
|
|
|
p.pool = newFilePool(p, path, flag, perm, fpExpire)
|
2018-11-03 17:50:00 +08:00
|
|
|
|
return p
|
2018-10-31 19:00:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 创建文件指针池
|
|
|
|
|
func newFilePool(p *Pool, path string, flag int, perm os.FileMode, expire int) *gpool.Pool {
|
|
|
|
|
pool := gpool.New(expire, func() (interface{}, error) {
|
2018-09-17 18:43:59 +08:00
|
|
|
|
file, err := os.OpenFile(path, flag, perm)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2018-11-08 14:00:28 +08:00
|
|
|
|
return &File {
|
|
|
|
|
File : file,
|
2018-11-01 17:22:55 +08:00
|
|
|
|
pool : p,
|
|
|
|
|
poolid : p.id.Val(),
|
|
|
|
|
flag : flag,
|
|
|
|
|
perm : perm,
|
|
|
|
|
path : path,
|
2018-09-17 18:43:59 +08:00
|
|
|
|
}, nil
|
2019-01-23 15:01:21 +08:00
|
|
|
|
}, func(i interface{}) {
|
2018-09-17 18:43:59 +08:00
|
|
|
|
i.(*File).File.Close()
|
|
|
|
|
})
|
2018-10-31 19:00:28 +08:00
|
|
|
|
return pool
|
2018-09-17 18:43:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获得一个文件打开指针
|
|
|
|
|
func (p *Pool) File() (*File, error) {
|
|
|
|
|
if v, err := p.pool.Get(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
} else {
|
2018-11-08 13:38:12 +08:00
|
|
|
|
f := v.(*File)
|
|
|
|
|
stat, err := os.Stat(f.path)
|
2018-09-17 18:43:59 +08:00
|
|
|
|
if f.flag & os.O_CREATE > 0 {
|
2018-11-08 13:38:12 +08:00
|
|
|
|
if os.IsNotExist(err) {
|
2018-10-31 19:00:28 +08:00
|
|
|
|
if file, err := os.OpenFile(f.path, f.flag, f.perm); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
} else {
|
2018-11-08 14:00:28 +08:00
|
|
|
|
f.File = file
|
2018-11-08 13:38:12 +08:00
|
|
|
|
if stat, err = f.Stat(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2018-10-31 19:00:28 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-09-17 18:43:59 +08:00
|
|
|
|
}
|
|
|
|
|
if f.flag & os.O_TRUNC > 0 {
|
2018-11-08 13:38:12 +08:00
|
|
|
|
if stat.Size() > 0 {
|
|
|
|
|
if err := f.Truncate(0); err != nil {
|
|
|
|
|
return nil, err
|
2018-10-31 19:00:28 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-09-17 18:43:59 +08:00
|
|
|
|
}
|
2018-09-25 15:45:07 +08:00
|
|
|
|
if f.flag & os.O_APPEND > 0 {
|
2018-10-31 19:00:28 +08:00
|
|
|
|
if _, err := f.Seek(0, 2); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2018-09-25 15:45:07 +08:00
|
|
|
|
} else {
|
2018-11-08 14:00:28 +08:00
|
|
|
|
if _, err := f.Seek(0, 0); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2018-10-31 19:00:28 +08:00
|
|
|
|
}
|
2019-02-26 22:21:57 +08:00
|
|
|
|
// !p.inited.Val() 使用原子读取操作判断,保证该操作判断的效率;
|
|
|
|
|
// p.inited.Set(true) == false 使用原子写入操作,保证该操作的原子性;
|
|
|
|
|
if !p.inited.Val() && p.inited.Set(true) == false {
|
2018-11-03 17:50:00 +08:00
|
|
|
|
gfsnotify.Add(f.path, func(event *gfsnotify.Event) {
|
|
|
|
|
// 如果文件被删除或者重命名,立即重建指针池
|
|
|
|
|
if event.IsRemove() || event.IsRename() {
|
|
|
|
|
// 原有的指针都不要了
|
|
|
|
|
p.id.Add(1)
|
|
|
|
|
// Clear相当于重建指针池
|
|
|
|
|
p.pool.Clear()
|
|
|
|
|
// 为保证原子操作,但又不想加锁,
|
|
|
|
|
// 这里再执行一次原子Add,将在两次Add中间可能分配出去的文件指针丢弃掉
|
|
|
|
|
p.id.Add(1)
|
2018-10-31 19:00:28 +08:00
|
|
|
|
}
|
2018-11-03 17:50:00 +08:00
|
|
|
|
}, false)
|
2018-09-25 15:45:07 +08:00
|
|
|
|
}
|
2018-09-17 18:43:59 +08:00
|
|
|
|
return f, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-24 20:15:11 +08:00
|
|
|
|
// 关闭指针池
|
|
|
|
|
func (p *Pool) Close() {
|
2018-09-17 18:43:59 +08:00
|
|
|
|
p.pool.Close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获得底层文件指针(返回error是标准库io.ReadWriteCloser接口实现)
|
|
|
|
|
func (f *File) Close() error {
|
2018-11-01 17:22:55 +08:00
|
|
|
|
if f.poolid == f.pool.id.Val() {
|
|
|
|
|
f.pool.pool.Put(f)
|
|
|
|
|
}
|
2018-09-17 18:43:59 +08:00
|
|
|
|
return nil
|
|
|
|
|
}
|