github.com/tailscale/wireguard-go@v0.0.20201119-0.20210522003738-46b531feb08a/device/channels.go (about)

     1  /* SPDX-License-Identifier: MIT
     2   *
     3   * Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
     4   */
     5  
     6  package device
     7  
     8  import (
     9  	"runtime"
    10  	"sync"
    11  )
    12  
    13  // An outboundQueue is a channel of QueueOutboundElements awaiting encryption.
    14  // An outboundQueue is ref-counted using its wg field.
    15  // An outboundQueue created with newOutboundQueue has one reference.
    16  // Every additional writer must call wg.Add(1).
    17  // Every completed writer must call wg.Done().
    18  // When no further writers will be added,
    19  // call wg.Done to remove the initial reference.
    20  // When the refcount hits 0, the queue's channel is closed.
    21  type outboundQueue struct {
    22  	c  chan *QueueOutboundElement
    23  	wg sync.WaitGroup
    24  }
    25  
    26  func newOutboundQueue() *outboundQueue {
    27  	q := &outboundQueue{
    28  		c: make(chan *QueueOutboundElement, QueueOutboundSize),
    29  	}
    30  	q.wg.Add(1)
    31  	go func() {
    32  		q.wg.Wait()
    33  		close(q.c)
    34  	}()
    35  	return q
    36  }
    37  
    38  // A inboundQueue is similar to an outboundQueue; see those docs.
    39  type inboundQueue struct {
    40  	c  chan *QueueInboundElement
    41  	wg sync.WaitGroup
    42  }
    43  
    44  func newInboundQueue() *inboundQueue {
    45  	q := &inboundQueue{
    46  		c: make(chan *QueueInboundElement, QueueInboundSize),
    47  	}
    48  	q.wg.Add(1)
    49  	go func() {
    50  		q.wg.Wait()
    51  		close(q.c)
    52  	}()
    53  	return q
    54  }
    55  
    56  // A handshakeQueue is similar to an outboundQueue; see those docs.
    57  type handshakeQueue struct {
    58  	c  chan QueueHandshakeElement
    59  	wg sync.WaitGroup
    60  }
    61  
    62  func newHandshakeQueue() *handshakeQueue {
    63  	q := &handshakeQueue{
    64  		c: make(chan QueueHandshakeElement, QueueHandshakeSize),
    65  	}
    66  	q.wg.Add(1)
    67  	go func() {
    68  		q.wg.Wait()
    69  		close(q.c)
    70  	}()
    71  	return q
    72  }
    73  
    74  type autodrainingInboundQueue struct {
    75  	c chan *QueueInboundElement
    76  }
    77  
    78  // newAutodrainingInboundQueue returns a channel that will be drained when it gets GC'd.
    79  // It is useful in cases in which is it hard to manage the lifetime of the channel.
    80  // The returned channel must not be closed. Senders should signal shutdown using
    81  // some other means, such as sending a sentinel nil values.
    82  func newAutodrainingInboundQueue(device *Device) *autodrainingInboundQueue {
    83  	q := &autodrainingInboundQueue{
    84  		c: make(chan *QueueInboundElement, QueueInboundSize),
    85  	}
    86  	runtime.SetFinalizer(q, device.flushInboundQueue)
    87  	return q
    88  }
    89  
    90  func (device *Device) flushInboundQueue(q *autodrainingInboundQueue) {
    91  	for {
    92  		select {
    93  		case elem := <-q.c:
    94  			elem.Lock()
    95  			device.PutMessageBuffer(elem.buffer)
    96  			device.PutInboundElement(elem)
    97  		default:
    98  			return
    99  		}
   100  	}
   101  }
   102  
   103  type autodrainingOutboundQueue struct {
   104  	c chan *QueueOutboundElement
   105  }
   106  
   107  // newAutodrainingOutboundQueue returns a channel that will be drained when it gets GC'd.
   108  // It is useful in cases in which is it hard to manage the lifetime of the channel.
   109  // The returned channel must not be closed. Senders should signal shutdown using
   110  // some other means, such as sending a sentinel nil values.
   111  // All sends to the channel must be best-effort, because there may be no receivers.
   112  func newAutodrainingOutboundQueue(device *Device) *autodrainingOutboundQueue {
   113  	q := &autodrainingOutboundQueue{
   114  		c: make(chan *QueueOutboundElement, QueueOutboundSize),
   115  	}
   116  	runtime.SetFinalizer(q, device.flushOutboundQueue)
   117  	return q
   118  }
   119  
   120  func (device *Device) flushOutboundQueue(q *autodrainingOutboundQueue) {
   121  	for {
   122  		select {
   123  		case elem := <-q.c:
   124  			elem.Lock()
   125  			device.PutMessageBuffer(elem.buffer)
   126  			device.PutOutboundElement(elem)
   127  		default:
   128  			return
   129  		}
   130  	}
   131  }