ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting
@ 2023-03-02 12:04 Eregon (Benoit Daloze) via ruby-core
  2023-03-02 12:07 ` [ruby-core:112665] " Eregon (Benoit Daloze) via ruby-core
                   ` (25 more replies)
  0 siblings, 26 replies; 27+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2023-03-02 12:04 UTC (permalink / raw)
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #19473 has been reported by Eregon (Benoit Daloze).

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
However, that's just one case, not all, so we should not raise an exception early like that.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:112665] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
@ 2023-03-02 12:07 ` Eregon (Benoit Daloze) via ruby-core
  2023-03-02 12:11 ` [ruby-core:112667] " Eregon (Benoit Daloze) via ruby-core
                   ` (24 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2023-03-02 12:07 UTC (permalink / raw)
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #19473 has been updated by Eregon (Benoit Daloze).


Also `Monitor` has the same problem on CRuby:
```
$ ruby -ve 'm=Monitor.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'

And again it works fine on truffleruby and jruby
```
And since `Monitor` is reentrant there is no reason at all (AFAIK) to prevent it in `trap`.

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-102113

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
However, that's just one case, not all, so we should not raise an exception early like that.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:112667] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
  2023-03-02 12:07 ` [ruby-core:112665] " Eregon (Benoit Daloze) via ruby-core
@ 2023-03-02 12:11 ` Eregon (Benoit Daloze) via ruby-core
  2023-03-09  3:55 ` [ruby-core:112752] " ioquatix (Samuel Williams) via ruby-core
                   ` (23 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2023-03-02 12:11 UTC (permalink / raw)
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #19473 has been updated by Eregon (Benoit Daloze).


Another approach would be to do it like e.g. Java which runs signal handlers on a separate thread.
Then there is no potential problem of the signal handler being reentrant to the main thread/fiber.
But that is a much bigger semantic change of course, so I think just removing the `can't be called from trap context (ThreadError)` is much more actionable.

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-102116

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
However, that's just one case, not all, so we should not raise an exception early like that.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:112752] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
  2023-03-02 12:07 ` [ruby-core:112665] " Eregon (Benoit Daloze) via ruby-core
  2023-03-02 12:11 ` [ruby-core:112667] " Eregon (Benoit Daloze) via ruby-core
@ 2023-03-09  3:55 ` ioquatix (Samuel Williams) via ruby-core
  2023-03-09 16:19 ` [ruby-core:112789] " Eregon (Benoit Daloze) via ruby-core
                   ` (22 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: ioquatix (Samuel Williams) via ruby-core @ 2023-03-09  3:55 UTC (permalink / raw)
  To: ruby-core; +Cc: ioquatix (Samuel Williams)

Issue #19473 has been updated by ioquatix (Samuel Williams).


I was under the impression that very few operations are safe to call from a signal handler. Is that not the case here?

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-102245

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:112789] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (2 preceding siblings ...)
  2023-03-09  3:55 ` [ruby-core:112752] " ioquatix (Samuel Williams) via ruby-core
@ 2023-03-09 16:19 ` Eregon (Benoit Daloze) via ruby-core
  2023-03-10  6:49 ` [ruby-core:112827] " ko1 (Koichi Sasada) via ruby-core
                   ` (21 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2023-03-09 16:19 UTC (permalink / raw)
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #19473 has been updated by Eregon (Benoit Daloze).


ioquatix (Samuel Williams) wrote in #note-5:
> I was under the impression that very few operations are safe to call from a signal handler. Is that not the case here?

I think you are thinking about C signal handlers and C async-safety. This is not relevant here, the Ruby signal handler doesn't run inside the C signal handler.

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-102285

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:112827] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (3 preceding siblings ...)
  2023-03-09 16:19 ` [ruby-core:112789] " Eregon (Benoit Daloze) via ruby-core
@ 2023-03-10  6:49 ` ko1 (Koichi Sasada) via ruby-core
  2023-03-10 15:47 ` [ruby-core:112846] " Eregon (Benoit Daloze) via ruby-core
                   ` (20 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: ko1 (Koichi Sasada) via ruby-core @ 2023-03-10  6:49 UTC (permalink / raw)
  To: ruby-core; +Cc: ko1 (Koichi Sasada)

Issue #19473 has been updated by ko1 (Koichi Sasada).


The current limitation is introduced to protect user from dead-lock error like that:

```
m = Mutex.new

trap(:INT){m.synchronizaton{ do_something } }

m.lock; sleep
# C-c here
```

will cause deadlock and it is hard to predict and coding for it. ( https://github.com/ruby/timeout/issues/17#issuecomment-1461498517 is one example)

> Another approach would be to do it like e.g. Java which runs signal handlers on a separate thread.

Yes. There was a discussion to introduce a thread for signal handlers. But it was not introduced maybe because it is too much for many cases.

akr had proposed that if someone want to use such case, how about to make a thread in a trap handler for workaround. I think it is reasonable.



----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-102334

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:112846] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (4 preceding siblings ...)
  2023-03-10  6:49 ` [ruby-core:112827] " ko1 (Koichi Sasada) via ruby-core
