2019-07-25 16:02:47 +02:00
|
|
|
package v2
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
/*
|
2019-07-29 08:45:59 +02:00
|
|
|
TODO:
|
|
|
|
* Look at refactoring routines
|
|
|
|
* single struct, paramterize the handle function
|
|
|
|
* introduce an sendError and seperate error channel
|
|
|
|
* How could this be tested?
|
|
|
|
* Ensure Start/Stopping
|
|
|
|
* Ensure all messages sent are processed
|
|
|
|
* Ensure that errors can be processed depsite outstanding messages
|
2019-07-25 16:02:47 +02:00
|
|
|
*/
|
|
|
|
type testEvent struct {
|
|
|
|
msg string
|
|
|
|
time time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
type testEventTwo struct {
|
|
|
|
msg string
|
|
|
|
}
|
|
|
|
|
|
|
|
type stopEvent struct{}
|
2019-07-27 00:25:32 +02:00
|
|
|
type timeCheck struct {
|
|
|
|
time time.Time
|
|
|
|
}
|
2019-07-25 16:02:47 +02:00
|
|
|
|
2019-07-29 11:58:13 +02:00
|
|
|
// XXX: what about state here, we need per routine state
|
|
|
|
// So handle func should take an event and a reference to state and return
|
|
|
|
// events and the new state
|
2019-07-29 08:45:59 +02:00
|
|
|
type handleFunc = func(event Event) Events
|
2019-07-27 10:58:58 +02:00
|
|
|
|
2019-07-29 08:45:59 +02:00
|
|
|
// Routine
|
|
|
|
type Routine struct {
|
|
|
|
name string
|
2019-07-27 11:38:37 +02:00
|
|
|
input chan Event
|
2019-07-29 12:40:59 +02:00
|
|
|
errors chan errEvent
|
2019-07-27 11:38:37 +02:00
|
|
|
output chan Event
|
|
|
|
stopped chan struct{}
|
2019-07-29 08:45:59 +02:00
|
|
|
handle handleFunc
|
2019-07-25 16:02:47 +02:00
|
|
|
}
|
|
|
|
|
2019-07-29 08:45:59 +02:00
|
|
|
func newRoutine(name string, output chan Event, handleFunc handleFunc) *Routine {
|
|
|
|
return &Routine{
|
|
|
|
name: name,
|
2019-07-28 09:07:35 +02:00
|
|
|
input: make(chan Event, 1),
|
2019-07-29 12:40:59 +02:00
|
|
|
errors: make(chan errEvent, 1),
|
2019-07-28 09:07:35 +02:00
|
|
|
output: output,
|
|
|
|
stopped: make(chan struct{}, 1),
|
2019-07-29 08:45:59 +02:00
|
|
|
handle: handleFunc,
|
2019-07-27 10:58:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-29 12:40:59 +02:00
|
|
|
type errEvent struct{}
|
2019-07-29 11:58:13 +02:00
|
|
|
|
2019-07-29 12:40:59 +02:00
|
|
|
// run thriough input an errors giving errors a chance to skip the queue
|
2019-07-29 08:45:59 +02:00
|
|
|
func (rt *Routine) run() {
|
|
|
|
fmt.Printf("%s: run\n", rt.name)
|
2019-07-25 16:02:47 +02:00
|
|
|
for {
|
2019-07-29 12:40:59 +02:00
|
|
|
select {
|
|
|
|
case iEvent, ok := <-rt.input:
|
|
|
|
if !ok {
|
|
|
|
fmt.Printf("%s: stopping\n", rt.name)
|
|
|
|
rt.stopped <- struct{}{}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
oEvents := rt.handle(iEvent)
|
|
|
|
fmt.Printf("%s handled %d events\n", rt.name, len(oEvents))
|
|
|
|
for _, event := range oEvents {
|
|
|
|
rt.output <- event
|
|
|
|
}
|
|
|
|
case iEvent, ok := <-rt.errors:
|
|
|
|
if !ok {
|
|
|
|
fmt.Printf("%s: errors closed\n", rt.name)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
oEvents := rt.handle(iEvent)
|
|
|
|
fmt.Printf("%s handled %d events from errors\n", rt.name, len(oEvents))
|
|
|
|
for _, event := range oEvents {
|
|
|
|
rt.output <- event
|
|
|
|
}
|
2019-07-27 00:25:32 +02:00
|
|
|
}
|
2019-07-25 16:02:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-29 08:45:59 +02:00
|
|
|
func (rt *Routine) send(event Event) bool {
|
|
|
|
fmt.Printf("%s: send\n", rt.name)
|
2019-07-29 12:40:59 +02:00
|
|
|
if err, ok := event.(errEvent); ok {
|
|
|
|
select {
|
|
|
|
case rt.errors <- err:
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
fmt.Printf("%s: errors channel was full\n", rt.name)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
select {
|
|
|
|
case rt.input <- event:
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
fmt.Printf("%s: channel was full\n", rt.name)
|
|
|
|
return false
|
|
|
|
}
|
2019-07-27 00:25:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-29 08:45:59 +02:00
|
|
|
func (rt *Routine) stop() {
|
|
|
|
fmt.Printf("%s: stop\n", rt.name)
|
2019-07-29 12:40:59 +02:00
|
|
|
close(rt.errors)
|
2019-07-29 08:45:59 +02:00
|
|
|
close(rt.input)
|
|
|
|
<-rt.stopped
|
|
|
|
}
|
|
|
|
|
2019-07-29 11:58:13 +02:00
|
|
|
type scTestEvent struct{}
|
|
|
|
|
2019-07-29 08:45:59 +02:00
|
|
|
func schedulerHandle(event Event) Events {
|
2019-07-27 10:58:58 +02:00
|
|
|
switch event.(type) {
|
2019-07-27 00:25:32 +02:00
|
|
|
case timeCheck:
|
|
|
|
fmt.Println("scheduler handle timeCheck")
|
|
|
|
case testEvent:
|
|
|
|
fmt.Println("scheduler handle testEvent")
|
2019-07-29 11:58:13 +02:00
|
|
|
return Events{scTestEvent{}}
|
2019-07-27 00:25:32 +02:00
|
|
|
}
|
|
|
|
return Events{}
|
|
|
|
}
|
|
|
|
|
2019-07-29 11:58:13 +02:00
|
|
|
type pcFinished struct{}
|
|
|
|
|
2019-07-29 08:45:59 +02:00
|
|
|
func processorHandle(event Event) Events {
|
2019-07-27 10:58:58 +02:00
|
|
|
switch event.(type) {
|
2019-07-27 00:25:32 +02:00
|
|
|
case timeCheck:
|
2019-07-27 10:58:58 +02:00
|
|
|
fmt.Println("processor handle timeCheck")
|
2019-07-27 00:25:32 +02:00
|
|
|
case testEvent:
|
|
|
|
fmt.Println("processor handle testEvent")
|
2019-07-29 11:58:13 +02:00
|
|
|
case scTestEvent:
|
|
|
|
fmt.Println("processor handle scTestEvent")
|
|
|
|
// should i stop myself?
|
|
|
|
return Events{pcFinished{}}
|
2019-07-27 00:25:32 +02:00
|
|
|
}
|
|
|
|
return Events{}
|
|
|
|
}
|
|
|
|
|
2019-07-29 11:58:13 +02:00
|
|
|
type demuxer struct {
|
|
|
|
eventbus chan Event
|
|
|
|
scheduler *Routine
|
|
|
|
processor *Routine
|
|
|
|
finished chan struct{}
|
|
|
|
stopped chan struct{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func newDemuxer(scheduler *Routine, processor *Routine) *demuxer {
|
|
|
|
return &demuxer{
|
|
|
|
eventbus: make(chan Event, 10),
|
|
|
|
scheduler: scheduler,
|
|
|
|
processor: processor,
|
|
|
|
stopped: make(chan struct{}, 1),
|
|
|
|
finished: make(chan struct{}, 1),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (dm *demuxer) run() {
|
|
|
|
fmt.Printf("demuxer: run\n")
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case event, ok := <-dm.eventbus:
|
|
|
|
if !ok {
|
|
|
|
fmt.Printf("demuxer: stopping\n")
|
|
|
|
dm.stopped <- struct{}{}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
oEvents := dm.handle(event)
|
|
|
|
for _, event := range oEvents {
|
|
|
|
dm.eventbus <- event
|
|
|
|
}
|
|
|
|
case event, ok := <-dm.scheduler.output:
|
|
|
|
if !ok {
|
|
|
|
fmt.Printf("demuxer: scheduler output closed\n")
|
|
|
|
continue
|
|
|
|
// todo: close?
|
|
|
|
}
|
|
|
|
oEvents := dm.handle(event)
|
|
|
|
for _, event := range oEvents {
|
|
|
|
dm.eventbus <- event
|
|
|
|
}
|
|
|
|
case event, ok := <-dm.processor.output:
|
|
|
|
if !ok {
|
|
|
|
fmt.Printf("demuxer: pricessor output closed\n")
|
|
|
|
continue
|
|
|
|
// todo: close?
|
|
|
|
}
|
|
|
|
oEvents := dm.handle(event)
|
|
|
|
for _, event := range oEvents {
|
|
|
|
dm.eventbus <- event
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type scFull struct{}
|
|
|
|
type pcFull struct{}
|
|
|
|
|
|
|
|
// XXX: What is the corerct behaviour here?
|
|
|
|
// onPcFinish, process no further events
|
|
|
|
// OR onPcFinish, process all queued events and then close
|
|
|
|
func (dm *demuxer) handle(event Event) Events {
|
|
|
|
switch event.(type) {
|
|
|
|
case pcFinished:
|
|
|
|
// dm.stop()
|
|
|
|
fmt.Println("demuxer received pcFinished")
|
|
|
|
dm.finished <- struct{}{}
|
|
|
|
default:
|
|
|
|
received := dm.scheduler.send(event)
|
2019-07-27 10:58:58 +02:00
|
|
|
if !received {
|
2019-07-29 11:58:13 +02:00
|
|
|
return Events{scFull{}} // backpressure
|
2019-07-27 10:58:58 +02:00
|
|
|
}
|
|
|
|
|
2019-07-29 11:58:13 +02:00
|
|
|
received = dm.processor.send(event)
|
2019-07-27 10:58:58 +02:00
|
|
|
if !received {
|
2019-07-29 11:58:13 +02:00
|
|
|
return Events{pcFull{}} // backpressure
|
2019-07-27 10:58:58 +02:00
|
|
|
}
|
2019-07-25 16:02:47 +02:00
|
|
|
|
2019-07-29 08:45:59 +02:00
|
|
|
return Events{}
|
2019-07-27 10:58:58 +02:00
|
|
|
}
|
2019-07-29 11:58:13 +02:00
|
|
|
return Events{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (dm *demuxer) send(event Event) bool {
|
|
|
|
fmt.Printf("demuxer send\n")
|
|
|
|
select {
|
|
|
|
case dm.eventbus <- event:
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
fmt.Printf("demuxer channel was full\n")
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (dm *demuxer) stop() {
|
|
|
|
fmt.Printf("demuxer stop\n")
|
|
|
|
close(dm.eventbus)
|
|
|
|
<-dm.stopped
|
2019-07-27 10:58:58 +02:00
|
|
|
}
|
|
|
|
|
2019-07-27 00:25:32 +02:00
|
|
|
// reactor
|
2019-07-25 16:02:47 +02:00
|
|
|
type DummyReactor struct {
|
2019-07-27 11:38:37 +02:00
|
|
|
events chan Event
|
2019-07-29 11:58:13 +02:00
|
|
|
demuxer *demuxer
|
2019-07-29 08:45:59 +02:00
|
|
|
scheduler *Routine
|
|
|
|
processor *Routine
|
2019-07-27 11:38:37 +02:00
|
|
|
ticker *time.Ticker
|
|
|
|
tickerStopped chan struct{}
|
2019-07-29 11:58:13 +02:00
|
|
|
completed chan struct{}
|
2019-07-25 16:02:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (dr *DummyReactor) Start() {
|
2019-07-27 00:25:32 +02:00
|
|
|
bufferSize := 10
|
|
|
|
events := make(chan Event, bufferSize)
|
|
|
|
|
2019-07-29 11:58:13 +02:00
|
|
|
dr.completed = make(chan struct{}, 1)
|
|
|
|
|
2019-07-29 08:45:59 +02:00
|
|
|
dr.scheduler = newRoutine("scheduler", events, schedulerHandle)
|
|
|
|
dr.processor = newRoutine("processor", events, processorHandle)
|
2019-07-29 11:58:13 +02:00
|
|
|
dr.demuxer = newDemuxer(dr.scheduler, dr.processor)
|
2019-07-27 11:38:37 +02:00
|
|
|
dr.tickerStopped = make(chan struct{})
|
2019-07-27 00:25:32 +02:00
|
|
|
|
2019-07-27 11:38:37 +02:00
|
|
|
go dr.scheduler.run()
|
|
|
|
go dr.processor.run()
|
2019-07-27 00:25:32 +02:00
|
|
|
go dr.demuxer.run()
|
2019-07-27 11:38:37 +02:00
|
|
|
|
2019-07-27 10:58:58 +02:00
|
|
|
go func() {
|
2019-07-27 11:38:37 +02:00
|
|
|
ticker := time.NewTicker(1 * time.Second)
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ticker.C:
|
|
|
|
dr.demuxer.send(timeCheck{})
|
|
|
|
case <-dr.tickerStopped:
|
|
|
|
fmt.Println("ticker stopped")
|
|
|
|
return
|
|
|
|
}
|
2019-07-27 10:58:58 +02:00
|
|
|
}
|
|
|
|
}()
|
2019-07-29 11:58:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (dr *DummyReactor) Wait() {
|
|
|
|
<-dr.demuxer.finished // maybe put this in a wait method
|
|
|
|
fmt.Println("completed routines")
|
|
|
|
dr.Stop()
|
2019-07-25 16:02:47 +02:00
|
|
|
}
|
|
|
|
|
2019-07-27 00:25:32 +02:00
|
|
|
func (dr *DummyReactor) Stop() {
|
2019-07-27 11:38:37 +02:00
|
|
|
fmt.Println("reactor stopping")
|
2019-07-29 11:58:13 +02:00
|
|
|
|
2019-07-27 11:38:37 +02:00
|
|
|
dr.tickerStopped <- struct{}{}
|
2019-07-28 09:07:35 +02:00
|
|
|
dr.demuxer.stop()
|
2019-07-27 11:38:37 +02:00
|
|
|
dr.scheduler.stop()
|
|
|
|
dr.processor.stop()
|
|
|
|
|
|
|
|
fmt.Println("reactor stopped")
|
2019-07-27 00:25:32 +02:00
|
|
|
}
|
2019-07-25 16:02:47 +02:00
|
|
|
|
2019-07-27 00:25:32 +02:00
|
|
|
func (dr *DummyReactor) Receive(event Event) {
|
2019-07-27 10:58:58 +02:00
|
|
|
fmt.Println("receive event")
|
2019-07-27 00:25:32 +02:00
|
|
|
sent := dr.demuxer.send(event)
|
|
|
|
if !sent {
|
|
|
|
panic("demuxer is full")
|
|
|
|
}
|
|
|
|
}
|
2019-07-25 16:02:47 +02:00
|
|
|
|
2019-07-27 00:25:32 +02:00
|
|
|
func (dr *DummyReactor) AddPeer() {
|
|
|
|
// TODO: add peer event and send to demuxer
|
|
|
|
}
|