-
Notifications
You must be signed in to change notification settings - Fork 6
/
link.go
128 lines (108 loc) · 2.49 KB
/
link.go
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package goprocess
import (
"sync"
)
// closedCh is an alread-closed channel. used to return
// in cases where we already know a channel is closed.
var closedCh chan struct{}
func init() {
closedCh = make(chan struct{})
close(closedCh)
}
// a processLink is an internal bookkeeping datastructure.
// it's used to form a relationship between two processes.
// It is mostly for keeping memory usage down (letting
// children close and be garbage-collected).
type processLink struct {
// guards all fields.
// DO NOT HOLD while holding process locks.
// it may be slow, and could deadlock if not careful.
sync.Mutex
parent Process
child Process
}
func newProcessLink(p, c Process) *processLink {
return &processLink{
parent: p,
child: c,
}
}
// Closing returns whether the child is closing
func (pl *processLink) ChildClosing() <-chan struct{} {
// grab a hold of it, and unlock, as .Closing may block.
pl.Lock()
child := pl.child
pl.Unlock()
if child == nil { // already closed? memory optimization.
return closedCh
}
return child.Closing()
}
func (pl *processLink) ChildClosed() <-chan struct{} {
// grab a hold of it, and unlock, as .Closed may block.
pl.Lock()
child := pl.child
pl.Unlock()
if child == nil { // already closed? memory optimization.
return closedCh
}
return child.Closed()
}
func (pl *processLink) ChildClose() {
// grab a hold of it, and unlock, as .Closed may block.
pl.Lock()
child := pl.child
pl.Unlock()
if child != nil { // already closed? memory optimization.
child.Close()
}
}
func (pl *processLink) ClearChild() {
pl.Lock()
pl.child = nil
pl.Unlock()
}
func (pl *processLink) ParentClear() {
pl.Lock()
pl.parent = nil
pl.Unlock()
}
func (pl *processLink) Child() Process {
pl.Lock()
defer pl.Unlock()
return pl.child
}
func (pl *processLink) Parent() Process {
pl.Lock()
defer pl.Unlock()
return pl.parent
}
func (pl *processLink) AddToChild() {
cp := pl.Child()
// is it a *process ? if not... panic.
var c *process
switch cp := cp.(type) {
case *process:
c = cp
case *bgProcess:
// Background process never closes so we don't need to do
// anything.
return
default:
panic("goprocess does not yet support other process impls.")
}
// first, is it Closed?
c.Lock()
select {
case <-c.Closed():
c.Unlock()
// already closed. must not add.
// we must clear it, though. do so without the lock.
pl.ClearChild()
return
default:
// put the process link into q's waiters
c.waiters = append(c.waiters, pl)
c.Unlock()
}
}