Fiber.wakeup advice

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:

  1. Is it okay in general to do this?
  2. 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.

Will this work?

mainch = Channel::Buffered(Nil).new(1)
timeoutch = Channel::Buffered(Symbol).new(1)
spawn { sleep 1; timeoutch.send(:timeout) }
spawn {
  loop do 
    # I don't remember the return values.
    ? = Channel.receive_first timeoutch, otherchannels... 
    break if timeout???
  end
  mainch.send nil # Signal main that fiber completed.
}

mainch.retrieve

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).

Thanks! The original question is mostly moot for me, I had to dig around scheduler and event loop and now I think I understand what’s going on.