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  }