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  }