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