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 7package syscall 8 9import "sync" 10 11// forkExecPipe atomically opens a pipe with O_CLOEXEC set on both file 12// descriptors. 13func forkExecPipe(p []int) error { 14 return Pipe2(p, O_CLOEXEC) 15} 16 17var ( 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. 28func 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. 39func 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. 84func 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