I’ve been trying to wrap my head around concurrency for several months now and I have an idea. At least it could become a good primer and maybe even a piece of documentation.
The goal is to write a program that is a simple chat server: a client writes a string to a socket and all the clients receive it. There are however some additional limitations.
- The program should exit clean on SIGINT (we’d like to illustrate signal handling).
- The program should not raise any exceptions during normal operation and the clean shutdown path.
- The client should be able to kick any client (closing connections from server side).
- The client should be able to set a read timeout for any client (fiber management).
- The client should be able to pipe a string to an external program run by the server (child processes).
- The program should reap all its child processes.
- No sleep loops (the purpose is the async mindset).
- The program shouldn’t avoid static analysis (e.g. pipes instead of channels).
Bonus points: the program should allow intraspection, for example listing its fibers to the client on command.
Nice and easy, right?
2 Likes
I am giving it a shot, yes. So far I’ve modified the switching logic a bit, something like this:
require "./crystal_scheduler/wakeup_parent"
class Crystal::Scheduler
private def forbid_double_reschedule(me : Jiber) : Nil
fatal_resume_error me, "Reschedule called while handling an interrupt" if me.interrupt_inside_handler
end
private def detect_and_handle_interrupt(me : Jiber) : Nil
event = me.interrupt_event
return if event.is_a? Nothing
me.interrupt_event = nothing
me.interrupt_inside_handler = true
case event
when Jiber::Event::Shutdown then me.handle_shutdown
when Jiber::Event::Finished then me.handle_finished event.jid
else me.handle_interrupt event
end
me.interrupt_inside_handler = false
resume event.origin
rescue x
fatal_resume_error me, "Unhandled exception during interrupt handling: #{x.inspect_with_backtrace}"
end
protected def reschedule : Nil
current = @current
forbid_double_reschedule current if current.is_a? Jiber
if current.dead?
@runnables.delete current
check_and_wakeup_my_parent current if current.is_a? Jiber
end
previous_def
detect_and_handle_interrupt current if current.is_a? Jiber
end
end
Jiber
is a Fiber
subclass with additional bits that allow some interesting properties like a parent-child relationship, waiting on children and signal-like interrupts.
It’s a bit too early to go into details, but imo there is some promising potential.
2 Likes