@ 2023-03-10 15:47 ` Eregon (Benoit Daloze) via ruby-core
  2023-03-10 16:16 ` [ruby-core:112847] " Eregon (Benoit Daloze) via ruby-core
                   ` (19 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2023-03-10 15:47 UTC (permalink / raw)
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #19473 has been updated by Eregon (Benoit Daloze).


Right, like I mentioned above.
But the deadlock might not happen (or even cannot happen for some cases I mentioned above), and then CRuby fails needlessly.
If the deadlock does happen, then we would get this error: `deadlock; recursive locking (ThreadError)`. Which means the program doesn't hang and there is a stacktrace and it shows relevant information which is useful to fix it.

So I think it is just a mistake to fail early with `can't be called from trap context (ThreadError)`.
In fact CRuby even cheats this rule for IO I saw.
There are fully correct use cases for using a Mutex inside `trap` as I mentioned in the description.

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-102358

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:112847] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (5 preceding siblings ...)
  2023-03-10 15:47 ` [ruby-core:112846] " Eregon (Benoit Daloze) via ruby-core
@ 2023-03-10 16:16 ` Eregon (Benoit Daloze) via ruby-core
  2023-03-10 17:51 ` [ruby-core:112850] " Eregon (Benoit Daloze) via ruby-core
                   ` (18 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2023-03-10 16:16 UTC (permalink / raw)
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #19473 has been updated by Eregon (Benoit Daloze).


https://github.com/ruby/timeout/issues/17#issuecomment-1464039853 seems a proof this restriction is wrong, and actually prevents a correct solution to that issue.

Also this restriction makes no sense for Monitor, since that's reentrant by design.

In fact, one could make their own Mutex using Queue, since Queue is allowed in trap handlers to workaround this restriction. It doesn't make any sense to reimplement Mutex in such an inefficient way of course.

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-102359

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:112850] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (6 preceding siblings ...)
  2023-03-10 16:16 ` [ruby-core:112847] " Eregon (Benoit Daloze) via ruby-core
@ 2023-03-10 17:51 ` Eregon (Benoit Daloze) via ruby-core
  2023-03-12 15:00 ` [ruby-core:112856] " ko1 (Koichi Sasada) via ruby-core
                   ` (17 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2023-03-10 17:51 UTC (permalink / raw)
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #19473 has been updated by Eregon (Benoit Daloze).


ko1 (Koichi Sasada) wrote in #note-7:
> Yes. There was a discussion to introduce a thread for signal handlers. But it was not introduced maybe because it is too much for many cases.

I think this is ultimately the proper solution to this problem.
It's extremely hard to reason about a signal handler running on top of any line of code of the main thread.

> akr had proposed that if someone want to use such case, how about to make a thread in a trap handler for workaround. I think it is reasonable.

I cannot really use that here, or I would need an efficient way to know I'm in a trap handler (I suppose I could catch the `can't be called from trap context (ThreadError)` but that's really ugly and likely quite slow).
Because Timeout doesn't define the trap handler, the user does.
And asking every user to workaround like that doesn't seem good, that actually creates more threads than a single signal-handling thread created by Ruby.

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-102362

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:112856] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (7 preceding siblings ...)
  2023-03-10 17:51 ` [ruby-core:112850] " Eregon (Benoit Daloze) via ruby-core
@ 2023-03-12 15:00 ` ko1 (Koichi Sasada) via ruby-core
  2023-03-12 15:35 ` [ruby-core:112858] " Eregon (Benoit Daloze) via ruby-core
                   ` (16 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: ko1 (Koichi Sasada) via ruby-core @ 2023-03-12 15:00 UTC (permalink / raw)
  To: ruby-core; +Cc: ko1 (Koichi Sasada)

Issue #19473 has been updated by ko1 (Koichi Sasada).


Eregon (Benoit Daloze) wrote in #note-8:
> Right, like I mentioned above.
> But the deadlock might not happen (or even cannot happen for some cases I mentioned above), and then CRuby fails needlessly.
> If the deadlock does happen, then we would get this error: `deadlock; recursive locking (ThreadError)`. Which means the program doesn't hang and there is a stacktrace and it shows relevant information which is useful to fix it.

My understandings are:

(1) It is hard to detect such deadlock risk because such errors occur with with very low frequency.
(2) It is hard to expect that the Mutex users care about signal handlers.
(3) It is hard to modify fixing with signal handlers.

So I think current limitation makes sense.

>> akr had proposed that if someone want to use such case, how about to make a thread in a trap handler for workaround. I think it is reasonable.

> I cannot really use that here, or I would need an efficient way to know I'm in a trap handler (I suppose I could catch the can't be called from trap context (ThreadError) but that's really ugly and likely quite slow).
> Because Timeout doesn't define the trap handler, the user does.
> And asking every user to workaround like that doesn't seem good, that actually creates more threads than a single signal-handling thread created by Ruby.

akr's idea is not about Timeout library, but for Timeout users in trap handers.
If someone wants to use Timeout (or other complex/thread-safety needed code), making a thread like `trap(...){Thread.new{ ... }}` seems feasible workaround.

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-102367

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:112858] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (8 preceding siblings ...)
  2023-03-12 15:00 ` [ruby-core:112856] " ko1 (Koichi Sasada) via ruby-core
@ 2023-03-12 15:35 ` Eregon (Benoit Daloze) via ruby-core
  2023-03-12 15:47 ` [ruby-core:112859] " ko1 (Koichi Sasada) via ruby-core
                   ` (15 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2023-03-12 15:35 UTC (permalink / raw)
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #19473 has been updated by Eregon (Benoit Daloze).


ko1 (Koichi Sasada) wrote in #note-11:
> My understandings are:
> 
> (1) It is hard to detect such deadlock risk because such errors occur with with very low frequency.
> (2) It is hard to expect that the Mutex users care about signal handlers.
> (3) It is hard to modify fixing with signal handlers.
> 
> So I think current limitation makes sense.

No, it doesn't make sense, because it actually prevents the proper fix for `TIMEOUT_THREAD_MUTEX` for Timeout:
https://github.com/ruby/timeout/issues/17#issuecomment-1464039853
So the limitation is unsound (it prevents valid and correct usages) and so we must remove it.

Also it doesn't catch `try_lock` and `Queue#pop`, etc, so it's very rough and inaccurate.

We could make it a `$VERBOSE = true` warning maybe, but again it would be false positives and impossible to address, so it seems not good.

Another idea would be being able to mark which Mutex are "trap-safe", maybe with an argument to Mutex#initialize.
Sort of like how IO does it.

ko1 (Koichi Sasada) wrote in #note-11:
> akr's idea is not about Timeout library, but for Timeout users in trap handers.
> If someone wants to use Timeout (or other complex/thread-safety needed code), making a thread like `trap(...){Thread.new{ ... }}` seems feasible workaround.

But users might use some gem or some library code in trap handler and they don't know whether that might not know whether that uses Timeout or not.
And even if they know, it should just work, without needing any workaround from the user.

My conclusion is doing nothing is unacceptable, this CRuby limitation breaks valid and correct code such as https://github.com/ruby/timeout/issues/17#issuecomment-1464039853.
So either:
1) We remove this unsound limitation, same as on other Rubies
2) We add an argument to Mutex#initialize to mark it as trap-safe
3) We execute trap handlers on a separate thread

