github.com/gdamore/mangos@v1.4.0/transport/inproc/inproc.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 inproc implements an simple inproc transport for mangos.
    16  package inproc
    17  
    18  import (
    19  	"strings"
    20  	"sync"
    21  
    22  	"nanomsg.org/go-mangos"
    23  )
    24  
    25  // inproc implements the Pipe interface on top of channels.
    26  type inproc struct {
    27  	rq     chan *mangos.Message
    28  	wq     chan *mangos.Message
    29  	closeq chan struct{}
    30  	readyq chan struct{}
    31  	proto  mangos.Protocol
    32  	addr   addr
    33  	peer   *inproc
    34  	sync.Mutex
    35  }
    36  
    37  type addr string
    38  
    39  func (a addr) String() string {
    40  	s := string(a)
    41  	if strings.HasPrefix(s, "inproc://") {
    42  		s = s[len("inproc://"):]
    43  	}
    44  	return s
    45  }
    46  
    47  func (addr) Network() string {
    48  	return "inproc"
    49  }
    50  
    51  type listener struct {
    52  	addr      string
    53  	proto     mangos.Protocol
    54  	accepters []*inproc
    55  }
    56  
    57  type inprocTran struct{}
    58  
    59  var listeners struct {
    60  	// Who is listening, on which "address"?
    61  	byAddr map[string]*listener
    62  	cv     sync.Cond
    63  	mx     sync.Mutex
    64  }
    65  
    66  func init() {
    67  	listeners.byAddr = make(map[string]*listener)
    68  	listeners.cv.L = &listeners.mx
    69  }
    70  
    71  func (p *inproc) Recv() (*mangos.Message, error) {
    72  
    73  	if p.peer == nil {
    74  		return nil, mangos.ErrClosed
    75  	}
    76  	select {
    77  	case m, ok := <-p.rq:
    78  		if m == nil || !ok {
    79  			return nil, mangos.ErrClosed
    80  		}
    81  		// Upper protocols expect to have to pick header and
    82  		// body part.  So mush them back together.
    83  		//msg.Body = append(msg.Header, msg.Body...)
    84  		//msg.Header = make([]byte, 0, 32)
    85  		return m, nil
    86  	case <-p.closeq:
    87  		return nil, mangos.ErrClosed
    88  	case <-p.peer.closeq:
    89  		return nil, mangos.ErrClosed
    90  	}
    91  }
    92  
    93  func (p *inproc) Send(m *mangos.Message) error {
    94  
    95  	if p.peer == nil {
    96  		return mangos.ErrClosed
    97  	}
    98  
    99  	if m.Expired() {
   100  		m.Free()
   101  		return nil
   102  	}
   103  
   104  	// Upper protocols expect to have to pick header and body part.
   105  	// Also we need to have a fresh copy of the message for receiver, to
   106  	// break ownership.
   107  	nmsg := mangos.NewMessage(len(m.Header) + len(m.Body))
   108  	nmsg.Body = append(nmsg.Body, m.Header...)
   109  	nmsg.Body = append(nmsg.Body, m.Body...)
   110  	select {
   111  	case p.wq <- nmsg:
   112  		return nil
   113  	case <-p.closeq:
   114  		nmsg.Free()
   115  		return mangos.ErrClosed
   116  	case <-p.peer.closeq:
   117  		nmsg.Free()
   118  		return mangos.ErrClosed
   119  	}
   120  }
   121  
   122  func (p *inproc) LocalProtocol() uint16 {
   123  	return p.proto.Number()
   124  }
   125  
   126  func (p *inproc) RemoteProtocol() uint16 {
   127  	return p.proto.PeerNumber()
   128  }
   129  
   130  func (p *inproc) Close() error {
   131  	p.Lock()
   132  	defer p.Unlock()
   133  	if p.IsOpen() {
   134  		close(p.closeq)
   135  	}
   136  	return nil
   137  }
   138  
   139  func (p *inproc) IsOpen() bool {
   140  	select {
   141  	case <-p.closeq:
   142  		return false
   143  	default:
   144  		return true
   145  	}
   146  }
   147  
   148  func (p *inproc) GetProp(name string) (interface{}, error) {
   149  	switch name {
   150  	case mangos.PropRemoteAddr:
   151  		return p.addr, nil
   152  	case mangos.PropLocalAddr:
   153  		return p.addr, nil
   154  	}
   155  	// We have no special properties
   156  	return nil, mangos.ErrBadProperty
   157  }
   158  
   159  type dialer struct {
   160  	addr  string
   161  	proto mangos.Protocol
   162  }
   163  
   164  func (d *dialer) Dial() (mangos.Pipe, error) {
   165  
   166  	var server *inproc
   167  	client := &inproc{proto: d.proto, addr: addr(d.addr)}
   168  	client.readyq = make(chan struct{})
   169  	client.closeq = make(chan struct{})
   170  
   171  	listeners.mx.Lock()
   172  
   173  	// NB: No timeouts here!
   174  	for {
   175  		var l *listener
   176  		var ok bool
   177  		if l, ok = listeners.byAddr[d.addr]; !ok || l == nil {
   178  			listeners.mx.Unlock()
   179  			return nil, mangos.ErrConnRefused
   180  		}
   181  
   182  		if !mangos.ValidPeers(client.proto, l.proto) {
   183  			return nil, mangos.ErrBadProto
   184  		}
   185  
   186  		if len(l.accepters) != 0 {
   187  			server = l.accepters[len(l.accepters)-1]
   188  			l.accepters = l.accepters[:len(l.accepters)-1]
   189  			break
   190  		}
   191  
   192  		listeners.cv.Wait()
   193  		continue
   194  	}
   195  
   196  	listeners.mx.Unlock()
   197  
   198  	server.wq = make(chan *mangos.Message)
   199  	server.rq = make(chan *mangos.Message)
   200  	client.rq = server.wq
   201  	client.wq = server.rq
   202  	server.peer = client
   203  	client.peer = server
   204  
   205  	close(server.readyq)
   206  	close(client.readyq)
   207  	return client, nil
   208  }
   209  
   210  func (*dialer) SetOption(string, interface{}) error {
   211  	return mangos.ErrBadOption
   212  }
   213  
   214  func (*dialer) GetOption(string) (interface{}, error) {
   215  	return nil, mangos.ErrBadOption
   216  }
   217  
   218  func (l *listener) Listen() error {
   219  	listeners.mx.Lock()
   220  	if _, ok := listeners.byAddr[l.addr]; ok {
   221  		listeners.mx.Unlock()
   222  		return mangos.ErrAddrInUse
   223  	}
   224  	listeners.byAddr[l.addr] = l
   225  	listeners.cv.Broadcast()
   226  	listeners.mx.Unlock()
   227  	return nil
   228  }
   229  
   230  func (l *listener) Address() string {
   231  	return l.addr
   232  }
   233  
   234  func (l *listener) Accept() (mangos.Pipe, error) {
   235  	server := &inproc{proto: l.proto, addr: addr(l.addr)}
   236  	server.readyq = make(chan struct{})
   237  	server.closeq = make(chan struct{})
   238  
   239  	listeners.mx.Lock()
   240  	l.accepters = append(l.accepters, server)
   241  	listeners.cv.Broadcast()
   242  	listeners.mx.Unlock()
   243  
   244  	select {
   245  	case <-server.readyq:
   246  		return server, nil
   247  	case <-server.closeq:
   248  		return nil, mangos.ErrClosed
   249  	}
   250  }
   251  
   252  func (*listener) SetOption(string, interface{}) error {
   253  	return mangos.ErrBadOption
   254  }
   255  
   256  func (*listener) GetOption(string) (interface{}, error) {
   257  	return nil, mangos.ErrBadOption
   258  }
   259  
   260  func (l *listener) Close() error {
   261  	listeners.mx.Lock()
   262  	if listeners.byAddr[l.addr] == l {
   263  		delete(listeners.byAddr, l.addr)
   264  	}
   265  	servers := l.accepters
   266  	l.accepters = nil
   267  	listeners.cv.Broadcast()
   268  	listeners.mx.Unlock()
   269  
   270  	for _, s := range servers {
   271  		close(s.closeq)
   272  	}
   273  
   274  	return nil
   275  }
   276  
   277  func (t *inprocTran) Scheme() string {
   278  	return "inproc"
   279  }
   280  
   281  func (t *inprocTran) NewDialer(addr string, sock mangos.Socket) (mangos.PipeDialer, error) {
   282  	if _, err := mangos.StripScheme(t, addr); err != nil {
   283  		return nil, err
   284  	}
   285  	return &dialer{addr: addr, proto: sock.GetProtocol()}, nil
   286  }
   287  
   288  func (t *inprocTran) NewListener(addr string, sock mangos.Socket) (mangos.PipeListener, error) {
   289  	if _, err := mangos.StripScheme(t, addr); err != nil {
   290  		return nil, err
   291  	}
   292  	l := &listener{addr: addr, proto: sock.GetProtocol()}
   293  	return l, nil
   294  }
   295  
   296  // NewTransport allocates a new inproc:// transport.
   297  func NewTransport() mangos.Transport {
   298  	return &inprocTran{}
   299  }