github.com/noisysockets/noisysockets@v0.21.2-0.20240515114641-7f467e651c90/internal/transport/channels.go (about)

     1  // SPDX-License-Identifier: MPL-2.0
     2  /*
     3   * Copyright (C) 2024 The Noisy Sockets Authors.
     4   *
     5   * This Source Code Form is subject to the terms of the Mozilla Public
     6   * License, v. 2.0. If a copy of the MPL was not distributed with this
     7   * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     8   *
     9   * Portions of this file are based on code originally from wireguard-go,
    10   *
    11   * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
    12   *
    13   * Permission is hereby granted, free of charge, to any person obtaining a copy of
    14   * this software and associated documentation files (the "Software"), to deal in
    15   * the Software without restriction, including without limitation the rights to
    16   * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
    17   * of the Software, and to permit persons to whom the Software is furnished to do
    18   * so, subject to the following conditions:
    19   *
    20   * The above copyright notice and this permission notice shall be included in all
    21   * copies or substantial portions of the Software.
    22   *
    23   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    24   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    25   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    26   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    27   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    28   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    29   * SOFTWARE.
    30   */
    31  
    32  package transport
    33  
    34  import (
    35  	"runtime"
    36  	"sync"
    37  )
    38  
    39  // An outboundQueue is a channel of QueueOutboundElements awaiting encryption.
    40  // An outboundQueue is ref-counted using its wg field.
    41  // An outboundQueue created with newOutboundQueue has one reference.
    42  // Every additional writer must call wg.Add(1).
    43  // Every completed writer must call wg.Done().
    44  // When no further writers will be added,
    45  // call wg.Done to remove the initial reference.
    46  // When the refcount hits 0, the queue's channel is closed.
    47  type outboundQueue struct {
    48  	c  chan *QueueOutboundElementsContainer
    49  	wg sync.WaitGroup
    50  }
    51  
    52  func newOutboundQueue() *outboundQueue {
    53  	q := &outboundQueue{
    54  		c: make(chan *QueueOutboundElementsContainer, QueueOutboundSize),
    55  	}
    56  	q.wg.Add(1)
    57  	go func() {
    58  		q.wg.Wait()
    59  		close(q.c)
    60  	}()
    61  	return q
    62  }
    63  
    64  // A inboundQueue is similar to an outboundQueue; see those docs.
    65  type inboundQueue struct {
    66  	c  chan *QueueInboundElementsContainer
    67  	wg sync.WaitGroup
    68  }
    69  
    70  func newInboundQueue() *inboundQueue {
    71  	q := &inboundQueue{
    72  		c: make(chan *QueueInboundElementsContainer, QueueInboundSize),
    73  	}
    74  	q.wg.Add(1)
    75  	go func() {
    76  		q.wg.Wait()
    77  		close(q.c)
    78  	}()
    79  	return q
    80  }
    81  
    82  // A handshakeQueue is similar to an outboundQueue; see those docs.
    83  type handshakeQueue struct {
    84  	c  chan QueueHandshakeElement
    85  	wg sync.WaitGroup
    86  }
    87  
    88  func newHandshakeQueue() *handshakeQueue {
    89  	q := &handshakeQueue{
    90  		c: make(chan QueueHandshakeElement, QueueHandshakeSize),
    91  	}
    92  	q.wg.Add(1)
    93  	go func() {
    94  		q.wg.Wait()
    95  		close(q.c)
    96  	}()
    97  	return q
    98  }
    99  
   100  type autodrainingInboundQueue struct {
   101  	c chan *QueueInboundElementsContainer
   102  }
   103  
   104  // newAutodrainingInboundQueue returns a channel that will be drained when it gets GC'd.
   105  // It is useful in cases in which is it hard to manage the lifetime of the channel.
   106  // The returned channel must not be closed. Senders should signal shutdown using
   107  // some other means, such as sending a sentinel nil values.
   108  func newAutodrainingInboundQueue(transport *Transport) *autodrainingInboundQueue {
   109  	q := &autodrainingInboundQueue{
   110  		c: make(chan *QueueInboundElementsContainer, QueueInboundSize),
   111  	}
   112  	runtime.SetFinalizer(q, transport.flushInboundQueue)
   113  	return q
   114  }
   115  
   116  func (transport *Transport) flushInboundQueue(q *autodrainingInboundQueue) {
   117  	for {
   118  		select {
   119  		case elemsContainer := <-q.c:
   120  			elemsContainer.Lock()
   121  			for _, elem := range elemsContainer.elems {
   122  				transport.PutMessageBuffer(elem.buffer)
   123  				transport.PutInboundElement(elem)
   124  			}
   125  			transport.PutInboundElementsContainer(elemsContainer)
   126  		default:
   127  			return
   128  		}
   129  	}
   130  }
   131  
   132  type autodrainingOutboundQueue struct {
   133  	c chan *QueueOutboundElementsContainer
   134  }
   135  
   136  // newAutodrainingOutboundQueue returns a channel that will be drained when it gets GC'd.
   137  // It is useful in cases in which is it hard to manage the lifetime of the channel.
   138  // The returned channel must not be closed. Senders should signal shutdown using
   139  // some other means, such as sending a sentinel nil values.
   140  // All sends to the channel must be best-effort, because there may be no receivers.
   141  func newAutodrainingOutboundQueue(transport *Transport) *autodrainingOutboundQueue {
   142  	q := &autodrainingOutboundQueue{
   143  		c: make(chan *QueueOutboundElementsContainer, QueueOutboundSize),
   144  	}
   145  	runtime.SetFinalizer(q, transport.flushOutboundQueue)
   146  	return q
   147  }
   148  
   149  func (transport *Transport) flushOutboundQueue(q *autodrainingOutboundQueue) {
   150  	for {
   151  		select {
   152  		case elemsContainer := <-q.c:
   153  			elemsContainer.Lock()
   154  			for _, elem := range elemsContainer.elems {
   155  				transport.PutMessageBuffer(elem.buffer)
   156  				transport.PutOutboundElement(elem)
   157  			}
   158  			transport.PutOutboundElementsContainer(elemsContainer)
   159  		default:
   160  			return
   161  		}
   162  	}
   163  }