Which one do we choose?

I think 3 is the best (much easier to reason about), but of course there is some potential for incompatibility there.
1 or 2 seem easy, so we could do them fast and maybe even backport it.

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-102369

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:112859] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (9 preceding siblings ...)
  2023-03-12 15:35 ` [ruby-core:112858] " Eregon (Benoit Daloze) via ruby-core
@ 2023-03-12 15:47 ` ko1 (Koichi Sasada) via ruby-core
  2023-03-13 15:41 ` [ruby-core:112864] " Eregon (Benoit Daloze) via ruby-core
                   ` (14 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: ko1 (Koichi Sasada) via ruby-core @ 2023-03-12 15:47 UTC (permalink / raw)
  To: ruby-core; +Cc: ko1 (Koichi Sasada)

Issue #19473 has been updated by ko1 (Koichi Sasada).


> No, it doesn't make sense, because it actually prevents the proper fix for TIMEOUT_THREAD_MUTEX for Timeout:

I think this reason violates (1).

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-102370

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:112864] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (10 preceding siblings ...)
  2023-03-12 15:47 ` [ruby-core:112859] " ko1 (Koichi Sasada) via ruby-core
@ 2023-03-13 15:41 ` Eregon (Benoit Daloze) via ruby-core
  2025-04-24 10:09 ` [ruby-core:121723] [Ruby " ioquatix (Samuel Williams) via ruby-core
                   ` (13 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2023-03-13 15:41 UTC (permalink / raw)
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #19473 has been updated by Eregon (Benoit Daloze).


ko1 (Koichi Sasada) wrote in #note-13:
> I think this reason violates (1).

What do you mean?
AFAIK https://github.com/ruby/timeout/issues/17#issuecomment-1464039853 is correct.
If it's incorrect, please point the mistake in my reasoning.

We can't have understanding `(1)` by using solution `1.` (remove the trap+Mutex check), indeed those are exclusive.
IMO it's still worth it because we are always breaking when it's only rarely a problem.
So CRuby is causing hard failures for things that might just work.

Here is a list of your 3 understandings and how the 3 solutions address them, I also add (0):
(0) Fixes soundness, i.e. doesn't fail when the code correctly handles signal handler reentrancy

Solutions:
1. fixes (0)
2. fixes (0), (1) because no change there, (2) because opt-in
3. fixes (0), (1), (2), (3)

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-102380

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:121723] [Ruby Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (11 preceding siblings ...)
  2023-03-13 15:41 ` [ruby-core:112864] " Eregon (Benoit Daloze) via ruby-core
@ 2025-04-24 10:09 ` ioquatix (Samuel Williams) via ruby-core
  2025-06-06  9:29 ` [ruby-core:122482] " ioquatix (Samuel Williams) via ruby-core
                   ` (12 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: ioquatix (Samuel Williams) via ruby-core @ 2025-04-24 10:09 UTC (permalink / raw)
  To: ruby-core; +Cc: ioquatix (Samuel Williams)

Issue #19473 has been updated by ioquatix (Samuel Williams).


I agree the current behaviour seems too limiting. If user code does something incorrect, it should fail, or deadlock. But we should not prevent valid programs just because some users will write invalid programs.

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-112776

* Author: Eregon (Benoit Daloze)
* Status: Open
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:122482] [Ruby Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (12 preceding siblings ...)
  2025-04-24 10:09 ` [ruby-core:121723] [Ruby " ioquatix (Samuel Williams) via ruby-core
@ 2025-06-06  9:29 ` ioquatix (Samuel Williams) via ruby-core
  2025-06-24  4:21 ` [ruby-core:122588] " ioquatix (Samuel Williams) via ruby-core
                   ` (11 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: ioquatix (Samuel Williams) via ruby-core @ 2025-06-06  9:29 UTC (permalink / raw)
  To: ruby-core; +Cc: ioquatix (Samuel Williams)

Issue #19473 has been updated by ioquatix (Samuel Williams).


Folks, what do you think of <https://github.com/ruby/ruby/pull/13545>?

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-113671

* Author: Eregon (Benoit Daloze)
* Status: Open
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:122588] [Ruby Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (13 preceding siblings ...)
  2025-06-06  9:29 ` [ruby-core:122482] " ioquatix (Samuel Williams) via ruby-core
@ 2025-06-24  4:21 ` ioquatix (Samuel Williams) via ruby-core
  2025-06-24  7:01 ` [ruby-core:122591] " ioquatix (Samuel Williams) via ruby-core
                   ` (10 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: ioquatix (Samuel Williams) via ruby-core @ 2025-06-24  4:21 UTC (permalink / raw)
  To: ruby-core; +Cc: ioquatix (Samuel Williams)

Issue #19473 has been updated by ioquatix (Samuel Williams).


Had to special case another mutex: https://github.com/ruby/ruby/pull/13684

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-113823

* Author: Eregon (Benoit Daloze)
* Status: Open
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:122591] [Ruby Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (14 preceding siblings ...)
  2025-06-24  4:21 ` [ruby-core:122588] " ioquatix (Samuel Williams) via ruby-core
@ 2025-06-24  7:01 ` ioquatix (Samuel Williams) via ruby-core
  2025-06-24  7:11 ` [ruby-core:122592] " ioquatix (Samuel Williams) via ruby-core
                   ` (9 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: ioquatix (Samuel Williams) via ruby-core @ 2025-06-24  7:01 UTC (permalink / raw)
  To: ruby-core; +Cc: ioquatix (Samuel Williams)

Issue #19473 has been updated by ioquatix (Samuel Williams).


I agree, it would be nice to relax this restriction. But I also understand that it's unpredictable since the trap handler can run at any point the Ruby interpreter checks for interrupts (which for user code is fairly frequent)

As an alternative (to relaxing the restriction), we could expose the existing logic/flag, e.g. `Mutex#safe_in_trap_context = true/false`. It's a bit ugly, but it's one idea that might allow us to move forward while retaining the default existing behaviour.

> It's extremely hard to reason about a signal handler running on top of any line of code of the main thread.

In Async, we handle this using `Thread.handle_interrupt` to defer interrupts until a specific known point in the event loop execution: <https://github.com/socketry/async/blob/4206e32da4e04b65a4074080f9a59d66b827a347/lib/async/scheduler.rb#L520-L540>. This helps to make interrupt handling more predictable. This makes asynchronous interrupts a little bit safer, IMHO.

> There seems to be no valid reason to prevent all `Mutex#synchronize` in trap.

`pthread_mutex_` functions are not async-signal-safe. However, Ruby does not run code directly in the signal handler, so it's not the same risk - but I feel like in principle it's still the same semantic issue. So, it's not entirely clear to me that it's safe to use any kind of blocking operation in principle, in trap handlers.

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-113826

* Author: Eregon (Benoit Daloze)
* Status: Open
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:122592] [Ruby Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (15 preceding siblings ...)
  2025-06-24  7:01 ` [ruby-core:122591] " ioquatix (Samuel Williams) via ruby-core
@ 2025-06-24  7:11 ` ioquatix (Samuel Williams) via ruby-core
  2025-06-24 20:23 ` [ruby-core:122595] " Eregon (Benoit Daloze) via ruby-core
                   ` (8 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: ioquatix (Samuel Williams) via ruby-core @ 2025-06-24  7:11 UTC (permalink / raw)
  To: ruby-core; +Cc: ioquatix (Samuel Williams)

Issue #19473 has been updated by ioquatix (Samuel Williams).


As one other idea, what about introducing `Mutex#reentrant = true/false`. This seems like a more general model than `safe_in_trap_context` and I think we could then allow reentrant mutex to be used safely in trap contexts. The user would be explicitly opting into that scenario.

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-113827

* Author: Eregon (Benoit Daloze)
* Status: Open
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:122595] [Ruby Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (16 preceding siblings ...)
  2025-06-24  7:11 ` [ruby-core:122592] " ioquatix (Samuel Williams) via ruby-core
@ 2025-06-24 20:23 ` Eregon (Benoit Daloze) via ruby-core
  2025-07-08  5:10 ` [ruby-core:122671] " ko1 (Koichi Sasada) via ruby-core
                   ` (7 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2025-06-24 20:23 UTC (permalink / raw)
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #19473 has been updated by Eregon (Benoit Daloze).


I think a simple way to look at this issue is to analyze all cases:
1. The Mutex is not held by any Thread, and the signal handler acquires it (the common case): this is fully correct and would work just fine without the unnecessary `can't be called from trap context (ThreadError)` from CRuby which breaks this.
2. The Mutex is held by the main Thread, and the signal handler tries to acquire it: it would be ` deadlock; recursive locking (ThreadError)` which is as good as the current `can't be called from trap context (ThreadError)`.
3. The Mutex is held by another Thread, and the signal handler tries to acquire it: the signal handler will wait until that Thread releases the Mutex. This is totally normal, it is Mutex semantics. You can't argue the Thread might keep the Mutex forever because that would be a bug in the first place. In the worst case it hangs, the user would Ctrl+C and see the stacktrace, which is a reasonable outcome for that case. CRuby is even sometimes able to detect such a deadlock and raises a `fatal` error in that case.

So removing the check is clearly better for the 1st case, is equivalent for the 2nd case, and is better for the 3rd case (at least for the subcase where the other Thread doesn't hold the Mutex forever).
If anyone disagrees, can they explain how it is better for all of these cases to fail with `can't be called from trap context (ThreadError)`?

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-113834

* Author: Eregon (Benoit Daloze)
* Status: Open
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:122671] [Ruby Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (17 preceding siblings ...)
  2025-06-24 20:23 ` [ruby-core:122595] " Eregon (Benoit Daloze) via ruby-core
@ 2025-07-08  5:10 ` ko1 (Koichi Sasada) via ruby-core
  2025-07-08  8:38 ` [ruby-core:122677] " mame (Yusuke Endoh) via ruby-core
                   ` (6 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: ko1 (Koichi Sasada) via ruby-core @ 2025-07-08  5:10 UTC (permalink / raw)
  To: ruby-core; +Cc: ko1 (Koichi Sasada)

Issue #19473 has been updated by ko1 (Koichi Sasada).


Eregon (Benoit Daloze) wrote in #note-20:
> I think a simple way to look at this issue is to analyze all cases:
> 1. The Mutex is not held by any Thread, and the signal handler acquires it (the common case): this is fully correct and would work just fine without the unnecessary `can't be called from trap context (ThreadError)` from CRuby which breaks this.
> 2. The Mutex is held by the main Thread, and the signal handler tries to acquire it: it would be ` deadlock; recursive locking (ThreadError)` which is as good as the current `can't be called from trap context (ThreadError)`.
> 3. The Mutex is held by another Thread, and the signal handler tries to acquire it: the signal handler will wait until that Thread releases the Mutex. This is totally normal, it is Mutex semantics. You can't argue the Thread might keep the Mutex forever because that would be a bug in the first place. In the worst case it hangs, the user would Ctrl+C and see the stacktrace, which is a reasonable outcome for that case. CRuby is even sometimes able to detect such a deadlock and raises a `fatal` error in that case.

> If anyone disagrees, can they explain how it is better for all of these cases to fail with `can't be called from trap context (ThreadError)`?


In most cases, (1) occurs, so it is difficult to notice the possibility of (2).
With the current behavior, it is possible to recognize the risk of (2) even in case (1). This is the reason why this behavior is merged into Ruby.
(In this thread, I mentioned as "hard to predict" — this is what it refers to.)


----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-113951

* Author: Eregon (Benoit Daloze)
* Status: Open
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:122677] [Ruby Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (18 preceding siblings ...)
  2025-07-08  5:10 ` [ruby-core:122671] " ko1 (Koichi Sasada) via ruby-core
@ 2025-07-08  8:38 ` mame (Yusuke Endoh) via ruby-core
  2025-07-08 11:24 ` [ruby-core:122678] " tompng (tomoya ishida) via ruby-core
                   ` (5 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: mame (Yusuke Endoh) via ruby-core @ 2025-07-08  8:38 UTC (permalink / raw)
  To: ruby-core; +Cc: mame (Yusuke Endoh)

Issue #19473 has been updated by mame (Yusuke Endoh).


If `Mutex#lock` were permitted inside a trap handler, a program like the following would have a very hard-to-reproduce race condition bug.

```ruby
m = Mutex.new

trap(:INT) { m.synchronize { p :signalled } }

while true
  sleep 1
  m.synchronize { p :hi! }
end
```

If you press Ctrl+C while this program is running, it would, in most cases, print `:signalled` and continue execution, which is an expected behavior. However, if the signal arrives at a particularly unlucky moment, it would cause a `deadlock; recursive locking (ThreadError)`.

The problem isn't just that the bug is hard to reproduce; it's that programmers often don't even realize they've introduced such a race condition bug. To prevent this, `Mutex#lock` is designed to consistently raise an exception when called inside a trap handler.

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-113957

* Author: Eregon (Benoit Daloze)
* Status: Open
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:122678] [Ruby Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (19 preceding siblings ...)
  2025-07-08  8:38 ` [ruby-core:122677] " mame (Yusuke Endoh) via ruby-core
@ 2025-07-08 11:24 ` tompng (tomoya ishida) via ruby-core
  2025-07-08 20:33 ` [ruby-core:122686] " Eregon (Benoit Daloze) via ruby-core
                   ` (4 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: tompng (tomoya ishida) via ruby-core @ 2025-07-08 11:24 UTC (permalink / raw)
  To: ruby-core; +Cc: tompng (tomoya ishida)

Issue #19473 has been updated by tompng (tomoya ishida).


Locking multiple mutex with a fixed order is considered to be deadlock-safe, but permitting mutex in signal_handler, I think fixed order locking is hard or impossible.

The code below looks deadlock-safe, but it's not.
~~~ruby
trap(:INT){A.synchronize{}}
Thread.new{A.synchronize{sleep 0.1; B.synchronize{}}}
B.synchronize{sleep}
~~~

- Main thread locks B
- Another thread locks A and then tries to lock B
- Signal handler locks A

Main thread can lock B and then A if signal handler is called while main thread is locking B.
I don't think there's an easy way to fix this race condition except `trap(...){Tread.new{...}}`, so I think the restriction is reasonable.


----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-113958

* Author: Eregon (Benoit Daloze)
* Status: Open
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:122686] [Ruby Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (20 preceding siblings ...)
  2025-07-08 11:24 ` [ruby-core:122678] " tompng (tomoya ishida) via ruby-core
@ 2025-07-08 20:33 ` Eregon (Benoit Daloze) via ruby-core
  2025-07-09  0:28 ` [ruby-core:122691] " mame (Yusuke Endoh) via ruby-core
                   ` (3 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2025-07-08 20:33 UTC (permalink / raw)
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #19473 has been updated by Eregon (Benoit Daloze).


OK, thank you all 3 for clarifying.
I understand that point of view, even though I don't fully agree with it.

One problem is this limitation makes it impossible to support `Timeout.timeout` in `trap`, see https://github.com/ruby/timeout/issues/17, but that would work 100% correctly without this limitation.
In fact it already works correctly on JRuby and TruffleRuby which don't raise for case 1.

I see two ways to resolve this:
* Run signal handlers not on the main thread but another thread. Then there is no need to prevent Mutex/Monitor/etc in `trap` since there won't be such problems. It seems the best solution and already e.g. what the JVM does. It might have some incompatibility due to running that code on another thread, but it shouldn't be the case since even currently trap handlers run "concurrently" to the main thread (they can run at any interrupt point which is pretty much anywhere). For exceptions from trap handlers (like the default SIGINT handler), they can simply be forwarded to the main thread so that case is easy to stay compatible. I think the main thing missing here is someone taking the time to implement this, anyone interested?
* Allow specific Mutex & Monitor to be marked as "trap-safe" via a new method (like `rb_mutex_allow_trap()` but as a Ruby method) as proposed by @ioquatix in https://bugs.ruby-lang.org/issues/19473#note-18. That method documentation can then show the examples above and examples of safe usages of it. The default is still safe, but then at least it's possible to use Mutex in trap handler when we have checked the logic would be correct and CRuby no longer prevents such correct cases.

@ko1 @mame @tompng Any opinion on which of these two we should do?

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-113964

* Author: Eregon (Benoit Daloze)
* Status: Open
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:122691] [Ruby Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (21 preceding siblings ...)
  2025-07-08 20:33 ` [ruby-core:122686] " Eregon (Benoit Daloze) via ruby-core
@ 2025-07-09  0:28 ` mame (Yusuke Endoh) via ruby-core
  2025-07-09  8:37 ` [ruby-core:122696] " Eregon (Benoit Daloze) via ruby-core
                   ` (2 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: mame (Yusuke Endoh) via ruby-core @ 2025-07-09  0:28 UTC (permalink / raw)
  To: ruby-core; +Cc: mame (Yusuke Endoh)

Issue #19473 has been updated by mame (Yusuke Endoh).


> Run signal handlers not on the main thread but another thread.

While I don't have a strong opinion on this, I personally feel this approach makes more sense.

However, ko1 said that users can just write this explicitly themselves. Indeed, creating a new thread for each signal arrival is extremely simple.

```ruby
trap { ... }

# is rewritten to:

trap { Thread.new { ... } }
```

Setting up a single, dedicated thread to execute signal handlers serially is more complex, but it still doesn't require a lot of code.

```ruby
trap { ... }

# is rewritten to:

Q = Queue.new
Thread.new do
  Q.each { ... }
end
trap { Q << it }
```

If core language support were to be added for this, I suppose it would be for cases where you want to force a trap handler written by someone else (e.g., in a gem) to run in a separate thread. But I wonder, is such a scenario realistic?

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-113969

* Author: Eregon (Benoit Daloze)
* Status: Open
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:122696] [Ruby Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (22 preceding siblings ...)
  2025-07-09  0:28 ` [ruby-core:122691] " mame (Yusuke Endoh) via ruby-core
@ 2025-07-09  8:37 ` Eregon (Benoit Daloze) via ruby-core
  2025-07-09 10:40 ` [ruby-core:122697] " mame (Yusuke Endoh) via ruby-core
  2025-07-10  3:44 ` [ruby-core:122702] " ioquatix (Samuel Williams) via ruby-core
  25 siblings, 0 replies; 27+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2025-07-09  8:37 UTC (permalink / raw)
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #19473 has been updated by Eregon (Benoit Daloze).


mame (Yusuke Endoh) wrote in #note-25:
> Indeed, creating a new thread for each signal arrival is extremely simple.

It feels like a hack though and it's a pretty high cost to create a new Thread for every signal.
If this was the official solution, then the `can't be called from trap context (ThreadError)` should mention it, but I think it's not good enough to be an official solution, just a hacky workaround.
It seems nobody mentioned this workaround on https://github.com/ruby/timeout/issues/17. I think that's because it's unnatural and not a proper solution.

BTW another problem with that workaround is if the same signal is sent twice in quick succession (e.g. two Ctrl+C) the handlers will run concurrently in two threads, which is a recipe for complicated problems.
Currently trap handlers run serially (see issue description) and that seems clearly safer.

> Setting up a single, dedicated thread to execute signal handlers serially is more complex, but it still doesn't require a lot of code.

The problem there is then all `trap` handlers need to `Q << it` which is too difficult to guarantee in general.

> I suppose it would be for cases where you want to force a trap handler written by someone else (e.g., in a gem) to run in a separate thread. But I wonder, is such a scenario realistic?

Yes, for example gems like webservers like Puma set trap handlers.
There are many gems with trap handlers:
```
$ gem-codesearch '\bSignal\.trap\b' | wc -l
5528
```
(more with `\btrap\b` but that takes too long to list)

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-113974

* Author: Eregon (Benoit Daloze)
* Status: Open
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:122697] [Ruby Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (23 preceding siblings ...)
  2025-07-09  8:37 ` [ruby-core:122696] " Eregon (Benoit Daloze) via ruby-core
@ 2025-07-09 10:40 ` mame (Yusuke Endoh) via ruby-core
  2025-07-10  3:44 ` [ruby-core:122702] " ioquatix (Samuel Williams) via ruby-core
  25 siblings, 0 replies; 27+ messages in thread
From: mame (Yusuke Endoh) via ruby-core @ 2025-07-09 10:40 UTC (permalink / raw)
  To: ruby-core; +Cc: mame (Yusuke Endoh)

Issue #19473 has been updated by mame (Yusuke Endoh).


> Yes, for example gems like webservers like Puma set trap handlers.

The trap handler code that has already been written and published probably does not use `Mutex#lock`, so it is not related to this problem. Such benign trap handlers do not require invoking a new thread.

In terms of using `Timeout.timeout` in a trap handler, I don't think it is a good idea to run in a trap handler time-consuming code that requires `Timeout.timeout`, because the trap handler may be executed with the main thread suspended in a strange state.

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-113975

* Author: Eregon (Benoit Daloze)
* Status: Open
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [ruby-core:122702] [Ruby Bug#19473] can't be called from trap context (ThreadError) is too limiting
  2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
                   ` (24 preceding siblings ...)
  2025-07-09 10:40 ` [ruby-core:122697] " mame (Yusuke Endoh) via ruby-core
@ 2025-07-10  3:44 ` ioquatix (Samuel Williams) via ruby-core
  25 siblings, 0 replies; 27+ messages in thread
From: ioquatix (Samuel Williams) via ruby-core @ 2025-07-10  3:44 UTC (permalink / raw)
  To: ruby-core; +Cc: ioquatix (Samuel Williams)

Issue #19473 has been updated by ioquatix (Samuel Williams).


We know that signal handlers run in a trap context, but are there other ways this can happen? I was under the impression GC finalizers also run in a pseudo-trap context, preventing the use of Mutex too. Of course, using a mutex during GC is probably a bad idea.

----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473#change-113979

* Author: Eregon (Benoit Daloze)
* Status: Open
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
	from -e:1:in `block in <main>'
	from -e:1:in `kill'
	from -e:1:in `<main>'
```

Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK

$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```

This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939

I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
But that would otherwise already raise `deadlock; recursive locking (ThreadError)`, so there is no point to fail early.
And that's just one case, not all, so we should not always raise an exception.

There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

in trap
-e:1:in `wait'
-e:1:in `<main>'

```

And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2025-07-10  3:45 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-02 12:04 [ruby-core:112664] [Ruby master Bug#19473] can't be called from trap context (ThreadError) is too limiting Eregon (Benoit Daloze) via ruby-core
2023-03-02 12:07 ` [ruby-core:112665] " Eregon (Benoit Daloze) via ruby-core
2023-03-02 12:11 ` [ruby-core:112667] " Eregon (Benoit Daloze) via ruby-core
2023-03-09  3:55 ` [ruby-core:112752] " ioquatix (Samuel Williams) via ruby-core
2023-03-09 16:19 ` [ruby-core:112789] " Eregon (Benoit Daloze) via ruby-core
2023-03-10  6:49 ` [ruby-core:112827] " ko1 (Koichi Sasada) via ruby-core
2023-03-10 15:47 ` [ruby-core:112846] " Eregon (Benoit Daloze) via ruby-core
2023-03-10 16:16 ` [ruby-core:112847] " Eregon (Benoit Daloze) via ruby-core
2023-03-10 17:51 ` [ruby-core:112850] " Eregon (Benoit Daloze) via ruby-core
2023-03-12 15:00 ` [ruby-core:112856] " ko1 (Koichi Sasada) via ruby-core
2023-03-12 15:35 ` [ruby-core:112858] " Eregon (Benoit Daloze) via ruby-core
2023-03-12 15:47 ` [ruby-core:112859] " ko1 (Koichi Sasada) via ruby-core
2023-03-13 15:41 ` [ruby-core:112864] " Eregon (Benoit Daloze) via ruby-core
2025-04-24 10:09 ` [ruby-core:121723] [Ruby " ioquatix (Samuel Williams) via ruby-core
2025-06-06  9:29 ` [ruby-core:122482] " ioquatix (Samuel Williams) via ruby-core
2025-06-24  4:21 ` [ruby-core:122588] " ioquatix (Samuel Williams) via ruby-core
2025-06-24  7:01 ` [ruby-core:122591] " ioquatix (Samuel Williams) via ruby-core
2025-06-24  7:11 ` [ruby-core:122592] " ioquatix (Samuel Williams) via ruby-core
2025-06-24 20:23 ` [ruby-core:122595] " Eregon (Benoit Daloze) via ruby-core
2025-07-08  5:10 ` [ruby-core:122671] " ko1 (Koichi Sasada) via ruby-core
2025-07-08  8:38 ` [ruby-core:122677] " mame (Yusuke Endoh) via ruby-core
2025-07-08 11:24 ` [ruby-core:122678] " tompng (tomoya ishida) via ruby-core
2025-07-08 20:33 ` [ruby-core:122686] " Eregon (Benoit Daloze) via ruby-core
2025-07-09  0:28 ` [ruby-core:122691] " mame (Yusuke Endoh) via ruby-core
2025-07-09  8:37 ` [ruby-core:122696] " Eregon (Benoit Daloze) via ruby-core
2025-07-09 10:40 ` [ruby-core:122697] " mame (Yusuke Endoh) via ruby-core
2025-07-10  3:44 ` [ruby-core:122702] " ioquatix (Samuel Williams) via ruby-core

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).