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