Each hash-table
access operation is thread-safe and atomic, but there is no guarantee of atomicity between access operations.
The modify macros (for example incf
) all expand to two access operations, reading the value and writing the modified value, and are therefore not atomic. They need to be either done while holding a lock, or using modify-hash.
Another common operation is "ensuring an entry", that is reading and, if reading fails, adding a value to the table. For example:
(or (gethash key hash-table)
(setf (gethash key hash-table) (construct-new-value)))
If two threads do that in parallel, one of them may end up with a value that is not in the table. One solution is to lock that table:
(with-hash-table-locked hash-table
(or (gethash key hash-table)
(setf (gethash key hash-table) (construct-new-value))))
However that always locks the table, which is inefficient. The correct way to do it is either to do:
(or (gethash key hash-table) ; first try without the lock
(with-hash-table-locked hash-table
(or (gethash key hash-table) ; check again inside the lock
(setf (gethash key hash-table) (construct-new-value)))))
or use gethash-ensuring or with-ensuring-gethash.
LispWorks User Guide and Reference Manual - 20 Sep 2017