Under the hood: Transactions

The final major “under-the-hood enhancement” in Toves’s design worth discussing is the transactional-based access scheme. In Logisim, there is a persistent bug where a circuit’s simulation occasionally messes up. It seems to be quite rare, even for large circuits, and it happens unpredictably. My best guess is that it has to do with multithreaded access.

To avoid multithreading issues, Toves uses the concept of a “transaction” from databases. Its key data structures (the circuit layout, the simulation engine, the overall project) provide no way to access them directly. Instead, you have to nest all access within a transaction. In the program, it looks like this:

Transaction xn = new Transaction();
ILayoutAccess layout = xn.RequestReadAccess(layoutStructure);
ISimulationAccess sim = xn.RequestWriteAccess(simulationStructure);
using (xn.Start()) {
    // code using layout and sim to read layoutStructure and modify simulationStructure

The ‘layoutStructure‘ object actually doesn’t even have methods like ‘GetWires‘ or ‘AddWire‘. Such methods are provided in the ILayoutAccess interface, and such methods always confirm that the transaction is still live before proceeding with their work.

The example above shows how a transaction can request simultaneous locks on different structures. If there prove to be issues with deadlock between transactions, these will hopefully be able to be addressed by making Transaction’s Start method more sophisticated in the order in which it obtains locks.

(I suspect that the distinction between read access and write access is a matter of overengineering: In practice, it probably happens that two threads never end up getting read access simultaneously.)

This should degrade performance somewhat, as repainting the circuit (which happens often) will never happen at the same time as stepping the circuit (which happens extremely often). But this simultaneous access is exactly what makes bugs possible – and in practice I believe the interface events like repainting the circuit happen at most 50 times per second, whereas the simulation-stepping transactions happen upwards of 1,000 times per second. In any case, the locking can easily be removed (by making Transaction’s Start method do absolutely nothing) if performance proves problematic – though that requires more fine-grained concurrency that may actually hurt performance even more than the exclusive access.