Concurrency challenge

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.

  1. The program should exit clean on SIGINT (we’d like to illustrate signal handling).
  2. The program should not raise any exceptions during normal operation and the clean shutdown path.
  3. The client should be able to kick any client (closing connections from server side).
  4. The client should be able to set a read timeout for any client (fiber management).
  5. The client should be able to pipe a string to an external program run by the server (child processes).
  6. The program should reap all its child processes.
  7. No sleep loops (the purpose is the async mindset).
  8. 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

Give it a shot! :)

1 Like

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