Source file src/syscall/forkpipe2.go
1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris 6 7 package syscall 8 9 import "sync" 10 11 // forkExecPipe atomically opens a pipe with O_CLOEXEC set on both file 12 // descriptors. 13 func forkExecPipe(p []int) error { 14 return Pipe2(p, O_CLOEXEC) 15 } 16 17 var ( 18 // Guard the forking variable. 19 forkingLock sync.Mutex 20 // Number of goroutines currently forking, and thus the 21 // number of goroutines holding a conceptual write lock 22 // on ForkLock. 23 forking int 24 ) 25 26 // hasWaitingReaders reports whether any goroutine is waiting 27 // to acquire a read lock on rw. It is defined in the sync package. 28 func hasWaitingReaders(rw *sync.RWMutex) bool 29 30 // acquireForkLock acquires a write lock on ForkLock. 31 // ForkLock is exported and we've promised that during a fork 32 // we will call ForkLock.Lock, so that no other threads create 33 // new fds that are not yet close-on-exec before we fork. 34 // But that forces all fork calls to be serialized, which is bad. 35 // But we haven't promised that serialization, and it is essentially 36 // undetectable by other users of ForkLock, which is good. 37 // Avoid the serialization by ensuring that ForkLock is locked 38 // at the first fork and unlocked when there are no more forks. 39 func acquireForkLock() { 40 forkingLock.Lock() 41 defer forkingLock.Unlock() 42 43 if forking == 0 { 44 // There is no current write lock on ForkLock. 45 ForkLock.Lock() 46 forking++ 47 return 48 } 49 50 // ForkLock is currently locked for writing. 51 52 if hasWaitingReaders(&ForkLock) { 53 // ForkLock is locked for writing, and at least one 54 // goroutine is waiting to read from it. 55 // To avoid lock starvation, allow readers to proceed. 56 // The simple way to do this is for us to acquire a 57 // read lock. That will block us until all current 58 // conceptual write locks are released. 59 // 60 // Note that this case is unusual on modern systems 61 // with O_CLOEXEC and SOCK_CLOEXEC. On those systems 62 // the standard library should never take a read 63 // lock on ForkLock. 64 65 forkingLock.Unlock() 66 67 ForkLock.RLock() 68 ForkLock.RUnlock() 69 70 forkingLock.Lock() 71 72 // Readers got a chance, so now take the write lock. 73 74 if forking == 0 { 75 ForkLock.Lock() 76 } 77 } 78 79 forking++ 80 } 81 82 // releaseForkLock releases the conceptual write lock on ForkLock 83 // acquired by acquireForkLock. 84 func releaseForkLock() { 85 forkingLock.Lock() 86 defer forkingLock.Unlock() 87 88 if forking <= 0 { 89 panic("syscall.releaseForkLock: negative count") 90 } 91 92 forking-- 93 94 if forking == 0 { 95 // No more conceptual write locks. 96 ForkLock.Unlock() 97 } 98 } 99