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