summaryrefslogtreecommitdiff
path: root/libguile/vm-engine.c
diff options
context:
space:
mode:
authorMark H Weaver <mhw@netris.org>2018-09-27 01:00:11 -0400
committerMark H Weaver <mhw@netris.org>2018-10-05 18:19:34 -0400
commit2d09e0513fc11e2305077ba3653f6e4c2f266ddb (patch)
treea001c9cac86a371dfcdebe9ec5801a9c4ea28fa3 /libguile/vm-engine.c
parent3ee7673cc98d2acca3144a81bfbe1351b2bbe0fd (diff)
downloadguile-2d09e0513fc11e2305077ba3653f6e4c2f266ddb.tar.gz
Fix 'atomic-box-compare-and-swap!'.
Fixes <https://bugs.gnu.org/32786>. 'scm_atomic_compare_and_swap_scm' is a thin wrapper around 'atomic_compare_exchange_weak' (where available), and therefore it may spuriously fail on some platforms, leaving the atomic object unchanged even when the observed value is equal to the expected value. Since 'scm_atomic_compare_and_swap_scm' returns both a boolean result and the observed value, the caller is able to detect spurious failures when using that API. 'atomic-box-compare-and-swap!' presents a simpler API, returning only the observed value. The documentation advises callers to assume that the exchange succeeded if the observed value is 'eq?' to the expected value. It's therefore not possible to report spurious failures with this API. 'atomic-box-compare-and-swap!' uses 'scm_atomic_compare_and_swap_scm', and prior to this commit would simply ignore the boolean result and return the observed value. In case of spurious failures, the caller would legitimately conclude that the exchange had succeeded. With this commit, 'atomic-box-compare-and-swap!' now retries in case of spurious failures. * libguile/atomic.c (scm_atomic_box_compare_and_swap_x): If 'scm_atomic_compare_and_swap_scm' returns false and the observed value is equal to 'expected', then try again. * libguile/vm-engine.c (atomic-box-compare-and-swap!): Ditto.
Diffstat (limited to 'libguile/vm-engine.c')
-rw-r--r--libguile/vm-engine.c19
1 files changed, 14 insertions, 5 deletions
diff --git a/libguile/vm-engine.c b/libguile/vm-engine.c
index 19ff3e498..53d7f3522 100644
--- a/libguile/vm-engine.c
+++ b/libguile/vm-engine.c
@@ -3868,16 +3868,25 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
{
scm_t_uint16 dst, box;
scm_t_uint32 expected, desired;
- SCM scm_box, scm_expected;
+ SCM scm_box, scm_expected, scm_result;
UNPACK_12_12 (op, dst, box);
UNPACK_24 (ip[1], expected);
UNPACK_24 (ip[2], desired);
scm_box = SP_REF (box);
VM_VALIDATE_ATOMIC_BOX (scm_box, "atomic-box-compare-and-swap!");
- scm_expected = SP_REF (expected);
- scm_atomic_compare_and_swap_scm (scm_atomic_box_loc (scm_box),
- &scm_expected, SP_REF (desired));
- SP_SET (dst, scm_expected);
+ scm_result = scm_expected = SP_REF (expected);
+ while (!scm_atomic_compare_and_swap_scm (scm_atomic_box_loc (scm_box),
+ &scm_result, SP_REF (desired))
+ && scm_is_eq (scm_result, scm_expected))
+ {
+ /* 'scm_atomic_compare_and_swap_scm' has spuriously failed,
+ i.e. it has returned 0 to indicate failure, although the
+ observed value is 'eq?' to EXPECTED. In this case, we *must*
+ try again, because the API of 'atomic-box-compare-and-swap!'
+ provides no way to indicate to the caller that the exchange
+ failed when the observed value is 'eq?' to EXPECTED. */
+ }
+ SP_SET (dst, scm_result);
NEXT (3);
}