domain.mx raw
1 //go:build linux || darwin
2
3 package runtime
4
5 import "unsafe"
6
7 // Domain registry — tracks child domains spawned by this process.
8 // Single-threaded cooperative scheduler, so no lock needed.
9
10 type domainEntry struct {
11 pid int32
12 parentFd int32 // parent's end of socketpair
13 alive bool
14 next *domainEntry
15 }
16
17 var domainList *domainEntry
18
19 func domainRegister(pid, fd int32) {
20 domainList = &domainEntry{pid: pid, parentFd: fd, alive: true, next: domainList}
21 }
22
23 func domainCount() int {
24 n := 0
25 for e := domainList; e != nil; e = e.next {
26 if e.alive {
27 n++
28 }
29 }
30 return n
31 }
32
33 func domainLookupByPid(pid int32) *domainEntry {
34 for e := domainList; e != nil; e = e.next {
35 if e.pid == pid {
36 return e
37 }
38 }
39 return nil
40 }
41
42 func domainLookupByFd(fd int32) *domainEntry {
43 for e := domainList; e != nil; e = e.next {
44 if e.parentFd == fd {
45 return e
46 }
47 }
48 return nil
49 }
50
51 // domainReapChildren checks for exited children and marks them dead.
52 // Non-blocking (WNOHANG). Call periodically from the scheduler.
53 func domainReapChildren() {
54 for e := domainList; e != nil; e = e.next {
55 if !e.alive {
56 continue
57 }
58 var status int32
59 r := moxie_waitpid(e.pid, unsafe.Pointer(&status), 1) // WNOHANG=1
60 if r > 0 {
61 e.alive = false
62 moxie_close(e.parentFd)
63 }
64 }
65 }
66
67 // domainShutdownAll sends SIGTERM to all live children, then waits
68 // with a timeout. Stragglers get SIGKILL.
69 func domainShutdownAll() {
70 // Signal all children.
71 for e := domainList; e != nil; e = e.next {
72 if e.alive {
73 moxie_kill(e.pid, 15) // SIGTERM
74 }
75 }
76
77 // Wait briefly for exits.
78 for i := 0; i < 100; i++ {
79 allDead := true
80 for e := domainList; e != nil; e = e.next {
81 if !e.alive {
82 continue
83 }
84 var status int32
85 r := moxie_waitpid(e.pid, unsafe.Pointer(&status), 1)
86 if r > 0 {
87 e.alive = false
88 moxie_close(e.parentFd)
89 } else {
90 allDead = false
91 }
92 }
93 if allDead {
94 return
95 }
96 sleepTicks(timeUnit(10_000_000)) // 10ms
97 }
98
99 // SIGKILL stragglers.
100 for e := domainList; e != nil; e = e.next {
101 if e.alive {
102 moxie_kill(e.pid, 9) // SIGKILL
103 var status int32
104 moxie_waitpid(e.pid, unsafe.Pointer(&status), 0)
105 e.alive = false
106 moxie_close(e.parentFd)
107 }
108 }
109 }
110
111 // C functions defined in spawn_unix.c.
112
113 //export moxie_waitpid
114 func moxie_waitpid(pid int32, status unsafe.Pointer, options int32) int32
115
116 //export moxie_kill
117 func moxie_kill(pid int32, sig int32) int32
118