Design review

Let's take a moment to review the journey we've been through.

Lockless with channel serialization

Our first implementation of a multithreaded ticket store used:

  • a single long-lived thread (server), to hold the shared state
  • multiple clients sending requests to it via channels from their own threads.

No locking of the state was necessary, since the server was the only one modifying the state. That's because the "inbox" channel naturally serialized incoming requests: the server would process them one by one.
We've already discussed the limitations of this approach when it comes to patching behaviour, but we didn't discuss the performance implications of the original design: the server could only process one request at a time, including reads.

Fine-grained locking

We then moved to a more sophisticated design, where each ticket was protected by its own lock and clients could independently decide if they wanted to read or atomically modify a ticket, acquiring the appropriate lock.

This design allows for better parallelism (i.e. multiple clients can read tickets at the same time), but it is still fundamentally serial: the server processes commands one by one. In particular, it hands out locks to clients one by one.

Could we remove the channels entirely and allow clients to directly access the TicketStore, relying exclusively on locks to synchronize access?

Removing channels

We have two problems to solve:

  • Sharing TicketStore across threads
  • Synchronizing access to the store

Sharing TicketStore across threads

We want all threads to refer to the same state, otherwise we don't really have a multithreaded system—we're just running multiple single-threaded systems in parallel.
We've already encountered this problem when we tried to share a lock across threads: we can use an Arc.

Synchronizing access to the store

There is one interaction that's still lockless thanks to the serialization provided by the channels: inserting (or removing) a ticket from the store.
If we remove the channels, we need to introduce (another) lock to synchronize access to the TicketStore itself.

If we use a Mutex, then it makes no sense to use an additional RwLock for each ticket: the Mutex will already serialize access to the entire store, so we wouldn't be able to read tickets in parallel anyway.
If we use a RwLock, instead, we can read tickets in parallel. We just need to pause all reads while inserting or removing a ticket.

Let's go down this path and see where it leads us.

Exercise

The exercise for this section is located in 07_threads/13_without_channels