go.nanomsg.org/mangos/v3@v3.4.3-0.20240217232803-46464076f1f5/internal/core/pipe.go (about)

     1  // Copyright 2019 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 core
    16  
    17  import (
    18  	"crypto/rand"
    19  	"encoding/binary"
    20  	"sync"
    21  
    22  	"go.nanomsg.org/mangos/v3"
    23  	"go.nanomsg.org/mangos/v3/transport"
    24  )
    25  
    26  // This is an application-wide global ID allocator.  Unfortunately we need
    27  // to have unique pipe IDs globally to permit certain things to work
    28  // correctly.
    29  
    30  type pipeIDAllocator struct {
    31  	used map[uint32]struct{}
    32  	next uint32
    33  	lock sync.Mutex
    34  }
    35  
    36  func (p *pipeIDAllocator) Get() uint32 {
    37  	p.lock.Lock()
    38  	defer p.lock.Unlock()
    39  	if p.used == nil {
    40  		b := make([]byte, 4)
    41  		// The following could in theory fail, but in that case
    42  		// we will wind up with IDs starting at zero.  It should
    43  		// not happen unless the platform can't get good entropy.
    44  		_, _ = rand.Read(b)
    45  		p.used = make(map[uint32]struct{})
    46  		p.next = binary.BigEndian.Uint32(b)
    47  	}
    48  	for {
    49  		id := p.next & 0x7fffffff
    50  		p.next++
    51  		if id == 0 {
    52  			continue
    53  		}
    54  		if _, ok := p.used[id]; ok {
    55  			continue
    56  		}
    57  		p.used[id] = struct{}{}
    58  		return id
    59  	}
    60  }
    61  
    62  func (p *pipeIDAllocator) Free(id uint32) {
    63  	p.lock.Lock()
    64  	if _, ok := p.used[id]; !ok {
    65  		panic("free of unused pipe ID")
    66  	}
    67  	delete(p.used, id)
    68  	p.lock.Unlock()
    69  }
    70  
    71  var pipeIDs pipeIDAllocator
    72  
    73  type pipeList struct {
    74  	pipes map[uint32]*pipe
    75  	lock  sync.Mutex
    76  }
    77  
    78  func (l *pipeList) Add(p *pipe) {
    79  	l.lock.Lock()
    80  	if l.pipes == nil {
    81  		l.pipes = make(map[uint32]*pipe)
    82  	}
    83  	l.pipes[p.id] = p
    84  	l.lock.Unlock()
    85  }
    86  
    87  func (l *pipeList) Remove(p *pipe) {
    88  	l.lock.Lock()
    89  	delete(l.pipes, p.id)
    90  	l.lock.Unlock()
    91  }
    92  
    93  // CloseAll closes all pipes, asynchronously.
    94  func (l *pipeList) CloseAll() {
    95  	l.lock.Lock()
    96  	for _, p := range l.pipes {
    97  		go p.close()
    98  	}
    99  	l.lock.Unlock()
   100  }
   101  
   102  // pipe wraps the Pipe data structure with the stuff we need to keep
   103  // for the core.  It implements the Pipe interface.
   104  type pipe struct {
   105  	id        uint32
   106  	p         transport.Pipe
   107  	l         *listener
   108  	d         *dialer
   109  	s         *socket
   110  	closeOnce sync.Once
   111  	data      interface{} // Protocol private
   112  	added     bool
   113  	closing   bool
   114  	lock      sync.Mutex // held across calls to remPipe and addPipe
   115  }
   116  
   117  func newPipe(tp transport.Pipe, s *socket, d *dialer, l *listener) *pipe {
   118  	p := &pipe{
   119  		p:  tp,
   120  		d:  d,
   121  		l:  l,
   122  		s:  s,
   123  		id: pipeIDs.Get(),
   124  	}
   125  	return p
   126  }
   127  
   128  func (p *pipe) ID() uint32 {
   129  	return p.id
   130  }
   131  
   132  func (p *pipe) close() {
   133  	_ = p.Close()
   134  }
   135  
   136  func (p *pipe) Close() error {
   137  	p.closeOnce.Do(func() {
   138  		// Close the underlying transport pipe first.
   139  		_ = p.p.Close()
   140  
   141  		// Deregister it from the socket.  This will also arrange
   142  		// for asynchronously running the event callback, and
   143  		// releasing the pipe ID for reuse.
   144  		p.lock.Lock()
   145  		p.closing = true
   146  		if p.added {
   147  			p.s.remPipe(p)
   148  		}
   149  		p.lock.Unlock()
   150  
   151  		if p.d != nil {
   152  			// Inform the dialer so that it will redial.
   153  			go p.d.pipeClosed()
   154  		}
   155  	})
   156  	return nil
   157  }
   158  
   159  func (p *pipe) SendMsg(msg *mangos.Message) error {
   160  
   161  	if err := p.p.Send(msg); err != nil {
   162  		_ = p.Close()
   163  		return err
   164  	}
   165  	return nil
   166  }
   167  
   168  func (p *pipe) RecvMsg() *mangos.Message {
   169  
   170  	msg, err := p.p.Recv()
   171  	if err != nil {
   172  		_ = p.Close()
   173  		return nil
   174  	}
   175  	msg.Pipe = p
   176  	return msg
   177  }
   178  
   179  func (p *pipe) Address() string {
   180  	switch {
   181  	case p.l != nil:
   182  		return p.l.Address()
   183  	case p.d != nil:
   184  		return p.d.Address()
   185  	}
   186  	return ""
   187  }
   188  
   189  func (p *pipe) GetOption(name string) (interface{}, error) {
   190  	val, err := p.p.GetOption(name)
   191  	if err == mangos.ErrBadOption {
   192  		if p.d != nil {
   193  			val, err = p.d.GetOption(name)
   194  		} else if p.l != nil {
   195  			val, err = p.l.GetOption(name)
   196  		}
   197  	}
   198  	return val, err
   199  }
   200  
   201  func (p *pipe) Dialer() mangos.Dialer {
   202  	if p.d == nil {
   203  		return nil
   204  	}
   205  	return p.d
   206  }
   207  
   208  func (p *pipe) Listener() mangos.Listener {
   209  	if p.l == nil {
   210  		return nil
   211  	}
   212  	return p.l
   213  }
   214  
   215  func (p *pipe) SetPrivate(i interface{}) {
   216  	p.data = i
   217  }
   218  
   219  func (p *pipe) GetPrivate() interface{} {
   220  	return p.data
   221  }