-
Notifications
You must be signed in to change notification settings - Fork 413
/
atomics.chpl
133 lines (118 loc) · 4.31 KB
/
atomics.chpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Atomics
/*
This primer illustrates Chapel's atomic variables. For more information
on Chapel's atomics, see the :ref:`Chapel Language Specification
<chapel-spec>`.
*/
// The ``atomic`` keyword is a type qualifiers that can be applied to
// the following Chapel primitive types to declare atomic variables:
//
// - ``bool``
// - ``int`` (all supported sizes)
// - ``uint`` (all supported sizes)
// - ``real`` (all supported sizes)
//
// Atomic variables support a limited number of operations that are
// currently implemented as methods.
//
// Atomic variables can only be used or assigned via the ``read()`` and
// ``write()`` methods, respectively.
//
config const n = 31;
const R = 1..n;
var x: atomic int;
x.write(n);
if x.read() != n then
halt("Error: x (", x.read(), ") != n (", n, ")");
// All atomic types support atomic ``exchange()``,
// ``compareExchangeStrong()``, ``compareExchangeWeak()``, and
// ``compareExchange()`` (same as ``compareExchangeStrong()``).
//
// The ``exchange()`` method atomically swaps the old value with the given
// argument and returns the old value. The ``compareExchange()``,
// ``compareExchangeStrong()`` and ``compareExchangeWeak()`` methods only
// perform the swap if the current value is equal to the first argument,
// returning ``true`` if the exchange was performed See the :ref:`Chapel
// Language Specification <chapel-spec>` for the difference between the weak and
// strong varieties.
//
// In the following example, ``n`` parallel tasks are created via a
// ``coforall`` statement. Each task tries to set the current value of ``x``
// to ``id-1``, but only one will succeed.
//
var numFound: atomic int;
coforall id in R {
numFound.add(x.compareExchangeWeak(n, id-1));
}
if numFound.read() != 1 then
halt("Error: numFound != 1 (", numFound.read(), ")");
if x.read() == n then
halt("Error: x == n");
// As a convenience, ``atomic bools`` also support ``testAndSet()`` and
// ``clear()``. The ``testAndSet()`` method atomically reads the value of
// the variable, sets it to ``true``, then returns the original value. The
// ``clear()`` method atomically sets the value to ``false``.
//
// In the following example, we create ``n`` tasks and each task calls
// ``testAndSet()`` on the atomic variable named ``flag`` and saves the
// result in a unique element of the ``result`` array.
//
var flag: atomic bool;
var result: [R] bool;
coforall r in result do
r = flag.testAndSet();
// When the ``coforall`` is complete, only one of the above ``testAndSet()``
// calls should have returned ``false``.
//
var found = false;
for r in result {
if !r then
if found then
halt("Error: found multiple times!");
else
found = true;
}
if !found then
halt("Error: not found!");
flag.clear();
// The integral and ``real`` atomic types also support the following atomic
// fetch and non-fetching operations:
//
// - ``fetchAdd()`` and ``add()``
// - ``fetchSub()`` and ``sub()``
// - ``fetchOr()`` and ``or()`` (bit-wise) (integral types only)
// - ``fetchAnd()`` and ``and()`` (bit-wise) (integral types only)
// - ``fetchXor()`` and ``xor()`` (bit-wise) (integral types only)
//
// Each of the above atomically reads the variable, stores the result
// of the operation (``+``, ``-``, ``|``, ``&``, ``^``) using the value and the
// method argument, then, for the fetchOps functions, returns the original
// value.
//
// In the following example, we create ``n`` tasks to atomically increment
// the atomic variable ``a`` with the square of the task's given ``id``.
//
var a: atomic int;
coforall id in R do a.add(id*id);
// The sum of this finite series should be ``n(n+1)*(2n+1)/6``
//
var expected = n*(n+1)*(2*n+1)/6;
if a.read() != expected then
halt("Error: a=", a.read(), " (should be ", expected, ")");
// In the following example, we create ``n`` tasks to atomically increment
// the atomic variable ``a`` with the square of the task's given ``id``. The
// sum of this finite series is ``n(n+1)*(2n+1)/6``. If the returned value
// is the expected value less ``id*id`` (last task to arrive), check the
// results.
//
a.write(0);
const sumOfSq = n*(n+1)*(2*n+1)/6;
coforall id in R {
const mySq = id*id;
const last = a.fetchAdd(mySq);
if sumOfSq-mySq == last {
const t = a.read();
if t != n*(n+1)*(2*n+1)/6 then
halt("Error: a=", t, " (should be ", sumOfSq, ") id=", id);
}
}