/
gfpool_pool.go
121 lines (114 loc) · 3.23 KB
/
gfpool_pool.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// 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,
// You can obtain one at https://github.com/gogf/gf.
package gfpool
import (
"os"
"time"
"github.com/gogf/gf/container/gpool"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/os/gfsnotify"
)
// New creates and returns a file pointer pool with given file path, flag and opening permission.
//
// Note the expiration logic:
// ttl = 0 : not expired;
// ttl < 0 : immediate expired after use;
// ttl > 0 : timeout expired;
// It is not expired in default.
func New(path string, flag int, perm os.FileMode, ttl ...time.Duration) *Pool {
var fpTTL time.Duration
if len(ttl) > 0 {
fpTTL = ttl[0]
}
p := &Pool{
id: gtype.NewInt(),
ttl: fpTTL,
init: gtype.NewBool(),
}
p.pool = newFilePool(p, path, flag, perm, fpTTL)
return p
}
// newFilePool creates and returns a file pointer pool with given file path, flag and opening permission.
func newFilePool(p *Pool, path string, flag int, perm os.FileMode, ttl time.Duration) *gpool.Pool {
pool := gpool.New(ttl, func() (interface{}, error) {
file, err := os.OpenFile(path, flag, perm)
if err != nil {
return nil, err
}
return &File{
File: file,
pid: p.id.Val(),
pool: p,
flag: flag,
perm: perm,
path: path,
}, nil
}, func(i interface{}) {
_ = i.(*File).File.Close()
})
return pool
}
// File retrieves file item from the file pointer pool and returns it. It creates one if
// the file pointer pool is empty.
// Note that it should be closed when it will never be used. When it's closed, it is not
// really closed the underlying file pointer but put back to the file pinter pool.
func (p *Pool) File() (*File, error) {
if v, err := p.pool.Get(); err != nil {
return nil, err
} else {
var err error
f := v.(*File)
f.stat, err = os.Stat(f.path)
if f.flag&os.O_CREATE > 0 {
if os.IsNotExist(err) {
if f.File, err = os.OpenFile(f.path, f.flag, f.perm); err != nil {
return nil, err
} else {
// Retrieve the state of the new created file.
if f.stat, err = f.File.Stat(); err != nil {
return nil, err
}
}
}
}
if f.flag&os.O_TRUNC > 0 {
if f.stat.Size() > 0 {
if err = f.Truncate(0); err != nil {
return nil, err
}
}
}
if f.flag&os.O_APPEND > 0 {
if _, err = f.Seek(0, 2); err != nil {
return nil, err
}
} else {
if _, err = f.Seek(0, 0); err != nil {
return nil, err
}
}
// It firstly checks using !p.init.Val() for performance purpose.
if !p.init.Val() && p.init.Cas(false, true) {
_, _ = gfsnotify.Add(f.path, func(event *gfsnotify.Event) {
// If teh file is removed or renamed, recreates the pool by increasing the pool id.
if event.IsRemove() || event.IsRename() {
// It drops the old pool.
p.id.Add(1)
// Clears the pool items staying in the pool.
p.pool.Clear()
// It uses another adding to drop the file items between the two adding.
// Whenever the pool id changes, the pool will be recreated.
p.id.Add(1)
}
}, false)
}
return f, nil
}
}
// Close closes current file pointer pool.
func (p *Pool) Close() {
p.pool.Close()
}