blob: 92e572a0569bfea40afa2dc8151040b214a5a5e0 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package discovery
import "reflect"
// A Trigger is a simple multi-channel receiver. It triggers callbacks
// when it receives signals from the corresponding channels.
// Each callback will run only once and run in a separate goroutine.
type Trigger struct {
addCh chan *addRequest
}
type addRequest struct {
callback func()
sc reflect.SelectCase
}
// Add enqueues callback to be invoked on the next event on ch.
func (tr *Trigger) Add(callback func(), ch <-chan struct{}) {
sc := reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ch)}
tr.addCh <- &addRequest{callback, sc}
}
// Stop stops the trigger.
func (tr *Trigger) Stop() {
close(tr.addCh)
}
func (tr *Trigger) loop() {
callbacks := make([]func(), 1, 4)
cases := make([]reflect.SelectCase, 1, 4)
cases[0] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(tr.addCh)}
for {
chosen, recv, ok := reflect.Select(cases)
switch chosen {
case 0:
if !ok {
// The trigger has been stopped.
return
}
// Add a new callback.
req := recv.Interface().(*addRequest)
callbacks = append(callbacks, req.callback)
cases = append(cases, req.sc)
default:
go callbacks[chosen]()
n := len(callbacks) - 1
callbacks[chosen], callbacks[n] = callbacks[n], nil
callbacks = callbacks[:n]
cases[chosen], cases[n] = cases[n], reflect.SelectCase{}
cases = cases[:n]
}
}
}
func NewTrigger() *Trigger {
tr := &Trigger{addCh: make(chan *addRequest)}
go tr.loop()
return tr
}