github.com/devops-filetransfer/sshego@v7.0.4+incompatible/buzz.go (about)

     1  package sshego
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"sync"
     7  	"time"
     8  
     9  	ssh "github.com/glycerine/sshego/xendor/github.com/glycerine/xcryptossh"
    10  )
    11  
    12  // UHPTower is an 1:M non-blocking value-loadable channel.
    13  //
    14  // Each subscriber gets their own private channel, and it
    15  // will get a copy of whatever is sent to UHPTower.
    16  //
    17  // Sends don't block, as subscribers are given buffered channels.
    18  //
    19  type UHPTower struct {
    20  	subs   []chan *UHP
    21  	mut    sync.Mutex
    22  	closed bool
    23  
    24  	halt *ssh.Halter
    25  }
    26  
    27  // NewUHPTower makes a new UHPTower.
    28  func NewUHPTower(halt *ssh.Halter) *UHPTower {
    29  	if halt == nil {
    30  		halt = ssh.NewHalter()
    31  	}
    32  	tower := &UHPTower{
    33  		halt: halt,
    34  	}
    35  	return tower
    36  }
    37  
    38  // Subscribe if given notify (notify is optional)
    39  // will return notify and notify will receive
    40  // all Broadcast values. If notify is nil, Subscribe
    41  // will allocate a new channel and return that.
    42  // When provided, notify should typically be a size 1 buffered
    43  // chan. If other sizes of chan are used, be sure
    44  // to service reads in a timely manner, or we
    45  // will panic since Subscribe is meant to be
    46  // non-blocking or minimally blocking for a very
    47  // short time. Note that buffer size 1 channels
    48  // are intended for lossy status: where if new
    49  // status arrives before the old is read, it
    50  // is desirable to discard the old and update
    51  // to the new status value. To get non-lossy
    52  // behavior, use an unbuffered notify or
    53  // a buffer with size > 1. In both those
    54  // cases, as above, you must arrange to
    55  // service the channel promptly.
    56  func (b *UHPTower) Subscribe(notify chan *UHP) (ch chan *UHP) {
    57  	pp("UHPTower %p sees Subscribe, notify=%p", b, notify)
    58  
    59  	b.mut.Lock()
    60  	if notify == nil {
    61  		ch = make(chan *UHP, 1)
    62  	} else {
    63  		ch = notify
    64  	}
    65  	b.subs = append(b.subs, ch)
    66  	b.mut.Unlock()
    67  	return ch
    68  }
    69  
    70  func (b *UHPTower) Unsub(x chan *UHP) {
    71  	b.mut.Lock()
    72  	defer b.mut.Unlock()
    73  
    74  	// find it
    75  	k := -1
    76  	for i := range b.subs {
    77  		if b.subs[i] == x {
    78  			k = i
    79  			break
    80  		}
    81  	}
    82  	if k == -1 {
    83  		// not found
    84  		return
    85  	}
    86  	// found. delete it
    87  	b.subs = append(b.subs[:k], b.subs[k+1:]...)
    88  }
    89  
    90  var ErrClosed = fmt.Errorf("channel closed")
    91  
    92  // Broadcast sends a copy of val to all subs.
    93  // Any old unreceived values are purged
    94  // from the receive queues before sending.
    95  // Since the receivers are all buffered
    96  // channels, Broadcast should never block
    97  // waiting on a receiver.
    98  //
    99  // Any subscriber who subscribes after the Broadcast will not
   100  // receive the Broadcast value, as it is not
   101  // stored internally.
   102  //
   103  func (b *UHPTower) Broadcast(val *UHP) error {
   104  	b.mut.Lock()
   105  	defer b.mut.Unlock()
   106  	if b.closed {
   107  		return ErrClosed
   108  	}
   109  	for i := range b.subs {
   110  		if cap(b.subs[i]) == 1 {
   111  			// clear any old, so there is
   112  			// space for the new without blocking.
   113  			select {
   114  			case <-b.subs[i]:
   115  			default:
   116  			}
   117  		}
   118  
   119  		// apply the new
   120  		select {
   121  		case b.subs[i] <- val:
   122  		case <-b.halt.ReqStopChan():
   123  			return b.internalClose()
   124  		case <-time.After(10 * time.Second):
   125  			pp("UHPTower.Broadcast() blocked: could not send for 10 seconds.")
   126  			// return or panic?
   127  			panic("big problem: Broadcast blocked for 10 seconds! Prefer buffered channel of size 1 for Tower subscribe channels.")
   128  		}
   129  	}
   130  	return nil
   131  }
   132  
   133  func (b *UHPTower) Signal(val *UHP) error {
   134  	b.mut.Lock()
   135  	defer b.mut.Unlock()
   136  	if b.closed {
   137  		return ErrClosed
   138  	}
   139  	n := len(b.subs)
   140  	i := rand.Intn(n)
   141  	b.subs[i] <- val
   142  	return nil
   143  }
   144  
   145  func (b *UHPTower) Close() (err error) {
   146  	b.mut.Lock()
   147  	err = b.internalClose()
   148  	b.mut.Unlock()
   149  	return
   150  }
   151  
   152  // for internal use only, caller must have locked b.mut
   153  func (b *UHPTower) internalClose() error {
   154  	if b.closed {
   155  		return ErrClosed
   156  	}
   157  	b.closed = true
   158  
   159  	for i := range b.subs {
   160  		close(b.subs[i])
   161  	}
   162  	b.halt.MarkDone()
   163  	return nil
   164  }
   165  
   166  func (b *UHPTower) Clear() {
   167  	b.mut.Lock()
   168  	for i := range b.subs {
   169  		select {
   170  		case <-b.subs[i]:
   171  		default:
   172  		}
   173  	}
   174  	b.mut.Unlock()
   175  }
   176  
   177  // EmptyUHPChan is helper utility.
   178  // It clears everything out of ch.
   179  func EmptyUHPChan(ch chan *UHP) {
   180  	for {
   181  		select {
   182  		case <-ch:
   183  		default:
   184  			return
   185  		}
   186  	}
   187  }