github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/integration/messagebus/glue/channel.go (about) 1 /* 2 * Glue - Robust Go and Javascript Socket Library 3 * Copyright (C) 2015 Roland Singer <roland.singer[at]desertbit.com> 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 package glue 20 21 import ( 22 "fmt" 23 "runtime/debug" 24 "sync" 25 "time" 26 27 "github.com/mdaxf/iac/integration/messagebus/glue/log" 28 "github.com/mdaxf/iac/integration/messagebus/glue/utils" 29 ) 30 31 //#################// 32 //### Constants ###// 33 //#################// 34 35 const ( 36 // The channel buffer size for received data. 37 readChanBuffer = 7 38 ) 39 40 //####################// 41 //### Channel type ###// 42 //####################// 43 44 // A Channel is a separate communication channel. 45 type Channel struct { 46 s *Socket 47 readHandler *handler 48 49 name string 50 readChan chan string 51 } 52 53 func newChannel(s *Socket, name string) *Channel { 54 return &Channel{ 55 s: s, 56 readHandler: newHandler(), 57 name: name, 58 readChan: make(chan string, readChanBuffer), 59 } 60 } 61 62 // Socket returns the channel's socket. 63 func (c *Channel) Socket() *Socket { 64 return c.s 65 } 66 67 // Write data to the channel. 68 func (c *Channel) Write(data string) { 69 // Prepend the socket command and send the channel name and data. 70 c.s.write(cmdChannelData + utils.MarshalValues(c.name, data)) 71 } 72 73 // Read the next message from the channel. This method is blocking. 74 // One variadic argument sets a timeout duration. 75 // If no timeout is specified, this method will block forever. 76 // ErrSocketClosed is returned, if the socket connection is closed. 77 // ErrReadTimeout is returned, if the timeout is reached. 78 func (c *Channel) Read(timeout ...time.Duration) (string, error) { 79 timeoutChan := make(chan (struct{})) 80 81 // Create a timeout timer if a timeout is specified. 82 if len(timeout) > 0 && timeout[0] > 0 { 83 timer := time.AfterFunc(timeout[0], func() { 84 // Trigger the timeout by closing the channel. 85 close(timeoutChan) 86 }) 87 88 // Always stop the timer on defer. 89 defer timer.Stop() 90 } 91 92 select { 93 case data := <-c.readChan: 94 return data, nil 95 case <-c.s.isClosedChan: 96 // The connection was closed. 97 // Return an error. 98 return "", ErrSocketClosed 99 case <-timeoutChan: 100 // The timeout was reached. 101 // Return an error. 102 return "", ErrReadTimeout 103 } 104 } 105 106 // OnRead sets the function which is triggered if new data is received on the channel. 107 // If this event function based method of reading data from the socket is used, 108 // then don't use the socket Read method. 109 // Either use the OnRead or the Read approach. 110 func (c *Channel) OnRead(f OnReadFunc) { 111 // Create a new read handler for this channel. 112 // Previous handlers are stopped first. 113 handlerStopped := c.readHandler.New() 114 115 // Start the handler goroutine. 116 go func() { 117 for { 118 select { 119 case data := <-c.readChan: 120 // Call the callback in a new goroutine. 121 go func() { 122 // Recover panics and log the error. 123 defer func() { 124 if e := recover(); e != nil { 125 log.L.Errorf("glue: panic while calling onRead function: %v\n%s", e, debug.Stack()) 126 } 127 }() 128 129 // Trigger the on read event function. 130 f(data) 131 }() 132 case <-c.s.isClosedChan: 133 // Release this goroutine if the socket is closed. 134 return 135 case <-handlerStopped: 136 // Release this goroutine. 137 return 138 } 139 } 140 }() 141 } 142 143 // DiscardRead ignores and discars the data received from this channel. 144 // Call this method during initialization, if you don't read any data from 145 // this channel. If received data is not discarded, then the read buffer will block as soon 146 // as it is full, which will also block the keep-alive mechanism of the socket. The result 147 // would be a closed socket... 148 func (c *Channel) DiscardRead() { 149 // Create a new read handler for this channel. 150 // Previous handlers are stopped first. 151 handlerStopped := c.readHandler.New() 152 153 // Start the handler goroutine. 154 go func() { 155 for { 156 select { 157 case <-c.readChan: 158 // Don't do anything. 159 // Just discard the data. 160 case <-c.s.isClosedChan: 161 // Release this goroutine if the socket is closed. 162 return 163 case <-handlerStopped: 164 // Release this goroutine. 165 return 166 } 167 } 168 }() 169 } 170 171 func (c *Channel) triggerRead(data string) { 172 // Send the data to the read channel. 173 c.readChan <- data 174 } 175 176 //#####################// 177 //### Channels type ###// 178 //#####################// 179 180 type channels struct { 181 m map[string]*Channel 182 mutex sync.Mutex 183 } 184 185 func newChannels() *channels { 186 return &channels{ 187 m: make(map[string]*Channel), 188 } 189 } 190 191 func (cs *channels) get(name string) *Channel { 192 // Lock the mutex. 193 cs.mutex.Lock() 194 defer cs.mutex.Unlock() 195 196 return cs.m[name] 197 } 198 199 func (cs *channels) triggerReadForChannel(name, data string) error { 200 // Get the channel. 201 c := cs.get(name) 202 if c == nil { 203 return fmt.Errorf("received data for channel '%s': channel does not exists", name) 204 } 205 206 // Trigger the read. 207 c.triggerRead(data) 208 209 return nil 210 } 211 212 //#################################// 213 //### Additional Socket Methods ###// 214 //#################################// 215 216 // Channel returns the corresponding channel value specified by the name. 217 // If no channel value exists for the given name, a new channel is created. 218 // Multiple calls to Channel with the same name, will always return the same 219 // channel value pointer. 220 func (s *Socket) Channel(name string) *Channel { 221 // Get the socket channel pointer. 222 cs := s.channels 223 224 // Lock the mutex. 225 cs.mutex.Lock() 226 defer cs.mutex.Unlock() 227 228 // Get the channel if it exists. 229 c, ok := cs.m[name] 230 if ok { 231 return c 232 } 233 234 // Create and add the new channel to the socket channels map. 235 c = newChannel(s, name) 236 cs.m[name] = c 237 238 return c 239 }