My laptop's SSD died this morning. I had some work from yesterday committed to the git repo on it, but not pushed as it didn't build. Luckily I was able to get that off the SSD, which is now a read-only drive -- even mounting it fails with fsck write errors.

Wish I'd realized the SSD was dying before the day before my trip to Nicaragua.. Getting back to a useful laptop used most of my time and energy today.

I did manage to fix transfers to not block the rest of the assistant's threads. Problem was that, without Haskell's threaded runtime, waiting on something like a rsync command blocks all threads. To fix this, transfers now are run in separate processes.

Also added code to allow multiple transfers to run at once. Each transfer takes up a slot, with the number of free slots tracked by a QSemN. This allows the transfer starting thread to block until a slot frees up, and then run the transfer.

This needs to be extended to be aware of transfers initiated by remotes. The transfer watcher thread should detect those starting and stopping and update the QSemN accordingly. It would also be nice if transfers initiated by remotes would be delayed when there are no free slots for them ... but I have not thought of a good way to do that.

There's a bug somewhere in the new transfer code, when two transfers are queued close together, the second one is lost and doesn't happen. Would debug this, but I'm spent for the day.