github.com/wangyougui/gf/v2@v2.6.5/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/wangyougui/gf.
     6  
     7  package gfpool
     8  
     9  import (
    10  	"os"
    11  	"time"
    12  
    13  	"github.com/wangyougui/gf/v2/container/gpool"
    14  	"github.com/wangyougui/gf/v2/container/gtype"
    15  	"github.com/wangyougui/gf/v2/errors/gerror"
    16  	"github.com/wangyougui/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  }