github.com/bluenviron/gomavlib/v2@v2.2.1-0.20240308101627-2c07e3da629c/channel.go (about)

     1  package gomavlib
     2  
     3  import (
     4  	"context"
     5  	"crypto/rand"
     6  	"errors"
     7  	"io"
     8  
     9  	"github.com/bluenviron/gomavlib/v2/pkg/frame"
    10  	"github.com/bluenviron/gomavlib/v2/pkg/message"
    11  )
    12  
    13  const (
    14  	writeBufferSize = 64
    15  )
    16  
    17  func randomByte() (byte, error) {
    18  	var buf [1]byte
    19  	_, err := rand.Read(buf[:])
    20  	return buf[0], err
    21  }
    22  
    23  // Channel is a communication channel created by an Endpoint.
    24  // An Endpoint can create channels.
    25  // For instance, a TCP client endpoint creates a single channel, while a TCP
    26  // server endpoint creates a channel for each incoming connection.
    27  type Channel struct {
    28  	n     *Node
    29  	e     Endpoint
    30  	label string
    31  	rwc   io.Closer
    32  
    33  	ctx       context.Context
    34  	ctxCancel func()
    35  	frw       *frame.ReadWriter
    36  	running   bool
    37  
    38  	// in
    39  	chWrite chan interface{}
    40  
    41  	// out
    42  	done chan struct{}
    43  }
    44  
    45  func newChannel(
    46  	n *Node,
    47  	e Endpoint,
    48  	label string,
    49  	rwc io.ReadWriteCloser,
    50  ) (*Channel, error) {
    51  	linkID, err := randomByte()
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	frw, err := frame.NewReadWriter(frame.ReadWriterConf{
    57  		ReadWriter:  rwc,
    58  		DialectRW:   n.dialectRW,
    59  		InKey:       n.conf.InKey,
    60  		OutSystemID: n.conf.OutSystemID,
    61  		OutVersion: func() frame.WriterOutVersion {
    62  			if n.conf.OutVersion == V2 {
    63  				return frame.V2
    64  			}
    65  			return frame.V1
    66  		}(),
    67  		OutComponentID:     n.conf.OutComponentID,
    68  		OutSignatureLinkID: linkID,
    69  		OutKey:             n.conf.OutKey,
    70  	})
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	ctx, ctxCancel := context.WithCancel(context.Background())
    76  
    77  	return &Channel{
    78  		n:         n,
    79  		e:         e,
    80  		label:     label,
    81  		rwc:       rwc,
    82  		ctx:       ctx,
    83  		ctxCancel: ctxCancel,
    84  		frw:       frw,
    85  		chWrite:   make(chan interface{}, writeBufferSize),
    86  		done:      make(chan struct{}),
    87  	}, nil
    88  }
    89  
    90  func (ch *Channel) close() {
    91  	ch.ctxCancel()
    92  	if !ch.running {
    93  		ch.rwc.Close()
    94  	}
    95  }
    96  
    97  func (ch *Channel) start() {
    98  	ch.running = true
    99  	ch.n.wg.Add(1)
   100  	go ch.run()
   101  }
   102  
   103  func (ch *Channel) run() {
   104  	defer close(ch.done)
   105  	defer ch.n.wg.Done()
   106  
   107  	readerDone := make(chan struct{})
   108  	go ch.runReader(readerDone)
   109  
   110  	writerTerminate := make(chan struct{})
   111  	writerDone := make(chan struct{})
   112  	go ch.runWriter(writerTerminate, writerDone)
   113  
   114  	select {
   115  	case <-readerDone:
   116  		ch.rwc.Close()
   117  
   118  		close(writerTerminate)
   119  		<-writerDone
   120  
   121  	case <-ch.ctx.Done():
   122  		close(writerTerminate)
   123  		<-writerDone
   124  
   125  		ch.rwc.Close()
   126  		<-readerDone
   127  	}
   128  
   129  	ch.ctxCancel()
   130  
   131  	ch.n.pushEvent(&EventChannelClose{ch})
   132  	ch.n.closeChannel(ch)
   133  }
   134  
   135  func (ch *Channel) runReader(readerDone chan struct{}) {
   136  	defer close(readerDone)
   137  
   138  	// wait client here, in order to allow the writer goroutine to start
   139  	// and allow clients to write messages before starting listening to events
   140  	ch.n.pushEvent(&EventChannelOpen{ch})
   141  
   142  	for {
   143  		fr, err := ch.frw.Read()
   144  		if err != nil {
   145  			var eerr frame.ReadError
   146  			if errors.As(err, &eerr) {
   147  				ch.n.pushEvent(&EventParseError{err, ch})
   148  				continue
   149  			}
   150  			return
   151  		}
   152  
   153  		evt := &EventFrame{fr, ch}
   154  
   155  		if ch.n.nodeStreamRequest != nil {
   156  			ch.n.nodeStreamRequest.onEventFrame(evt)
   157  		}
   158  
   159  		ch.n.pushEvent(evt)
   160  	}
   161  }
   162  
   163  func (ch *Channel) runWriter(writerTerminate chan struct{}, writerDone chan struct{}) {
   164  	defer close(writerDone)
   165  
   166  	for {
   167  		select {
   168  		case what := <-ch.chWrite:
   169  			switch wh := what.(type) {
   170  			case message.Message:
   171  				ch.frw.WriteMessage(wh) //nolint:errcheck
   172  
   173  			case frame.Frame:
   174  				ch.frw.WriteFrame(wh) //nolint:errcheck
   175  			}
   176  
   177  		case <-writerTerminate:
   178  			return
   179  		}
   180  	}
   181  }
   182  
   183  // String implements fmt.Stringer.
   184  func (ch *Channel) String() string {
   185  	return ch.label
   186  }
   187  
   188  // Endpoint returns the channel Endpoint.
   189  func (ch *Channel) Endpoint() Endpoint {
   190  	return ch.e
   191  }
   192  
   193  func (ch *Channel) write(what interface{}) {
   194  	select {
   195  	case ch.chWrite <- what:
   196  	case <-ch.ctx.Done():
   197  	default: // buffer is full
   198  	}
   199  }