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 }