github.com/gdamore/mangos@v1.4.0/protocol/bus/bus.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 bus implements the BUS protocol.  In this protocol, participants
    16  // send a message to each of their peers.
    17  package bus
    18  
    19  import (
    20  	"encoding/binary"
    21  	"sync"
    22  	"time"
    23  
    24  	"nanomsg.org/go-mangos"
    25  )
    26  
    27  type busEp struct {
    28  	ep mangos.Endpoint
    29  	q  chan *mangos.Message
    30  	x  *bus
    31  }
    32  
    33  type bus struct {
    34  	sock  mangos.ProtocolSocket
    35  	peers map[uint32]*busEp
    36  	raw   bool
    37  	w     mangos.Waiter
    38  	init  sync.Once
    39  
    40  	sync.Mutex
    41  }
    42  
    43  // Init implements the Protocol Init method.
    44  func (x *bus) Init(sock mangos.ProtocolSocket) {
    45  	x.sock = sock
    46  	x.peers = make(map[uint32]*busEp)
    47  	x.w.Init()
    48  	x.w.Add()
    49  	go x.sender()
    50  }
    51  
    52  func (x *bus) Shutdown(expire time.Time) {
    53  
    54  	x.w.WaitAbsTimeout(expire)
    55  
    56  	x.Lock()
    57  	peers := x.peers
    58  	x.peers = make(map[uint32]*busEp)
    59  	x.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 *busEp) peerSender() {
    70  	for {
    71  		m := <-pe.q
    72  		if m == nil {
    73  			return
    74  		}
    75  		if pe.ep.SendMsg(m) != nil {
    76  			m.Free()
    77  			return
    78  		}
    79  	}
    80  }
    81  
    82  func (x *bus) broadcast(m *mangos.Message, sender uint32) {
    83  
    84  	x.Lock()
    85  	for id, pe := range x.peers {
    86  		if sender == id {
    87  			continue
    88  		}
    89  		m = m.Dup()
    90  
    91  		select {
    92  		case pe.q <- m:
    93  		default:
    94  			// No room on outbound queue, drop it.
    95  			// Note that if we are passing on a linger/shutdown
    96  			// notification and we can't deliver due to queue
    97  			// full, it means we will wind up waiting the full
    98  			// linger time in the lower sender.  Its correct, if
    99  			// suboptimal, behavior.
   100  			m.Free()
   101  		}
   102  	}
   103  	x.Unlock()
   104  }
   105  
   106  func (x *bus) sender() {
   107  	cq := x.sock.CloseChannel()
   108  	sq := x.sock.SendChannel()
   109  	defer x.w.Done()
   110  	for {
   111  		var id uint32
   112  		select {
   113  		case <-cq:
   114  			return
   115  		case m := <-sq:
   116  			if m == nil {
   117  				sq = x.sock.SendChannel()
   118  				continue
   119  			}
   120  			// If a header was present, it means this message is
   121  			// being rebroadcast.  It should be a pipe ID.
   122  			if len(m.Header) >= 4 {
   123  				id = binary.BigEndian.Uint32(m.Header)
   124  				m.Header = m.Header[4:]
   125  			}
   126  			x.broadcast(m, id)
   127  			m.Free()
   128  		}
   129  	}
   130  }
   131  
   132  func (pe *busEp) receiver() {
   133  
   134  	rq := pe.x.sock.RecvChannel()
   135  	cq := pe.x.sock.CloseChannel()
   136  
   137  	for {
   138  		m := pe.ep.RecvMsg()
   139  		if m == nil {
   140  			return
   141  		}
   142  		v := pe.ep.GetID()
   143  		m.Header = append(m.Header,
   144  			byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
   145  
   146  		select {
   147  		case rq <- m:
   148  		case <-cq:
   149  			m.Free()
   150  			return
   151  		default:
   152  			// No room, so we just drop it.
   153  			m.Free()
   154  		}
   155  	}
   156  }
   157  
   158  func (x *bus) AddEndpoint(ep mangos.Endpoint) {
   159  	// Set our broadcast depth to match upper depth -- this should
   160  	// help avoid dropping when bursting, if we burst before we
   161  	// context switch.
   162  	depth := 16
   163  	if i, err := x.sock.GetOption(mangos.OptionWriteQLen); err == nil {
   164  		depth = i.(int)
   165  	}
   166  	pe := &busEp{ep: ep, x: x, q: make(chan *mangos.Message, depth)}
   167  	x.Lock()
   168  	x.peers[ep.GetID()] = pe
   169  	x.Unlock()
   170  	go pe.peerSender()
   171  	go pe.receiver()
   172  }
   173  
   174  func (x *bus) RemoveEndpoint(ep mangos.Endpoint) {
   175  	x.Lock()
   176  	if peer := x.peers[ep.GetID()]; peer != nil {
   177  		close(peer.q)
   178  		delete(x.peers, ep.GetID())
   179  	}
   180  	x.Unlock()
   181  }
   182  
   183  func (*bus) Number() uint16 {
   184  	return mangos.ProtoBus
   185  }
   186  
   187  func (*bus) Name() string {
   188  	return "bus"
   189  }
   190  
   191  func (*bus) PeerNumber() uint16 {
   192  	return mangos.ProtoBus
   193  }
   194  
   195  func (*bus) PeerName() string {
   196  	return "bus"
   197  }
   198  
   199  func (x *bus) RecvHook(m *mangos.Message) bool {
   200  	if !x.raw && len(m.Header) >= 4 {
   201  		m.Header = m.Header[4:]
   202  	}
   203  	return true
   204  }
   205  
   206  func (x *bus) SetOption(name string, v interface{}) error {
   207  	var ok bool
   208  	switch name {
   209  	case mangos.OptionRaw:
   210  		if x.raw, ok = v.(bool); !ok {
   211  			return mangos.ErrBadValue
   212  		}
   213  		return nil
   214  	default:
   215  		return mangos.ErrBadOption
   216  	}
   217  }
   218  
   219  func (x *bus) GetOption(name string) (interface{}, error) {
   220  	switch name {
   221  	case mangos.OptionRaw:
   222  		return x.raw, nil
   223  	default:
   224  		return nil, mangos.ErrBadOption
   225  	}
   226  }
   227  
   228  // NewSocket allocates a new Socket using the BUS protocol.
   229  func NewSocket() (mangos.Socket, error) {
   230  	return mangos.MakeSocket(&bus{}), nil
   231  }