(Sorry for the clickbait title, I’m not sure how to frame this better.)
I’ve been using Crystal for a year now, and so far I’m pretty much ready to give up on the most promising usecase that got me in: a tool to let me develop daemons. It’s got many things going for it: a really well done static analysis, a wonderful toolchain and many more, sadly one crucial piece is missing, being the concurrency workflow that I would be able to use.
To demonstrate the problem let’s look at the Socket
, or IO::Evented
: once I call io.read
, there is no documented way for me to cancel this “blocking call”, short of closing the io
. Why would I need that? Well, first thing that comes to mind is something like a graceful fiber shutdown, another is data isolation, let me explain.
Each socket is bi-directional, you use the same object to call read
and write
on, so if I need asynchronous operation – meaning there is no set order of instructions, rather a number of callbacks that should handle independent events – I have two basic choices: a) spawn two fibers that share the same io
, or 2) start drowning in emulating select()
atop of fibers that emulate threads atop of select()
.
Spawning two fibers looks easy until you realise that it’s your job to synchronise the state and error handling: you can’t return from a fiber, and you can’t rescue an exception from another fiber, so now you have to have a third fiber that sits atop of a channel to read the last words of your reader and writer to analyze them and hopefully make sense of what just happened.
At some point in this journey I’ve realized a symmetry between the fibers/channels and processes/pipes that make the userspace concurrent. I guess there’s an argument for the whole fork
model to also be considered a form of CSP, but guys!
- the kernel has one more crucial tool: signals
- you can’t fork your way out of any problem, that’s why
select()
exists!
Anyway, I’ve started looking around and tinkering with the scheduler, and I’ve come up with a set of ideas to implement a way to “interrupt” a “blocking call” so that I can have non-shareable data and some code around it and a way to use all that and not go insane.
(And then I’ve read Pony tutorial. I didn’t write any pony yet, but as I was reading about it, all my boxes were being checked, it felt like God was answering my prayers lol.)
So, now I have ideas for Crystal that I was hoping to make into some uploadable code, but wasn’t able to, and I’m not sure I would be anymore, but if anyone is interested, I’d like to somehow pass it on. I have an application built with these patches that seem to work, I can share the code, but I think it needs my comments. If anyone is interested, I’m available (and eager) for a call and a screenshare, otherwise maybe I’ll find the inspiration to hack together some sort of an article.
TL;DR: maybe that’s because I’m missing something really bad, but I don’t see how I can write a correct concurrent program using vanilla crystal.