github.com/influx6/npkg@v0.8.8/natomic/signal.go (about) 1 package natomic 2 3 import ( 4 "sync" 5 "sync/atomic" 6 "time" 7 8 "github.com/influx6/npkg/nerror" 9 ) 10 11 //************************************************************************ 12 // Signals 13 //************************************************************************ 14 15 // Signal defines a underline event representing some type and signal. 16 // 17 // The Type() return defines the unique means of identification 18 // for this signal. 19 type Signal interface { 20 Type() string 21 Target() string 22 Source() string 23 } 24 25 // SignalResponder defines an interface for sending signals 26 // to it's implementer. 27 type SignalResponder interface { 28 Respond(Signal) 29 } 30 31 // SignalRespondHandler defines a function type. 32 type SignalRespondHandler func(Signal) 33 34 // Respond implements the SignalResponder interface. 35 func (sr SignalRespondHandler) Respond(s Signal) { 36 sr(s) 37 } 38 39 // SignalResponderNotification defines an interface defining 40 // methods on a SignalResponder to deliver certain information 41 // regarding it's addition, removal from a ResponderGroup. 42 type SignalResponderNotification interface { 43 SignalResponder 44 45 OnAdded() 46 OnRemoved() 47 } 48 49 // SignalReceiver defines a host of signal delivery and 50 // de-registration. 51 type SignalReceiver interface { 52 SignalResponder 53 54 // Add adds new SignalResponder to the underline signal receiver. 55 // All signal provider to this SignalReceiver will be sent to the 56 // SignalResponder as well. 57 Add(SignalResponder) error 58 59 // Remove must remove the responder from the underline 60 // signal receiver. 61 Remove(SignalResponder) error 62 } 63 64 //***************************************************** 65 // Atom 66 //***************************************************** 67 68 // Atom implements the Atom interface, implementing 69 // the safe concurrent storing and reading of stored values 70 // without the use of mutex and relying on the atomic.Value 71 // construct, which is great for low-write and high-read usage. 72 // 73 // Atoms are concurrently safe to use both for getting/setting 74 // value and adding interceptors. 75 type Atom struct { 76 signals SignalReceiver 77 imx sync.Mutex 78 store *atomic.Value 79 } 80 81 // NewAtom returns a new instance of *Atom. 82 func NewAtom(receiver SignalReceiver) *Atom { 83 var newStore atomic.Value 84 return &Atom{ 85 signals: receiver, 86 store: &newStore, 87 } 88 } 89 90 // Set attempts to set giving value into atom, if giving value is 91 // not the same underline type as previous set calls, then an error is 92 // returned. 93 func (am *Atom) Set(val interface{}) error { 94 am.imx.Lock() 95 defer am.imx.Unlock() 96 am.store.Store(val) 97 98 if am.signals != nil { 99 am.signals.Respond(am) 100 } 101 return nil 102 } 103 104 // Source returns an empty string, has we have no source. 105 func (am *Atom) Source() string { 106 return "" 107 } 108 109 // Target returns an empty string, has we have no target. 110 func (am *Atom) Target() string { 111 return "" 112 } 113 114 // Type returns a giving string to represent the behaviour of 115 // giving type. 116 func (am *Atom) Type() string { 117 return "natomic.Atom" 118 } 119 120 // Read returns the giving value stored within giving atom. 121 // It returns nil if no value was ever set. 122 func (am *Atom) Read() interface{} { 123 return am.store.Load() 124 } 125 126 // Add adds a new SignalResponder into the atom responder 127 // provider. 128 func (am *Atom) Add(n SignalResponder) error { 129 if am.signals != nil { 130 return am.signals.Add(n) 131 } 132 return nil 133 } 134 135 // Remove removes giving SignalResponder from the atom responder 136 // provider. 137 func (am *Atom) Remove(n SignalResponder) error { 138 if am.signals != nil { 139 return am.signals.Remove(n) 140 } 141 return nil 142 } 143 144 // Respond delivers giving Signal to all interceptors 145 // registered with giving atom. 146 func (am *Atom) Respond(s Signal) { 147 if am.signals != nil { 148 am.signals.Respond(s) 149 } 150 } 151 152 //************************************************************************ 153 // ResponderGroup 154 //************************************************************************ 155 156 // ResponderGroup defines a grouping and manager for SignalResponders, which manages 157 // the addition, and removals, basically the management of the lifecycle of a set of 158 // SignalResponders. 159 type ResponderGroup struct { 160 sm sync.Mutex 161 spawned bool 162 waiter sync.WaitGroup 163 closer chan struct{} 164 actions chan func() 165 responders map[SignalResponder]struct{} 166 } 167 168 // NewResponderGroup returns a new ResponderGroup instance. 169 func NewResponderGroup() *ResponderGroup { 170 var rg ResponderGroup 171 rg.actions = make(chan func()) 172 rg.closer = make(chan struct{}) 173 rg.responders = map[SignalResponder]struct{}{} 174 return &rg 175 } 176 177 // Remove returns an error if it fails to deliver SignalResponder into 178 // responder group. 179 func (rg *ResponderGroup) Remove(r SignalResponder) error { 180 return rg.RemoveGuaranteed(r, nil) 181 } 182 183 // RemoveGuaranteed returns an error if it fails to deliver SignalResponder into 184 // responder group. 185 func (rg *ResponderGroup) RemoveGuaranteed(r SignalResponder, signal chan struct{}) error { 186 var action = func() { 187 if _, found := rg.responders[r]; found { 188 delete(rg.responders, r) 189 if irs, ok := r.(SignalResponderNotification); ok { 190 irs.OnRemoved() 191 } 192 } 193 194 if signal != nil { 195 signal <- struct{}{} 196 } 197 } 198 199 select { 200 case rg.actions <- action: 201 return nil 202 case <-time.After(time.Second): 203 return nerror.New("failed to remove responder") 204 } 205 } 206 207 // Add returns an error if it fails to deliver SignalResponder into 208 // responder group. 209 func (rg *ResponderGroup) Add(r SignalResponder) error { 210 return rg.AddGuaranteed(r, nil) 211 } 212 213 // AddGuaranteed returns an error if it fails to deliver SignalResponder into 214 // responder group. 215 func (rg *ResponderGroup) AddGuaranteed(r SignalResponder, signal chan struct{}) error { 216 var action = func() { 217 rg.responders[r] = struct{}{} 218 if irs, ok := r.(SignalResponderNotification); ok { 219 irs.OnAdded() 220 } 221 222 if signal != nil { 223 signal <- struct{}{} 224 } 225 } 226 227 select { 228 case rg.actions <- action: 229 return nil 230 case <-time.After(time.Second): 231 return nerror.New("failed to add responder") 232 } 233 } 234 235 // Respond notifies giving signal to all responders. 236 func (rg *ResponderGroup) Respond(s Signal) { 237 rg.RespondGuaranteed(s, nil) 238 } 239 240 // RespondGuaranteed returns an error if it fails to deliver SignalResponder into 241 // responder group. 242 func (rg *ResponderGroup) RespondGuaranteed(s Signal, signal chan struct{}) { 243 var action = func() { 244 for responder := range rg.responders { 245 responder.Respond(s) 246 } 247 if signal != nil { 248 signal <- struct{}{} 249 } 250 } 251 252 rg.actions <- action 253 } 254 255 // Close closes giving responder group, removing all 256 // it's subscription. 257 func (rg *ResponderGroup) Close() { 258 rg.closer <- struct{}{} 259 rg.waiter.Wait() 260 } 261 262 // Start runs giving responder group. 263 // 264 // Start accepts possible many channels but takes the first one 265 // and will deliver a signal to it to provide detail that it 266 // as started. 267 func (rg *ResponderGroup) Start(started ...chan struct{}) { 268 if rg.running() { 269 return 270 } 271 272 if rg.actions == nil { 273 rg.actions = make(chan func()) 274 } 275 if rg.closer == nil { 276 rg.closer = make(chan struct{}) 277 } 278 if rg.responders == nil { 279 rg.responders = map[SignalResponder]struct{}{} 280 } 281 282 if len(started) > 0 { 283 rg.manage(started[0]) 284 return 285 } 286 287 rg.manage(nil) 288 } 289 290 // Wait blocks till giving ResponderGroup is closed. 291 func (rg *ResponderGroup) Wait() { 292 rg.waiter.Wait() 293 } 294 295 func (rg *ResponderGroup) flip(t bool) { 296 rg.sm.Lock() 297 rg.spawned = t 298 rg.sm.Unlock() 299 } 300 301 func (rg *ResponderGroup) running() bool { 302 var running bool 303 rg.sm.Lock() 304 running = rg.spawned 305 rg.sm.Unlock() 306 return running 307 } 308 309 // manage setups the necessary goroutine for managing the underline 310 // process for giving responder. 311 // 312 // If a channel is passed then the giving channel receives a 313 // signal that giving goroutine as started. 314 func (rg *ResponderGroup) manage(started chan struct{}) { 315 rg.flip(true) 316 rg.waiter.Add(1) 317 318 go func() { 319 defer rg.waiter.Done() 320 defer rg.flip(false) 321 322 if started != nil { 323 started <- struct{}{} 324 } 325 326 for { 327 select { 328 case <-rg.closer: 329 rg.responders = map[SignalResponder]struct{}{} 330 return 331 case action := <-rg.actions: 332 action() 333 } 334 } 335 }() 336 }