├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── channel.go ├── channel_test.go ├── conn.go ├── conn_test.go └── pool.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 1.3 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Fatih Arslan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Archived project. No maintenance. 2 | This project is not maintained anymore and is archived. Feel free to fork and 3 | use make your own changes if needed. 4 | 5 | Thanks all for their work on this project. 6 | 7 | # Pool [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://godoc.org/github.com/fatih/pool) [![Build Status](http://img.shields.io/travis/fatih/pool.svg?style=flat-square)](https://travis-ci.org/fatih/pool) 8 | 9 | 10 | Pool is a thread safe connection pool for net.Conn interface. It can be used to 11 | manage and reuse connections. 12 | 13 | 14 | ## Install and Usage 15 | 16 | Install the package with: 17 | 18 | ```bash 19 | go get github.com/fatih/pool 20 | ``` 21 | 22 | Please vendor the package with one of the releases: https://github.com/fatih/pool/releases. 23 | `master` branch is **development** branch and will contain always the latest changes. 24 | 25 | 26 | ## Example 27 | 28 | ```go 29 | // create a factory() to be used with channel based pool 30 | factory := func() (net.Conn, error) { return net.Dial("tcp", "127.0.0.1:4000") } 31 | 32 | // create a new channel based pool with an initial capacity of 5 and maximum 33 | // capacity of 30. The factory will create 5 initial connections and put it 34 | // into the pool. 35 | p, err := pool.NewChannelPool(5, 30, factory) 36 | 37 | // now you can get a connection from the pool, if there is no connection 38 | // available it will create a new one via the factory function. 39 | conn, err := p.Get() 40 | 41 | // do something with conn and put it back to the pool by closing the connection 42 | // (this doesn't close the underlying connection instead it's putting it back 43 | // to the pool). 44 | conn.Close() 45 | 46 | // close the underlying connection instead of returning it to pool 47 | // it is useful when acceptor has already closed connection and conn.Write() returns error 48 | if pc, ok := conn.(*pool.PoolConn); ok { 49 | pc.MarkUnusable() 50 | pc.Close() 51 | } 52 | 53 | // close pool any time you want, this closes all the connections inside a pool 54 | p.Close() 55 | 56 | // currently available connections in the pool 57 | current := p.Len() 58 | ``` 59 | 60 | 61 | ## Credits 62 | 63 | * [Fatih Arslan](https://github.com/fatih) 64 | * [sougou](https://github.com/sougou) 65 | 66 | ## License 67 | 68 | The MIT License (MIT) - see LICENSE for more details 69 | -------------------------------------------------------------------------------- /channel.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "net" 7 | "sync" 8 | ) 9 | 10 | // channelPool implements the Pool interface based on buffered channels. 11 | type channelPool struct { 12 | // storage for our net.Conn connections 13 | mu sync.RWMutex 14 | conns chan net.Conn 15 | 16 | // net.Conn generator 17 | factory Factory 18 | } 19 | 20 | // Factory is a function to create new connections. 21 | type Factory func() (net.Conn, error) 22 | 23 | // NewChannelPool returns a new pool based on buffered channels with an initial 24 | // capacity and maximum capacity. Factory is used when initial capacity is 25 | // greater than zero to fill the pool. A zero initialCap doesn't fill the Pool 26 | // until a new Get() is called. During a Get(), If there is no new connection 27 | // available in the pool, a new connection will be created via the Factory() 28 | // method. 29 | func NewChannelPool(initialCap, maxCap int, factory Factory) (Pool, error) { 30 | if initialCap < 0 || maxCap <= 0 || initialCap > maxCap { 31 | return nil, errors.New("invalid capacity settings") 32 | } 33 | 34 | c := &channelPool{ 35 | conns: make(chan net.Conn, maxCap), 36 | factory: factory, 37 | } 38 | 39 | // create initial connections, if something goes wrong, 40 | // just close the pool error out. 41 | for i := 0; i < initialCap; i++ { 42 | conn, err := factory() 43 | if err != nil { 44 | c.Close() 45 | return nil, fmt.Errorf("factory is not able to fill the pool: %s", err) 46 | } 47 | c.conns <- conn 48 | } 49 | 50 | return c, nil 51 | } 52 | 53 | func (c *channelPool) getConnsAndFactory() (chan net.Conn, Factory) { 54 | c.mu.RLock() 55 | conns := c.conns 56 | factory := c.factory 57 | c.mu.RUnlock() 58 | return conns, factory 59 | } 60 | 61 | // Get implements the Pool interfaces Get() method. If there is no new 62 | // connection available in the pool, a new connection will be created via the 63 | // Factory() method. 64 | func (c *channelPool) Get() (net.Conn, error) { 65 | conns, factory := c.getConnsAndFactory() 66 | if conns == nil { 67 | return nil, ErrClosed 68 | } 69 | 70 | // wrap our connections with out custom net.Conn implementation (wrapConn 71 | // method) that puts the connection back to the pool if it's closed. 72 | select { 73 | case conn := <-conns: 74 | if conn == nil { 75 | return nil, ErrClosed 76 | } 77 | 78 | return c.wrapConn(conn), nil 79 | default: 80 | conn, err := factory() 81 | if err != nil { 82 | return nil, err 83 | } 84 | 85 | return c.wrapConn(conn), nil 86 | } 87 | } 88 | 89 | // put puts the connection back to the pool. If the pool is full or closed, 90 | // conn is simply closed. A nil conn will be rejected. 91 | func (c *channelPool) put(conn net.Conn) error { 92 | if conn == nil { 93 | return errors.New("connection is nil. rejecting") 94 | } 95 | 96 | c.mu.RLock() 97 | defer c.mu.RUnlock() 98 | 99 | if c.conns == nil { 100 | // pool is closed, close passed connection 101 | return conn.Close() 102 | } 103 | 104 | // put the resource back into the pool. If the pool is full, this will 105 | // block and the default case will be executed. 106 | select { 107 | case c.conns <- conn: 108 | return nil 109 | default: 110 | // pool is full, close passed connection 111 | return conn.Close() 112 | } 113 | } 114 | 115 | func (c *channelPool) Close() { 116 | c.mu.Lock() 117 | conns := c.conns 118 | c.conns = nil 119 | c.factory = nil 120 | c.mu.Unlock() 121 | 122 | if conns == nil { 123 | return 124 | } 125 | 126 | close(conns) 127 | for conn := range conns { 128 | conn.Close() 129 | } 130 | } 131 | 132 | func (c *channelPool) Len() int { 133 | conns, _ := c.getConnsAndFactory() 134 | return len(conns) 135 | } 136 | -------------------------------------------------------------------------------- /channel_test.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "net" 7 | "sync" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | var ( 13 | InitialCap = 5 14 | MaximumCap = 30 15 | network = "tcp" 16 | address = "127.0.0.1:7777" 17 | factory = func() (net.Conn, error) { return net.Dial(network, address) } 18 | ) 19 | 20 | func init() { 21 | // used for factory function 22 | go simpleTCPServer() 23 | time.Sleep(time.Millisecond * 300) // wait until tcp server has been settled 24 | 25 | rand.Seed(time.Now().UTC().UnixNano()) 26 | } 27 | 28 | func TestNew(t *testing.T) { 29 | _, err := newChannelPool() 30 | if err != nil { 31 | t.Errorf("New error: %s", err) 32 | } 33 | } 34 | func TestPool_Get_Impl(t *testing.T) { 35 | p, _ := newChannelPool() 36 | defer p.Close() 37 | 38 | conn, err := p.Get() 39 | if err != nil { 40 | t.Errorf("Get error: %s", err) 41 | } 42 | 43 | _, ok := conn.(*PoolConn) 44 | if !ok { 45 | t.Errorf("Conn is not of type poolConn") 46 | } 47 | } 48 | 49 | func TestPool_Get(t *testing.T) { 50 | p, _ := newChannelPool() 51 | defer p.Close() 52 | 53 | _, err := p.Get() 54 | if err != nil { 55 | t.Errorf("Get error: %s", err) 56 | } 57 | 58 | // after one get, current capacity should be lowered by one. 59 | if p.Len() != (InitialCap - 1) { 60 | t.Errorf("Get error. Expecting %d, got %d", 61 | (InitialCap - 1), p.Len()) 62 | } 63 | 64 | // get them all 65 | var wg sync.WaitGroup 66 | for i := 0; i < (InitialCap - 1); i++ { 67 | wg.Add(1) 68 | go func() { 69 | defer wg.Done() 70 | _, err := p.Get() 71 | if err != nil { 72 | t.Errorf("Get error: %s", err) 73 | } 74 | }() 75 | } 76 | wg.Wait() 77 | 78 | if p.Len() != 0 { 79 | t.Errorf("Get error. Expecting %d, got %d", 80 | (InitialCap - 1), p.Len()) 81 | } 82 | 83 | _, err = p.Get() 84 | if err != nil { 85 | t.Errorf("Get error: %s", err) 86 | } 87 | } 88 | 89 | func TestPool_Put(t *testing.T) { 90 | p, err := NewChannelPool(0, 30, factory) 91 | if err != nil { 92 | t.Fatal(err) 93 | } 94 | defer p.Close() 95 | 96 | // get/create from the pool 97 | conns := make([]net.Conn, MaximumCap) 98 | for i := 0; i < MaximumCap; i++ { 99 | conn, _ := p.Get() 100 | conns[i] = conn 101 | } 102 | 103 | // now put them all back 104 | for _, conn := range conns { 105 | conn.Close() 106 | } 107 | 108 | if p.Len() != MaximumCap { 109 | t.Errorf("Put error len. Expecting %d, got %d", 110 | 1, p.Len()) 111 | } 112 | 113 | conn, _ := p.Get() 114 | p.Close() // close pool 115 | 116 | conn.Close() // try to put into a full pool 117 | if p.Len() != 0 { 118 | t.Errorf("Put error. Closed pool shouldn't allow to put connections.") 119 | } 120 | } 121 | 122 | func TestPool_PutUnusableConn(t *testing.T) { 123 | p, _ := newChannelPool() 124 | defer p.Close() 125 | 126 | // ensure pool is not empty 127 | conn, _ := p.Get() 128 | conn.Close() 129 | 130 | poolSize := p.Len() 131 | conn, _ = p.Get() 132 | conn.Close() 133 | if p.Len() != poolSize { 134 | t.Errorf("Pool size is expected to be equal to initial size") 135 | } 136 | 137 | conn, _ = p.Get() 138 | if pc, ok := conn.(*PoolConn); !ok { 139 | t.Errorf("impossible") 140 | } else { 141 | pc.MarkUnusable() 142 | } 143 | conn.Close() 144 | if p.Len() != poolSize-1 { 145 | t.Errorf("Pool size is expected to be initial_size - 1", p.Len(), poolSize-1) 146 | } 147 | } 148 | 149 | func TestPool_UsedCapacity(t *testing.T) { 150 | p, _ := newChannelPool() 151 | defer p.Close() 152 | 153 | if p.Len() != InitialCap { 154 | t.Errorf("InitialCap error. Expecting %d, got %d", 155 | InitialCap, p.Len()) 156 | } 157 | } 158 | 159 | func TestPool_Close(t *testing.T) { 160 | p, _ := newChannelPool() 161 | 162 | // now close it and test all cases we are expecting. 163 | p.Close() 164 | 165 | c := p.(*channelPool) 166 | 167 | if c.conns != nil { 168 | t.Errorf("Close error, conns channel should be nil") 169 | } 170 | 171 | if c.factory != nil { 172 | t.Errorf("Close error, factory should be nil") 173 | } 174 | 175 | _, err := p.Get() 176 | if err == nil { 177 | t.Errorf("Close error, get conn should return an error") 178 | } 179 | 180 | if p.Len() != 0 { 181 | t.Errorf("Close error used capacity. Expecting 0, got %d", p.Len()) 182 | } 183 | } 184 | 185 | func TestPoolConcurrent(t *testing.T) { 186 | p, _ := newChannelPool() 187 | pipe := make(chan net.Conn, 0) 188 | 189 | go func() { 190 | p.Close() 191 | }() 192 | 193 | for i := 0; i < MaximumCap; i++ { 194 | go func() { 195 | conn, _ := p.Get() 196 | 197 | pipe <- conn 198 | }() 199 | 200 | go func() { 201 | conn := <-pipe 202 | if conn == nil { 203 | return 204 | } 205 | conn.Close() 206 | }() 207 | } 208 | } 209 | 210 | func TestPoolWriteRead(t *testing.T) { 211 | p, _ := NewChannelPool(0, 30, factory) 212 | 213 | conn, _ := p.Get() 214 | 215 | msg := "hello" 216 | _, err := conn.Write([]byte(msg)) 217 | if err != nil { 218 | t.Error(err) 219 | } 220 | } 221 | 222 | func TestPoolConcurrent2(t *testing.T) { 223 | p, _ := NewChannelPool(0, 30, factory) 224 | 225 | var wg sync.WaitGroup 226 | 227 | go func() { 228 | for i := 0; i < 10; i++ { 229 | wg.Add(1) 230 | go func(i int) { 231 | conn, _ := p.Get() 232 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(100))) 233 | conn.Close() 234 | wg.Done() 235 | }(i) 236 | } 237 | }() 238 | 239 | for i := 0; i < 10; i++ { 240 | wg.Add(1) 241 | go func(i int) { 242 | conn, _ := p.Get() 243 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(100))) 244 | conn.Close() 245 | wg.Done() 246 | }(i) 247 | } 248 | 249 | wg.Wait() 250 | } 251 | 252 | func TestPoolConcurrent3(t *testing.T) { 253 | p, _ := NewChannelPool(0, 1, factory) 254 | 255 | var wg sync.WaitGroup 256 | 257 | wg.Add(1) 258 | go func() { 259 | p.Close() 260 | wg.Done() 261 | }() 262 | 263 | if conn, err := p.Get(); err == nil { 264 | conn.Close() 265 | } 266 | 267 | wg.Wait() 268 | } 269 | 270 | func newChannelPool() (Pool, error) { 271 | return NewChannelPool(InitialCap, MaximumCap, factory) 272 | } 273 | 274 | func simpleTCPServer() { 275 | l, err := net.Listen(network, address) 276 | if err != nil { 277 | log.Fatal(err) 278 | } 279 | defer l.Close() 280 | 281 | for { 282 | conn, err := l.Accept() 283 | if err != nil { 284 | log.Fatal(err) 285 | } 286 | 287 | go func() { 288 | buffer := make([]byte, 256) 289 | conn.Read(buffer) 290 | }() 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /conn.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "net" 5 | "sync" 6 | ) 7 | 8 | // PoolConn is a wrapper around net.Conn to modify the the behavior of 9 | // net.Conn's Close() method. 10 | type PoolConn struct { 11 | net.Conn 12 | mu sync.RWMutex 13 | c *channelPool 14 | unusable bool 15 | } 16 | 17 | // Close() puts the given connects back to the pool instead of closing it. 18 | func (p *PoolConn) Close() error { 19 | p.mu.RLock() 20 | defer p.mu.RUnlock() 21 | 22 | if p.unusable { 23 | if p.Conn != nil { 24 | return p.Conn.Close() 25 | } 26 | return nil 27 | } 28 | return p.c.put(p.Conn) 29 | } 30 | 31 | // MarkUnusable() marks the connection not usable any more, to let the pool close it instead of returning it to pool. 32 | func (p *PoolConn) MarkUnusable() { 33 | p.mu.Lock() 34 | p.unusable = true 35 | p.mu.Unlock() 36 | } 37 | 38 | // newConn wraps a standard net.Conn to a poolConn net.Conn. 39 | func (c *channelPool) wrapConn(conn net.Conn) net.Conn { 40 | p := &PoolConn{c: c} 41 | p.Conn = conn 42 | return p 43 | } 44 | -------------------------------------------------------------------------------- /conn_test.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | ) 7 | 8 | func TestConn_Impl(t *testing.T) { 9 | var _ net.Conn = new(PoolConn) 10 | } 11 | -------------------------------------------------------------------------------- /pool.go: -------------------------------------------------------------------------------- 1 | // Package pool implements a pool of net.Conn interfaces to manage and reuse them. 2 | package pool 3 | 4 | import ( 5 | "errors" 6 | "net" 7 | ) 8 | 9 | var ( 10 | // ErrClosed is the error resulting if the pool is closed via pool.Close(). 11 | ErrClosed = errors.New("pool is closed") 12 | ) 13 | 14 | // Pool interface describes a pool implementation. A pool should have maximum 15 | // capacity. An ideal pool is threadsafe and easy to use. 16 | type Pool interface { 17 | // Get returns a new connection from the pool. Closing the connections puts 18 | // it back to the Pool. Closing it when the pool is destroyed or full will 19 | // be counted as an error. 20 | Get() (net.Conn, error) 21 | 22 | // Close closes the pool and all its connections. After Close() the pool is 23 | // no longer usable. 24 | Close() 25 | 26 | // Len returns the current number of connections of the pool. 27 | Len() int 28 | } 29 | --------------------------------------------------------------------------------