1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#pragma once
14
15#include <assert.h>
16#include <limits.h>
17#include <stddef.h>
18
19/** \brief Atomically increment an integer, accounting for possible overflow.
20 *
21 * @param x Pointer to integer to increment.
22 * @param[out] oldval Previous value of the integer. May be written to even if
23 *   the increment fails.
24 * @param success_memorder The memory order to enforce
25 * @return 0 if the increment succeeds, non-zero if it would cause an overflow.
26 */
27static inline int sync_atomic_increment_safe(volatile int *x, int *oldval, int success_memorder) {
28    assert(x != NULL);
29    assert(oldval != NULL);
30    do {
31        *oldval = *x;
32        if (*oldval == INT_MAX) {
33            /* We would overflow */
34            return -1;
35        }
36    } while (!__atomic_compare_exchange_n(x, oldval, *oldval + 1, 1, success_memorder, __ATOMIC_RELAXED));
37    return 0;
38}
39
40/** \brief Atomically decrement an integer, accounting for possible overflow.
41 *
42 * @param x Pointer to integer to decrement.
43 * @param[out] oldval Previous value of the integer. May be written to even if
44 *   the decrement fails.
45 * @param success_memorder The memory order to enforce if the decrement is successful
46 * @return 0 if the decrement succeeds, non-zero if it would cause an overflow.
47 */
48static inline int sync_atomic_decrement_safe(volatile int *x, int *oldval, int success_memorder) {
49    assert(x != NULL);
50    assert(oldval != NULL);
51    do {
52        *oldval = *x;
53        if (*oldval == INT_MIN) {
54            /* We would overflow */
55            return -1;
56        }
57    } while (!__atomic_compare_exchange_n(x, oldval, *oldval - 1, 1, success_memorder, __ATOMIC_RELAXED));
58    return 0;
59}
60
61/* Atomically increment an integer and return its new value. */
62static inline int sync_atomic_increment(volatile int *x, int memorder) {
63    return __atomic_add_fetch(x, 1, memorder);
64}
65
66/* Atomically decrement an integer and return its new value. */
67static inline int sync_atomic_decrement(volatile int *x, int memorder) {
68    return __atomic_sub_fetch(x, 1, memorder);
69}
70
71