Skip to content

Commit bc203cb

Browse files
Add Process.pthread_disable_cancelstate (#16446)
`fork` in a multi-threaded process is a very delicate matter. We must avoid cancelling the thread while performing a fork. Cleanup could cause problems while we're setting up the new process. This would not be necessary if we were using `vfork` semantics. That's generally the better choice for spawning a new process, as there is no need to copy the entire process memory. `vfork` is more efficient, and we should ideally use that. However, we're hoping to switch to `posix_spawn` eventually (#11337), which provides a safe and efficient implementation, and we don't need to deal with the details explicitly. This currently blocked on the need to reopen stdio in the housekeeping stage (#16353) but we're expecting to resolve that soon.
1 parent dd629fe commit bc203cb

File tree

16 files changed

+103
-11
lines changed

16 files changed

+103
-11
lines changed

src/crystal/system/unix/process.cr

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,11 @@ struct Crystal::System::Process
209209
{% raise("Process fork is unsupported with multithreaded mode") if flag?(:preview_mt) %}
210210

211211
pid, errno = lock_write do
212-
block_signals do
213-
pid = LibC.fork
214-
{pid, Errno.value}
212+
pthread_disable_cancelstate do
213+
block_signals do
214+
pid = LibC.fork
215+
{pid, Errno.value}
216+
end
215217
end
216218
end
217219

@@ -252,6 +254,21 @@ struct Crystal::System::Process
252254
end
253255
end
254256

257+
private def self.pthread_disable_cancelstate(&)
258+
# No thread cancellation on android
259+
{% if flag?(:android) %}
260+
yield
261+
{% else %}
262+
LibC.pthread_setcancelstate(LibC::PTHREAD_CANCEL_DISABLE, out cancel_state)
263+
264+
begin
265+
yield
266+
ensure
267+
LibC.pthread_setcancelstate(cancel_state, nil)
268+
end
269+
{% end %}
270+
end
271+
255272
# Duplicates the current process.
256273
# Returns a `Process` representing the new child process in the current process
257274
# and `nil` inside the new child process.

src/crystal/system/unix/spawn.cr

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,19 @@ struct Crystal::System::Process
6060

6161
private def self.fork_for_exec
6262
pid, errno = lock_write do
63-
block_signals do |sigmask|
64-
pid = LibC.fork
65-
if pid == 0
66-
# forked process
63+
pthread_disable_cancelstate do
64+
block_signals do |sigmask|
65+
pid = LibC.fork
66+
if pid == 0
67+
# forked process
6768

68-
Crystal::System::Signal.after_fork_before_exec
69+
Crystal::System::Signal.after_fork_before_exec
6970

70-
# reset sigmask (inherited on exec)
71-
LibC.sigemptyset(sigmask)
71+
# reset sigmask (inherited on exec)
72+
LibC.sigemptyset(sigmask)
73+
end
74+
{pid, Errno.value}
7275
end
73-
{pid, Errno.value}
7476
end
7577
end
7678

src/lib_c/aarch64-darwin/c/pthread.cr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ require "./sys/types"
33
lib LibC
44
PTHREAD_MUTEX_ERRORCHECK = 1
55

6+
PTHREAD_CANCEL_ENABLE = 0
7+
PTHREAD_CANCEL_DISABLE = 1
8+
69
fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int
710
fun pthread_condattr_init(x0 : PthreadCondattrT*) : Int
811
fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int
@@ -25,5 +28,6 @@ lib LibC
2528
fun pthread_mutex_trylock(x0 : PthreadMutexT*) : Int
2629
fun pthread_mutex_unlock(x0 : PthreadMutexT*) : Int
2730
fun pthread_self : PthreadT
31+
fun pthread_setcancelstate(state : Int, oldstate : Int*) : Int
2832
fun pthread_setname_np(Char*) : Int
2933
end

src/lib_c/aarch64-linux-gnu/c/pthread.cr

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ require "./sys/types"
1111
lib LibC
1212
PTHREAD_MUTEX_ERRORCHECK = 2
1313

14+
PTHREAD_CANCEL_ENABLE = 0
15+
PTHREAD_CANCEL_DISABLE = 1
16+
1417
fun pthread_attr_destroy(attr : PthreadAttrT*) : Int
1518
fun pthread_attr_getstack(addr : PthreadAttrT*, stackaddr : Void**, stacksize : SizeT*) : Int
1619
fun pthread_condattr_destroy(attr : PthreadCondattrT*) : Int
@@ -37,4 +40,8 @@ lib LibC
3740
fun pthread_mutex_unlock(mutex : PthreadMutexT*) : Int
3841
fun pthread_self : PthreadT
3942
fun pthread_setname_np(PthreadT, Char*) : Int
43+
44+
# Set cancellability state of current thread to STATE, returning old
45+
# state in *OLDSTATE if OLDSTATE is not NULL.
46+
fun pthread_setcancelstate(__state : Int, __oldstate : Int*) : Int
4047
end

src/lib_c/aarch64-linux-musl/c/pthread.cr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ require "./sys/types"
33
lib LibC
44
PTHREAD_MUTEX_ERRORCHECK = 2
55

6+
PTHREAD_CANCEL_ENABLE = 0
7+
PTHREAD_CANCEL_DISABLE = 1
8+
69
fun pthread_attr_destroy(x0 : PthreadAttrT*) : Int
710
fun pthread_attr_getstack(x0 : PthreadAttrT*, x1 : Void**, x2 : SizeT*) : Int
811
fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int
@@ -27,5 +30,6 @@ lib LibC
2730
fun pthread_mutex_trylock(x0 : PthreadMutexT*) : Int
2831
fun pthread_mutex_unlock(x0 : PthreadMutexT*) : Int
2932
fun pthread_self : PthreadT
33+
fun pthread_setcancelstate(Int, Int*) : Int
3034
fun pthread_setname_np(PthreadT, Char*) : Int
3135
end

src/lib_c/arm-linux-gnueabihf/c/pthread.cr

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ require "./sys/types"
1111
lib LibC
1212
PTHREAD_MUTEX_ERRORCHECK = 2
1313

14+
PTHREAD_CANCEL_ENABLE = 0
15+
PTHREAD_CANCEL_DISABLE = 1
16+
1417
fun pthread_attr_destroy(attr : PthreadAttrT*) : Int
1518
fun pthread_attr_getstack(addr : PthreadAttrT*, stackaddr : Void**, stacksize : SizeT*) : Int
1619
fun pthread_condattr_destroy(attr : PthreadCondattrT*) : Int
@@ -37,4 +40,8 @@ lib LibC
3740
fun pthread_mutex_unlock(mutex : PthreadMutexT*) : Int
3841
fun pthread_self : PthreadT
3942
fun pthread_setname_np(PthreadT, Char*) : Int
43+
44+
# Set cancellability state of current thread to STATE, returning old
45+
# state in *OLDSTATE if OLDSTATE is not NULL.
46+
fun pthread_setcancelstate(__state : Int, __oldstate : Int*) : Int
4047
end

src/lib_c/i386-linux-gnu/c/pthread.cr

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ require "./sys/types"
1111
lib LibC
1212
PTHREAD_MUTEX_ERRORCHECK = 2
1313

14+
PTHREAD_CANCEL_ENABLE = 0
15+
PTHREAD_CANCEL_DISABLE = 1
16+
1417
fun pthread_attr_destroy(attr : PthreadAttrT*) : Int
1518
fun pthread_attr_getstack(addr : PthreadAttrT*, stackaddr : Void**, stacksize : SizeT*) : Int
1619
fun pthread_condattr_destroy(attr : PthreadCondattrT*) : Int
@@ -36,4 +39,8 @@ lib LibC
3639
fun pthread_mutex_unlock(mutex : PthreadMutexT*) : Int
3740
fun pthread_self : PthreadT
3841
fun pthread_setname_np(PthreadT, Char*) : Int
42+
43+
# Set cancellability state of current thread to STATE, returning old
44+
# state in *OLDSTATE if OLDSTATE is not NULL.
45+
fun pthread_setcancelstate(__state : Int, __oldstate : Int*) : Int
3946
end

src/lib_c/i386-linux-musl/c/pthread.cr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ require "./sys/types"
33
lib LibC
44
PTHREAD_MUTEX_ERRORCHECK = 2
55

6+
PTHREAD_CANCEL_ENABLE = 0
7+
PTHREAD_CANCEL_DISABLE = 1
8+
69
fun pthread_attr_destroy(x0 : PthreadAttrT*) : Int
710
fun pthread_attr_getstack(x0 : PthreadAttrT*, x1 : Void**, x2 : SizeT*) : Int
811
fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int
@@ -27,5 +30,6 @@ lib LibC
2730
fun pthread_mutex_trylock(x0 : PthreadMutexT*) : Int
2831
fun pthread_mutex_unlock(x0 : PthreadMutexT*) : Int
2932
fun pthread_self : PthreadT
33+
fun pthread_setcancelstate(Int, Int*) : Int
3034
fun pthread_setname_np(PthreadT, Char*) : Int
3135
end

src/lib_c/x86_64-darwin/c/pthread.cr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ require "./sys/types"
33
lib LibC
44
PTHREAD_MUTEX_ERRORCHECK = 1
55

6+
PTHREAD_CANCEL_ENABLE = 0
7+
PTHREAD_CANCEL_DISABLE = 1
8+
69
fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int
710
fun pthread_condattr_init(x0 : PthreadCondattrT*) : Int
811
fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int
@@ -25,5 +28,6 @@ lib LibC
2528
fun pthread_mutex_trylock(x0 : PthreadMutexT*) : Int
2629
fun pthread_mutex_unlock(x0 : PthreadMutexT*) : Int
2730
fun pthread_self : PthreadT
31+
fun pthread_setcancelstate(state : Int, oldstate : Int*) : Int
2832
fun pthread_setname_np(Char*) : Int
2933
end

src/lib_c/x86_64-dragonfly/c/pthread.cr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ require "./sys/types"
44
lib LibC
55
PTHREAD_MUTEX_ERRORCHECK = 1
66

7+
# Flags for cancelling threads
8+
PTHREAD_CANCEL_ENABLE = 0
9+
PTHREAD_CANCEL_DISABLE = 1
10+
711
fun pthread_attr_destroy(x0 : PthreadAttrT*) : Int
812
fun pthread_attr_get_np(x0 : PthreadT, x1 : PthreadAttrT*) : Int
913
fun pthread_attr_getstack(x0 : PthreadAttrT*, x1 : Void**, x2 : SizeT*) : Int
@@ -29,5 +33,6 @@ lib LibC
2933
fun pthread_mutex_trylock(x0 : PthreadMutexT*) : Int
3034
fun pthread_mutex_unlock(x0 : PthreadMutexT*) : Int
3135
fun pthread_self : PthreadT
36+
fun pthread_setcancelstate(Int, Int*) : Int
3237
fun pthread_setname_np(PthreadT, Char*) : Int
3338
end

0 commit comments

Comments
 (0)