aboutsummaryrefslogtreecommitdiffstats
path: root/kill.go
blob: f0f04d08f888a6b2becd35add6c07c7cebb8aff4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package gui

// A Killable object can be told to shut down by sending a signal via the Kill() channel.
// As its last action, the object posts a signal to Dead() and closes both channels, indicating that it has finished shutting down.
type Killable interface {
	Kill() chan<- bool
	Dead() <-chan bool
}

// A killer can kill the victim that is attached to it.
// The victim can attach itself to the killer by sending itself via the killer's attach() channel.
// The victim can detach itself by sending a signal via its own detach() channel.
//
// Only one victim can be attached to the killer at a time.
// Further messages sent on the attach() channel will block until the current victim is detached.
//
// If the killer is killed while a victim is attached, it kills the victim.
// When killed, the victim must detach itself before dying.
type killer interface {
	attach() chan<- victim

	Killable
}

type victim interface {
	// Sending to detach() will detach the victim from the killer it is attached to.
	detach() <-chan bool

	Killable
}

type _killer struct {
	attachChan chan<- victim
	kill       chan<- bool
	dead       <-chan bool
}

func newKiller() killer {
	attach := make(chan victim)
	kill := make(chan bool)
	dead := make(chan bool)

	go func() {
		defer func() {
			dead <- true
			close(dead)
		}()
		defer close(kill)
		defer close(attach)

		for {
			select {
			case victim := <-attach:
				select {
				case <-victim.detach():
				case <-kill:
					victim.Kill() <- true
					<-victim.detach()
					<-victim.Dead()
					return
				}
			case <-kill:
				return
			}
		}
	}()

	return _killer{attach, kill, dead}
}

func (k _killer) attach() chan<- victim {
	return k.attachChan
}

func (k _killer) Kill() chan<- bool {
	return k.kill
}

func (k _killer) Dead() <-chan bool {
	return k.dead
}