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  }