Hello, I would like to ask you about the following hack:
def wakeup(ev : Crystal::Event)
timeout = 0.seconds
zero_timeout = LibC::Timeval.new(
tv_sec: timeout.total_seconds.to_i,
tv_usec: timeout.nanoseconds / 1_000
)
LibEvent2.event_add ev.@event, pointerof(zero_timeout)
end
def wakeup(ev : Nil)
end
fib = spawn { sleep 1; p fiber: Time.now }
p start: Time.now
sleep 0.1
p wakeup: Time.now
wakeup fib.@resume_event
sleep 2
p fin: Time.now
It works, however I’d like your opinion on two things:
Is it okay in general to do this?
Isn’t it worth adding to Fiber class, not slapping on the side?
My usecase is simple: I would like to exit main() only after all spawned fibers return. I can close channels, I can close sockets, I can kill children, but I can’t seem to find a clean way to interrupt a sleep.
There is a Channel.select that makes it possible to operate on multiple channels from a single blocking call, but that is not the point, as I’m interested in crashing the sleep call itself, much like I can crash the IO#read call by closing the io from another part of the program. Hope that makes sense.
Previously, there was Fiber#sleep(timeout) and you could simply call that with 0.seconds to wake up a fiber. Now that sleep is implemented on Scheduler, this is no longer available.
But you can yield to another fiber (and the current one is enqueued). This should result in mostly similar behaviour, the only difference is that the fiber is swapped immediately, instead of at the next time the current fiber pauses.
If you want to keep it that way, your wakeup implementation could just simply call fiber.resume_event.add(0.seconds).