Skip to content

Commit 492a608

Browse files
committed
Add documentation for atomicint.
Together with all of the atomic integer operations.
1 parent 4c3df89 commit 492a608

File tree

1 file changed

+264
-0
lines changed

1 file changed

+264
-0
lines changed

doc/Type/atomicint.pod6

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
=begin pod
2+
3+
=TITLE class atomicint
4+
5+
=SUBTITLE Integer (native storage at the platform's atomic operation size)
6+
7+
class atomicint is Int is repr('P6int') { }
8+
9+
An C<atomicint> is a native integer sized such that CPU-provided atomic
10+
operations can be performed upon it. On a 32-bit CPU it will typically be
11+
32 bits in size, and on an a 64-bit CPU it will typically be 64 bits in size.
12+
It exists to allow writing portable code that uses atomic operations.
13+
14+
# Would typically only work on a 64-bit machine and VM build.
15+
my int64 $active = 0;
16+
$active⚛++;
17+
18+
# Would typically only work on a 32-bit machine and VM build.
19+
my int32 $active = 0;
20+
$active⚛++;
21+
22+
# Will work portably, though can only portably assume range of 32 bits.
23+
my atomicint $active = 0;
24+
$active⚛++;
25+
26+
The use of the C<atomicint> type does not automatically provide atomicity; it
27+
must be used in conjunction with the atomic operations.
28+
29+
# Correct (will always output 80000)
30+
my atomicint $total = 0;
31+
start { for ^20000 { $total⚛++ } } xx 4;
32+
say $total;
33+
34+
# Either works correctly or dies, depending on platform.
35+
my int $total = 0;
36+
start { for ^20000 { $total⚛++ } } xx 4;
37+
say $total;
38+
39+
# Wrong due to lack of use of the atomic increment operator.
40+
my atomicint $total = 0;
41+
start { for ^20000 { $total++ } } xx 4;
42+
say $total;
43+
44+
=head1 Routines
45+
46+
=head2 atomic-assign
47+
48+
Defined as:
49+
50+
multi sub atomic-assign(atomcint $ is rw, int $value)
51+
multi sub atomic-assign(atomcint $ is rw, Int() $value)
52+
53+
Performs an atomic assignment to a native integer, which may be in a lexical,
54+
attribute, or native array element. If C<$value> cannot unbox to a 64-bit
55+
native integer due to being too large, an exception will be thrown. If the
56+
size of C<atomicint> is only 32 bits, then an out of range C<$value> will be
57+
silently truncated. The C<atomic-assign> routine ensures that any required
58+
barriers are performed such that the changed value will be "published" to
59+
other threads.
60+
61+
=head2 atomic-fetch
62+
63+
Defined as:
64+
65+
multi sub atomic-fetch(atomcint $ is rw)
66+
67+
Performs an atomic read of a native integer, which may live in a lexical,
68+
attribute, or native array element. Using this routine instead of simply
69+
using the variable ensures that the latest update to the variable from other
70+
threads will be seen, both by doing any required hardware barriers and also
71+
preventing the compiler from lifting reads. For example:
72+
73+
my atomicint $i = 0;
74+
start { atomic-assign($i, 1) }
75+
while atomc-fetch($i) == 0 { }
76+
77+
Is certain to terminate, while in:
78+
79+
my atomicint $i = 0;
80+
start { atomic-assign($i, 1) }
81+
while $i == 0 { }
82+
83+
It would be legal for a compiler to observe that C<$i> is not updated in the
84+
loop, and so lift the read out of the loop, thus causing the program to never
85+
terminate.
86+
87+
=head2 atomic-inc
88+
89+
Defined as:
90+
91+
multi sub atomic-inc(atomicint $ is rw)
92+
93+
Performs an atomic increment on a native integer. This will be performed using
94+
hardware-provided atomic operations. Since the operation is atomic, it is safe
95+
to use without acquiring a lock. Returns the value as seen before incrementing
96+
it. Overflow will wrap around silently.
97+
98+
=head2 atomic-dec
99+
100+
Defined as:
101+
102+
multi sub atomic-dec(atomicint $ is rw)
103+
104+
Performs an atomic decrement on a native integer. This will be performed using
105+
hardware-provided atomic operations. Since the operation is atomic, it is safe
106+
to use without acquiring a lock. Returns the value as seen before decrementing
107+
it. Overflow will wrap around silently.
108+
109+
=head2 atomic-add
110+
111+
Defined as:
112+
113+
multi sub atomic-add(atomicint $ is rw, int $value)
114+
multi sub atomic-add(atomicint $ is rw, Int() $value)
115+
116+
Performs an atomic addition on a native integer. This will be performed using
117+
hardware-provided atomic operations. Since the operation is atomic, it is safe
118+
to use without acquiring a lock. Returns the value as seen before the addition
119+
was performed. Overflow will wrap around silently. If C<$value> is too big to
120+
unbox to a 64-bit integer, an exception will be thrown. If C<$value> otherwise
121+
overflows C<atomicsize> then it will be silently truncated before the addition
122+
is performed.
123+
124+
=head2 cas
125+
126+
Defined as:
127+
128+
multi sub cas(atomicint $target is rw, int $expected, int $value)
129+
multi sub cas(atomicint $target is rw, Int() $expected, Int() $value)
130+
multi sub cas(atomicint $target is rw, &operation)
131+
132+
Performs an atomic compare and swap of the native integer value in location
133+
C<$target>. The first two forms have semantics like:
134+
135+
my int $seen = $target;
136+
if $seen == $expected {
137+
$target = $value;
138+
}
139+
return $seen;
140+
141+
Except it is performed as a single hardware-supported atomic instruction, as
142+
if all memory access to C<$target> were blocked while it took place. Therefore
143+
it is safe to attempt the operation from multiple threads without any other
144+
synchronization. For example:
145+
146+
my int $master = 0;
147+
await start {
148+
if cas($master, 0, 1) == 0 {
149+
say "Master!"
150+
}
151+
} xx 4
152+
153+
Will reliably only ever print C<Master!> one time, as only one of the threads
154+
will be successful in changing the 0 into a 1.
155+
156+
Both C<$expected> and C<$value> will be coerced to C<Int> and unboxed if
157+
needed. An exception will be thrown if they value cannot be represented as a
158+
64-bit integer. If the size of C<atomicint> is only 32 bits then the values
159+
will be silently truncated to this size.
160+
161+
The third form, taking a code object, will first do an atomic fetch of the
162+
current value and invoke the code object with it. It will then try to do an
163+
atomic compare and swap of the target, using the value passed to the code
164+
object as C<$exepcted> and the result of the code object as C<$value>. If
165+
this fails, it will read the latest value, and retry, until a CAS operation
166+
succeeds. Therefore, an atomic multiply of an C<atomicint> C<$i> by 2 could
167+
be implemented as:
168+
169+
cas $i, -> int $current { $current * 2 }
170+
171+
If another thread changed the value while C<$current * 2> was being calculated
172+
then the block would be called again with the latest value for a futher
173+
attempt, and this would be repeated until success.
174+
175+
=head1 Operators
176+
177+
=head2 infix ⚛=
178+
179+
multi sub infix:<⚛=>(atomcint $ is rw, int $value)
180+
multi sub infix:<⚛=>(atomcint $ is rw, Int() $value)
181+
182+
Performs an atomic assignment to a native integer, which may be in a lexical,
183+
attribute, or native array element. If C<$value> cannot unbox to a 64-bit
184+
native integer due to being too large, an exception will be thrown. If the
185+
size of C<atomicint> is only 32 bits, then an out of range C<$value> will be
186+
silently truncated. The C<⚛=> operator ensures that any required barriers are
187+
performed such that the changed value will be "published" to other threads.
188+
189+
=head2 prefix ⚛
190+
191+
multi sub prefix:<⚛>(atomcint $ is rw)
192+
193+
Performs an atomic read of a native integer, which may live in a lexical,
194+
attribute, or native array element. Using this operator instead of simply
195+
using the variable ensures that the latest update to the variable from other
196+
threads will be seen, both by doing any required hardware barriers and also
197+
preventing the compiler from lifting reads. For example:
198+
199+
my atomicint $i = 0;
200+
start { $i ⚛= 1 }
201+
while ⚛$i == 0 { }
202+
203+
Is certain to terminate, while in:
204+
205+
my atomicint $i = 0;
206+
start { $i ⚛= 1 }
207+
while $i == 0 { }
208+
209+
It would be legal for a compiler to observe that C<$i> is not updated in the
210+
loop, and so lift the read out of the loop, thus causing the program to never
211+
terminate.
212+
213+
=head2 prefix ⚛++
214+
215+
multi sub prefix:<⚛++>(atomcint $ is rw)
216+
217+
Performs an atomic increment on a native integer. This will be performed using
218+
hardware-provided atomic operations. Since the operation is atomic, it is safe
219+
to use without acquiring a lock. Returns the value resulting from the
220+
increment. Overflow will wrap around silently.
221+
222+
=head2 postfix ⚛++
223+
224+
multi sub postfix:<⚛++>(atomcint $ is rw)
225+
226+
Performs an atomic increment on a native integer. This will be performed using
227+
hardware-provided atomic operations. Since the operation is atomic, it is safe
228+
to use without acquiring a lock. Returns the value as seen before incrementing
229+
it. Overflow will wrap around silently.
230+
231+
=head2 prefix ⚛--
232+
233+
multi sub prefix:<⚛-->(atomcint $ is rw)
234+
235+
Performs an atomic decrement on a native integer. This will be performed using
236+
hardware-provided atomic operations. Since the operation is atomic, it is safe
237+
to use without acquiring a lock. Returns the value resulting from the
238+
decrement. Overflow will wrap around silently.
239+
240+
=head2 postfix ⚛--
241+
242+
multi sub postfix:<⚛-->(atomcint $ is rw)
243+
244+
Performs an atomic decrement on a native integer. This will be performed using
245+
hardware-provided atomic operations. Since the operation is atomic, it is safe
246+
to use without acquiring a lock. Returns the value as seen before decrementing
247+
it. Overflow will wrap around silently.
248+
249+
=head2 infix ⚛+=
250+
251+
Defined as:
252+
253+
multi sub infix:<⚛+=>(atomicint $ is rw, int $value)
254+
multi sub infix:<⚛+=>(atomicint $ is rw, Int() $value)
255+
256+
Performs an atomic addition on a native integer. This will be performed using
257+
hardware-provided atomic operations. Since the operation is atomic, it is safe
258+
to use without acquiring a lock. Evaluates to the result of the addition.
259+
Overflow will wrap around silently. If C<$value> is too big to unbox to a
260+
64-bit integer, an exception will be thrown. If C<$value> otherwise overflows
261+
C<atomicsize> then it will be silently truncated before the addition is
262+
performed.
263+
264+
=end pod

0 commit comments

Comments
 (0)