This is a proof of concept STM system implemented in a quite naive and likely buggy way. I wrote this mostly to get an idea how such things can be implemented, and have not used it since.
For any serious Common Lisp STM needs the cl-stm system is probably much better. That said, since I dropped it on GitHub I might just as well document it. I suppose there is a possibility that it might be informational to someone.
This system is much simpler than aforementioned cl-stm because it puts more responsibility on the user. While cl-stm uses the code walker to capture side effects into transactions, this system assumes all information is shared through special references to immutable data, called tvars following Haskell example. Of course, unlike in Haskell it is not possible to enforce neither the immutability of referenced data or that tvars will be accessed though transaction logs.
Immutable datastructures are available though, for example, FSet library.
Transactions as implemented here are in principle composable.
sb-thread is used directly.
bordeaux-threads does not have non-waiting
Tests (what few there are) require also:
Note that transaction failure is communicated using signals. Creating a
t condition handler will break them.
make-tvar &optional value
Create a transactional variable with an optional initial value.
get-tvar tvar &optional transaction-log wrapping-logs
Mostly synonymous. Optional arguments of the first form are used internally and default to thread-local special bindings. Access contents of a transactional variable. Can be used outside of transaction.
put-tvar tvar new-value &optional transaction-log wrapping-logs
(setf tvar-ref) new-value tvar
Mostly synonymous. Optional arguments of the first form are used internally and default to thread-local special bindings. Sets contents of a transactional variable. Can be used outside of transaction.
Restart transaction. Will suspend the thread until one of the tvars which were accessed so far in the transaction is changed.
with-retry-transaction &body body
Execute body within transaction. It will be retried if any tvars which were read during it have been changed.
with-orelse-transaction &body (alternative-body)*
Execute bodies withing orelse transaction, ie. if the first fails, the second is immediately attempted and so on. If all fail and there was at least one explicit (retry-transaction) the thread will be suspended until some referenced tvar changes.