github.com/la5nta/wl2k-go@v0.11.8/transport/ax25/agwpe/demux.go (about)

     1  package agwpe
     2  
     3  import (
     4  	"sync"
     5  )
     6  
     7  type framesFilter struct {
     8  	kinds []kind
     9  	port  *uint8
    10  	call  callsign // to OR from
    11  	to    callsign
    12  }
    13  
    14  type framesReq struct {
    15  	framesFilter
    16  	once bool
    17  	done chan struct{}
    18  	resp chan frame
    19  }
    20  
    21  func newFramesReq(bufSize int, filter framesFilter) framesReq {
    22  	return framesReq{
    23  		framesFilter: filter,
    24  		done:         make(chan struct{}),
    25  		resp:         make(chan frame, bufSize),
    26  	}
    27  }
    28  
    29  func (r framesReq) Cancel() { close(r.done) }
    30  
    31  func (f framesFilter) Want(frame frame) bool {
    32  	switch {
    33  	case f.port != nil && *f.port != frame.Port:
    34  		return false
    35  	case f.call != (callsign{}) && !(f.call == frame.From || f.call == frame.To):
    36  		return false
    37  	case f.to != (callsign{}) && !(f.to == frame.To):
    38  		return false
    39  	}
    40  	if len(f.kinds) == 0 {
    41  		return true
    42  	}
    43  	for _, k := range f.kinds {
    44  		if frame.DataKind == k {
    45  			return true
    46  		}
    47  	}
    48  	return false
    49  }
    50  
    51  type demux struct {
    52  	requests chan framesReq
    53  
    54  	mu     sync.Mutex
    55  	closed bool
    56  	in     chan frame
    57  }
    58  
    59  func newDemux() *demux {
    60  	d := demux{
    61  		in:       make(chan frame, 1),
    62  		requests: make(chan framesReq),
    63  	}
    64  	go d.run()
    65  	return &d
    66  }
    67  
    68  func (d *demux) isClosed() bool {
    69  	d.mu.Lock()
    70  	defer d.mu.Unlock()
    71  	return d.closed
    72  }
    73  
    74  func (d *demux) Chain(filter framesFilter) *demux {
    75  	if d.isClosed() {
    76  		panic("demux closed")
    77  	}
    78  	next := newDemux()
    79  	filtered, cancel := d.Frames(0, filter)
    80  	go func() {
    81  		defer cancel()
    82  		defer next.Close()
    83  		defer debugf("chain exited")
    84  		for {
    85  			f, ok := <-filtered
    86  			if !ok {
    87  				return
    88  			}
    89  			if !next.Enqueue(f) {
    90  				return
    91  			}
    92  		}
    93  	}()
    94  	return next
    95  }
    96  
    97  func (d *demux) Close() error {
    98  	d.mu.Lock()
    99  	defer d.mu.Unlock()
   100  	if d.closed {
   101  		return nil
   102  	}
   103  	close(d.in)
   104  	d.closed = true
   105  	return nil
   106  }
   107  
   108  func (d *demux) Enqueue(f frame) bool {
   109  	d.mu.Lock()
   110  	defer d.mu.Unlock()
   111  	if d.closed {
   112  		return false
   113  	}
   114  	select {
   115  	case d.in <- f:
   116  	default:
   117  		debugf("port buffer full - dropping frame")
   118  	}
   119  	return true
   120  }
   121  
   122  func (d *demux) NextFrame(kinds ...kind) <-chan frame {
   123  	d.mu.Lock()
   124  	defer d.mu.Unlock()
   125  	if d.closed {
   126  		c := make(chan frame)
   127  		close(c)
   128  		return c
   129  	}
   130  	req := newFramesReq(1, framesFilter{kinds: kinds})
   131  	req.once = true
   132  	d.requests <- req
   133  	return req.resp
   134  }
   135  
   136  func (d *demux) Frames(bufSize int, filter framesFilter) (filtered <-chan frame, cancel func()) {
   137  	d.mu.Lock()
   138  	defer d.mu.Unlock()
   139  	if d.closed {
   140  		return nil, func() {}
   141  	}
   142  	req := newFramesReq(bufSize, filter)
   143  	req.once = false
   144  	d.requests <- req
   145  	return req.resp, req.Cancel
   146  }
   147  
   148  func (d *demux) run() {
   149  	defer debugf("demux exited")
   150  	var clients []framesReq
   151  	for {
   152  		select {
   153  		case c := <-d.requests:
   154  			clients = append(clients, c)
   155  		case f, ok := <-d.in:
   156  			if !ok {
   157  				debugf("demux closing (%d clients)...", len(clients))
   158  				for _, c := range clients {
   159  					close(c.resp)
   160  				}
   161  				clients = nil
   162  				return
   163  			}
   164  			// Match against active clients
   165  			for i := 0; i < len(clients); i++ {
   166  				c := clients[i]
   167  				if !c.Want(f) {
   168  					continue
   169  				}
   170  				select {
   171  				case c.resp <- f:
   172  					if !c.once {
   173  						continue
   174  					}
   175  				case <-c.done:
   176  				}
   177  				close(c.resp)
   178  				clients = append(clients[:i], clients[i+1:]...)
   179  				i--
   180  			}
   181  		}
   182  	}
   183  }