github.com/stafiprotocol/go-substrate-rpc-client@v1.4.7/pkg/websocketpool/pool.go (about) 1 package websocket_pool 2 3 import ( 4 "errors" 5 "fmt" 6 "sync" 7 8 "github.com/sirupsen/logrus" 9 "github.com/stafiprotocol/go-substrate-rpc-client/pkg/recws" 10 ) 11 12 var ( 13 ErrClosed = errors.New("pool is closed") 14 ) 15 16 type Pool interface { 17 Get() (*PoolConn, error) 18 Put(*PoolConn) error 19 Len() int 20 } 21 22 type WsPool struct { 23 mu sync.RWMutex 24 exist map[*PoolConn]bool 25 conns chan *PoolConn 26 factory Factory 27 } 28 29 type Factory func() (*PoolConn, error) 30 31 func NewWsPool(initialCap, maxCap int, factory Factory) (Pool, error) { 32 if initialCap < 0 || maxCap <= 0 || initialCap > maxCap { 33 return nil, errors.New("invalid capacity settings") 34 } 35 c := &WsPool{ 36 conns: make(chan *PoolConn, maxCap), 37 exist: map[*PoolConn]bool{}, 38 factory: factory, 39 } 40 c.mu.Lock() 41 defer c.mu.Unlock() 42 for i := 0; i < initialCap; i++ { 43 conn, err := factory() 44 if err != nil { 45 c.Close() 46 return nil, fmt.Errorf("factory is not able to fill the pool: %s", err) 47 } 48 c.conns <- conn 49 c.exist[conn] = true 50 } 51 return c, nil 52 } 53 54 func (c *WsPool) getConnsAndFactory() (chan *PoolConn, Factory) { 55 conns := c.conns 56 factory := c.factory 57 return conns, factory 58 } 59 60 func (c *WsPool) Get() (*PoolConn, error) { 61 conns, factory := c.getConnsAndFactory() 62 if conns == nil { 63 return nil, ErrClosed 64 } 65 logrus.Tracef("WsPool.Get pool len :%d", len(c.conns)) 66 67 var err error 68 select { 69 case conn := <-conns: 70 if conn == nil || !conn.Conn.IsConnected() { 71 c.mu.Lock() 72 delete(c.exist, conn) 73 c.mu.Unlock() 74 75 logrus.Trace("use factory reconnect") 76 conn, err = factory() 77 if err != nil { 78 return nil, err 79 } 80 } else { 81 c.mu.Lock() 82 delete(c.exist, conn) 83 c.mu.Unlock() 84 } 85 return conn, nil 86 default: 87 conn, err := factory() 88 if err != nil { 89 return nil, err 90 } 91 return conn, nil 92 } 93 } 94 95 func (c *WsPool) Put(conn *PoolConn) error { 96 if conn == nil { 97 return errors.New("connection is nil. rejecting") 98 } 99 conn.mu.Lock() 100 defer conn.mu.Unlock() 101 102 logrus.Tracef("WsPool.Put pool len :%d", len(c.conns)) 103 if conn.unusable { 104 if conn.Conn != nil { 105 conn.Conn.Close() 106 } 107 return nil 108 } 109 110 c.mu.RLock() 111 if c.exist[conn] { 112 c.mu.RUnlock() 113 return nil 114 } 115 c.mu.RUnlock() 116 117 if c.conns == nil { 118 conn.Conn.Close() 119 return nil 120 } 121 122 select { 123 case c.conns <- conn: 124 c.mu.Lock() 125 c.exist[conn] = true 126 c.mu.Unlock() 127 return nil 128 default: 129 conn.Conn.Close() 130 return nil 131 } 132 } 133 134 func (c *WsPool) Close() { 135 c.mu.Lock() 136 conns := c.conns 137 c.conns = nil 138 c.factory = nil 139 c.mu.Unlock() 140 141 if conns == nil { 142 return 143 } 144 145 close(conns) 146 for conn := range conns { 147 conn.Conn.Close() 148 } 149 } 150 151 func (c *WsPool) Len() int { 152 c.mu.RLock() 153 defer c.mu.RUnlock() 154 conns, _ := c.getConnsAndFactory() 155 return len(conns) 156 } 157 158 type PoolConn struct { 159 Conn *recws.RecConn 160 mu sync.RWMutex 161 unusable bool 162 } 163 164 // MarkUnusable() marks the connection not usable any more, to let the pool close it instead of returning it to pool. 165 func (p *PoolConn) MarkUnusable() { 166 p.mu.Lock() 167 defer p.mu.Unlock() 168 p.unusable = true 169 } 170 171 // newConn wraps a standard net.Conn to a poolConn net.Conn. 172 func WrapConn(conn *recws.RecConn) *PoolConn { 173 p := &PoolConn{} 174 p.Conn = conn 175 return p 176 }