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 }