github.com/gdamore/mangos@v1.4.0/protocol/pub/pub.go (about)

     1  // Copyright 2018 The Mangos Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use file except in compliance with the License.
     5  // You may obtain 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,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package pub implements the PUB protocol.  This protocol publishes messages
    16  // to subscribers (SUB peers).  The subscribers will filter incoming messages
    17  // from the publisher based on their subscription.
    18  package pub
    19  
    20  import (
    21  	"sync"
    22  	"time"
    23  
    24  	"nanomsg.org/go-mangos"
    25  )
    26  
    27  type pubEp struct {
    28  	ep mangos.Endpoint
    29  	q  chan *mangos.Message
    30  	p  *pub
    31  	w  mangos.Waiter
    32  }
    33  
    34  type pub struct {
    35  	sock mangos.ProtocolSocket
    36  	eps  map[uint32]*pubEp
    37  	raw  bool
    38  	w    mangos.Waiter
    39  
    40  	sync.Mutex
    41  }
    42  
    43  func (p *pub) Init(sock mangos.ProtocolSocket) {
    44  	p.sock = sock
    45  	p.eps = make(map[uint32]*pubEp)
    46  	p.sock.SetRecvError(mangos.ErrProtoOp)
    47  	p.w.Init()
    48  	p.w.Add()
    49  	go p.sender()
    50  }
    51  
    52  func (p *pub) Shutdown(expire time.Time) {
    53  
    54  	p.w.WaitAbsTimeout(expire)
    55  
    56  	p.Lock()
    57  	peers := p.eps
    58  	p.eps = make(map[uint32]*pubEp)
    59  	p.Unlock()
    60  
    61  	for id, peer := range peers {
    62  		mangos.DrainChannel(peer.q, expire)
    63  		close(peer.q)
    64  		delete(peers, id)
    65  	}
    66  }
    67  
    68  // Bottom sender.
    69  func (pe *pubEp) peerSender() {
    70  
    71  	for {
    72  		m := <-pe.q
    73  		if m == nil {
    74  			break
    75  		}
    76  
    77  		if pe.ep.SendMsg(m) != nil {
    78  			m.Free()
    79  			break
    80  		}
    81  	}
    82  }
    83  
    84  // Top sender.
    85  func (p *pub) sender() {
    86  	defer p.w.Done()
    87  
    88  	cq := p.sock.CloseChannel()
    89  	sq := p.sock.SendChannel()
    90  
    91  	for {
    92  		select {
    93  		case <-cq:
    94  			return
    95  
    96  		case m := <-sq:
    97  			if m == nil {
    98  				sq = p.sock.SendChannel()
    99  				continue
   100  			}
   101  
   102  			p.Lock()
   103  			for _, peer := range p.eps {
   104  				m := m.Dup()
   105  				select {
   106  				case peer.q <- m:
   107  				default:
   108  					m.Free()
   109  				}
   110  			}
   111  			p.Unlock()
   112  			m.Free()
   113  		}
   114  	}
   115  }
   116  
   117  func (p *pub) AddEndpoint(ep mangos.Endpoint) {
   118  	depth := 16
   119  	if i, err := p.sock.GetOption(mangos.OptionWriteQLen); err == nil {
   120  		depth = i.(int)
   121  	}
   122  	pe := &pubEp{ep: ep, p: p, q: make(chan *mangos.Message, depth)}
   123  	pe.w.Init()
   124  	p.Lock()
   125  	p.eps[ep.GetID()] = pe
   126  	p.Unlock()
   127  
   128  	pe.w.Add()
   129  	go pe.peerSender()
   130  	go mangos.NullRecv(ep)
   131  }
   132  
   133  func (p *pub) RemoveEndpoint(ep mangos.Endpoint) {
   134  	id := ep.GetID()
   135  	p.Lock()
   136  	pe := p.eps[id]
   137  	delete(p.eps, id)
   138  	p.Unlock()
   139  	if pe != nil {
   140  		close(pe.q)
   141  	}
   142  }
   143  
   144  func (*pub) Number() uint16 {
   145  	return mangos.ProtoPub
   146  }
   147  
   148  func (*pub) PeerNumber() uint16 {
   149  	return mangos.ProtoSub
   150  }
   151  
   152  func (*pub) Name() string {
   153  	return "pub"
   154  }
   155  
   156  func (*pub) PeerName() string {
   157  	return "sub"
   158  }
   159  
   160  func (p *pub) SetOption(name string, v interface{}) error {
   161  	var ok bool
   162  	switch name {
   163  	case mangos.OptionRaw:
   164  		if p.raw, ok = v.(bool); !ok {
   165  			return mangos.ErrBadValue
   166  		}
   167  		return nil
   168  	default:
   169  		return mangos.ErrBadOption
   170  	}
   171  }
   172  
   173  func (p *pub) GetOption(name string) (interface{}, error) {
   174  	switch name {
   175  	case mangos.OptionRaw:
   176  		return p.raw, nil
   177  	default:
   178  		return nil, mangos.ErrBadOption
   179  	}
   180  }
   181  
   182  // NewSocket allocates a new Socket using the PUB protocol.
   183  func NewSocket() (mangos.Socket, error) {
   184  	return mangos.MakeSocket(&pub{}), nil
   185  }