You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

153 lines
3.0 KiB

  1. // Copyright 2014 Gary Burd
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License"): you may
  4. // not use this file except in compliance with the License. You may obtain
  5. // a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. // License for the specific language governing permissions and limitations
  13. // under the License.
  14. package redisx
  15. import (
  16. "errors"
  17. "sync"
  18. "github.com/garyburd/redigo/internal"
  19. "github.com/garyburd/redigo/redis"
  20. )
  21. // ConnMux multiplexes one or more connections to a single underlying
  22. // connection. The ConnMux connections do not support concurrency, commands
  23. // that associate server side state with the connection or commands that put
  24. // the connection in a special mode.
  25. type ConnMux struct {
  26. c redis.Conn
  27. sendMu sync.Mutex
  28. sendID uint
  29. recvMu sync.Mutex
  30. recvID uint
  31. recvWait map[uint]chan struct{}
  32. }
  33. func NewConnMux(c redis.Conn) *ConnMux {
  34. return &ConnMux{c: c, recvWait: make(map[uint]chan struct{})}
  35. }
  36. // Get gets a connection. The application must close the returned connection.
  37. func (p *ConnMux) Get() redis.Conn {
  38. c := &muxConn{p: p}
  39. c.ids = c.buf[:0]
  40. return c
  41. }
  42. // Close closes the underlying connection.
  43. func (p *ConnMux) Close() error {
  44. return p.c.Close()
  45. }
  46. type muxConn struct {
  47. p *ConnMux
  48. ids []uint
  49. buf [8]uint
  50. }
  51. func (c *muxConn) send(flush bool, cmd string, args ...interface{}) error {
  52. if internal.LookupCommandInfo(cmd).Set != 0 {
  53. return errors.New("command not supported by mux pool")
  54. }
  55. p := c.p
  56. p.sendMu.Lock()
  57. id := p.sendID
  58. c.ids = append(c.ids, id)
  59. p.sendID++
  60. err := p.c.Send(cmd, args...)
  61. if flush {
  62. err = p.c.Flush()
  63. }
  64. p.sendMu.Unlock()
  65. return err
  66. }
  67. func (c *muxConn) Send(cmd string, args ...interface{}) error {
  68. return c.send(false, cmd, args...)
  69. }
  70. func (c *muxConn) Flush() error {
  71. p := c.p
  72. p.sendMu.Lock()
  73. err := p.c.Flush()
  74. p.sendMu.Unlock()
  75. return err
  76. }
  77. func (c *muxConn) Receive() (interface{}, error) {
  78. if len(c.ids) == 0 {
  79. return nil, errors.New("mux pool underflow")
  80. }
  81. id := c.ids[0]
  82. c.ids = c.ids[1:]
  83. if len(c.ids) == 0 {
  84. c.ids = c.buf[:0]
  85. }
  86. p := c.p
  87. p.recvMu.Lock()
  88. if p.recvID != id {
  89. ch := make(chan struct{})
  90. p.recvWait[id] = ch
  91. p.recvMu.Unlock()
  92. <-ch
  93. p.recvMu.Lock()
  94. if p.recvID != id {
  95. panic("out of sync")
  96. }
  97. }
  98. v, err := p.c.Receive()
  99. id++
  100. p.recvID = id
  101. ch, ok := p.recvWait[id]
  102. if ok {
  103. delete(p.recvWait, id)
  104. }
  105. p.recvMu.Unlock()
  106. if ok {
  107. ch <- struct{}{}
  108. }
  109. return v, err
  110. }
  111. func (c *muxConn) Close() error {
  112. var err error
  113. if len(c.ids) == 0 {
  114. return nil
  115. }
  116. c.Flush()
  117. for _ = range c.ids {
  118. _, err = c.Receive()
  119. }
  120. return err
  121. }
  122. func (c *muxConn) Do(cmd string, args ...interface{}) (interface{}, error) {
  123. if err := c.send(true, cmd, args...); err != nil {
  124. return nil, err
  125. }
  126. return c.Receive()
  127. }
  128. func (c *muxConn) Err() error {
  129. return c.p.c.Err()
  130. }