crystal icon indicating copy to clipboard operation
crystal copied to clipboard

Bypass signal handling in interpreted code

Open cyangle opened this issue 1 year ago • 8 comments

Would this be an acceptable workaround for issue #12241?

It bypasses signal handling in interpreted code and waits until child process terminates.

I also tried forwarding child process exit code to interpreted code via Unix socket, but it doesn't work because it seems like the interpreter fiber never yields to other fibers unless it enters into debugger repl.

@straight-shoota

cyangle avatar Nov 26 '23 19:11 cyangle

My comment in https://github.com/crystal-lang/crystal/pull/14016#issuecomment-1825468528 still stands: What's the point? Do you have a specific use case which this change enables or is it just to get more specs running?

straight-shoota avatar Nov 26 '23 23:11 straight-shoota

This allows running shell commands with the interpreter.

And I think it's worth it because several github issues were created due to not being able to run shell commands. And fixing more spec tests is a side effect of fixing Process.run

As for the issue of handling process signals, I don't think it's used as widely as Process.run

This brings a lot of benefits with minimal efforts, so why not?

@straight-shoota

cyangle avatar Nov 27 '23 00:11 cyangle

So do you think this is actually a proper bugfix instead of a workaround?

straight-shoota avatar Dec 06 '23 10:12 straight-shoota

This is still a workaround with a much smaller impact than #14016.

cyangle avatar Dec 07 '23 01:12 cyangle

Process#wait must not block the current thread whether as a bug or by design, especially now that it is asynchronous on compiled Windows as well. IMO at a minimum the LibC.waitpid needs to happen on a fresh system thread.

HertzDevil avatar Dec 07 '23 06:12 HertzDevil

Hum, that workaround ain't so bad (fake it until you can make it :shushing_face:).

It's very inefficient, but if it enables running sub-processes in interpreted mode that would be nice. It currently hangs forever, and I regularly trip on that limitation myself + have a bunch of skip "interpreted mode hangs forever on Process.run" in some test suites (e.g. Minitest).

@HertzDevil you mean something like that? the issue is how to enqueue the suspended fiber, but #send_fiber should do the job:

def wait
  {% if flag?(:interpreted) %}
    fiber = Fiber.current
    exit_code = uninitialized Int32

    Thread.new do
      LibC.waitpid(pid, pointerof(exit_code), 0)
      fiber.current_thread.scheduler.send_fiber(fiber)
    end

    fiber.reschedule
    @channel.send(exit_code)
    @channel.close
  {% end %}
  @channel.receive
end

@cyangle if the reenabled specs are calling into Crystal, they may be calling crystal run and not crystal i :thinking: if so, they don't test what they should be.

ysbaddaden avatar Dec 14 '23 14:12 ysbaddaden

I don't think thread works properly when interpreted. There seems to be something wrong with thread local variables.

result = ""
thread = Thread.new do
  result = "From Thread #{Thread.current}"
end

thread.join

puts "#{Thread.current}: #{result}"

cyangle avatar Dec 16 '23 03:12 cyangle

I have nothing more to say about this PR. It's still just a hack, but not being able to spawn a child process is very limiting. Threads are indeed unsupported in interpreted code, so we can't use 'em.

ysbaddaden avatar Feb 12 '24 10:02 ysbaddaden

Clearing the milestone because it's not yet read to merge and https://github.com/crystal-lang/crystal/pull/14766 might provide a better solution.

straight-shoota avatar Jul 02 '24 08:07 straight-shoota