
| Current Path : /usr/share/gap/doc/hpc/ |
Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64 |
| Current File : //usr/share/gap/doc/hpc/chap5.txt |
[1X5 [33X[0;0YAtomic objects[133X[101X
[33X[0;0YHPC-GAP provides a number of atomic object types. These can be accessed by
multiple threads concurrently without requiring explicit synchronization,
but can have non-deterministic behavior for complex operations. Atomic lists
are fixed-size lists; they can be assigned to and read from like normal
plain lists. Atomic records are atomic versions of plain records. Unlike
plain records, though, it is not possible to delete elements from an atomic
record. The primary use of atomic lists and records is to facilitate storing
the result of idempotent operations and to support certain low-level
operations. Atomic lists and records can have three different replacement
policies: write-once, strict write-once, and rewritable. The replacement
policy determines whether an already assigned element can be changed. The
write-once policy allows elements to be assigned only once, with subsequent
assignments being ignored; the strict write-once policy allows elements also
to be assigned only once, but subsequent assignments will raise an error;
the rewritable policy allows elements to be assigned different values
repeatedly. The default for new atomic objects is to be rewritable.
Thread-local records are variants of plain records that are replicated on a
per-thread basis.[133X
[1X5.1 [33X[0;0YAtomic lists[133X[101X
[33X[0;0YAtomic lists are created using the [10XAtomicList[110X or [10XFixedAtomicList[110X functions.
After creation, they can be used exactly like any other list, except that
atomic lists created with [10XFixedAtomicList[110X cannot be resized. Their contents
can also be read as normal plain lists using [10XFromAtomicList[110X.[133X
[4X[32X Example [32X[104X
[4X[25Xgap>[125X [27Xa := AtomicList([1,2,4]);[127X[104X
[4X[28X<atomic list of size 3>[128X[104X
[4X[25Xgap>[125X [27XWaitTask(RunTask(function() a[1] := a[1] + a[2]; end));[127X[104X
[4X[25Xgap>[125X [27Xa[1];[127X[104X
[4X[28X3[128X[104X
[4X[25Xgap>[125X [27XFromAtomicList(a);[127X[104X
[4X[28X[ 3, 2, 4 ][128X[104X
[4X[32X[104X
[33X[0;0YBecause multiple threads can read and write the list concurrently without
synchronization, the results of modifying the list may be non-deterministic.
It is faster to write to fixed atomic lists than to a resizable atomic list.[133X
[1X5.1-1 AtomicList[101X
[33X[1;0Y[29X[2XAtomicList[102X( [3Xlist[103X ) [32X function[133X
[33X[1;0Y[29X[2XAtomicList[102X( [3Xcount[103X, [3Xobj[103X ) [32X function[133X
[33X[0;0Y[2XAtomicList[102X is used to create a new atomic list. It takes either a plain list
as an argument, in which case it will create a new atomic list of the same
size, populated by the same elements; or it takes a count and an object
argument. In that case, it creates an atomic list with [3Xcount[103X elements, each
set to the value of [3Xobj[103X.[133X
[4X[32X Example [32X[104X
[4X[25Xgap>[125X [27Xal := AtomicList([3, 1, 4]);[127X[104X
[4X[28X<atomic list of size 3>[128X[104X
[4X[25Xgap>[125X [27Xal[3];[127X[104X
[4X[28X4[128X[104X
[4X[25Xgap>[125X [27Xal := AtomicList(10, `"alpha");[127X[104X
[4X[28X<atomic list of size 10>[128X[104X
[4X[25Xgap>[125X [27Xal[3];[127X[104X
[4X[28X"alpha"[128X[104X
[4X[25Xgap>[125X [27XWaitTask(RunTask(function() al[3] := `"beta"; end));[127X[104X
[4X[25Xgap>[125X [27Xal[3];[127X[104X
[4X[28X"beta"[128X[104X
[4X[32X[104X
[1X5.1-2 FixedAtomicList[101X
[33X[1;0Y[29X[2XFixedAtomicList[102X( [3Xlist[103X ) [32X function[133X
[33X[1;0Y[29X[2XFixedAtomicList[102X( [3Xcount[103X, [3Xobj[103X ) [32X function[133X
[33X[0;0Y[2XFixedAtomicList[102X works like [2XAtomicList[102X ([14X5.1-1[114X) except that the resulting list
cannot be resized.[133X
[1X5.1-3 MakeFixedAtomicList[101X
[33X[1;0Y[29X[2XMakeFixedAtomicList[102X( [3Xlist[103X ) [32X function[133X
[33X[0;0Y[2XMakeFixedAtomicList[102X turns a resizable atomic list into a fixed atomic list.[133X
[4X[32X Example [32X[104X
[4X[25Xgap>[125X [27Xa := AtomicList([99]);[127X[104X
[4X[28X<atomic list of size 1>[128X[104X
[4X[25Xgap>[125X [27Xa[2] := 100;[127X[104X
[4X[28X100[128X[104X
[4X[25Xgap>[125X [27XMakeFixedAtomicList(a);[127X[104X
[4X[28X<fixed atomic list of size 2>[128X[104X
[4X[25Xgap>[125X [27Xa[3] := 101;[127X[104X
[4X[28XError, Atomic List Element: <pos>=3 is an invalid index for <list>[128X[104X
[4X[32X[104X
[1X5.1-4 FromAtomicList[101X
[33X[1;0Y[29X[2XFromAtomicList[102X( [3Xatomic_list[103X ) [32X function[133X
[33X[0;0Y[2XFromAtomicList[102X returns a plain list containing the same elements as
[3Xatomic_list[103X at the time of the call. Because other threads can write
concurrently to that list, the result is not guaranteed to be deterministic.[133X
[4X[32X Example [32X[104X
[4X[25Xgap>[125X [27Xal := AtomicList([10, 20, 30]);;[127X[104X
[4X[25Xgap>[125X [27XWaitTask(RunTask(function() al[2] := 40; end));[127X[104X
[4X[25Xgap>[125X [27XFromAtomicList(al);[127X[104X
[4X[28X[ 10, 40, 30 ][128X[104X
[4X[32X[104X
[1X5.1-5 ATOMIC_ADDITION[101X
[33X[1;0Y[29X[2XATOMIC_ADDITION[102X( [3Xatomic_list[103X, [3Xindex[103X, [3Xvalue[103X ) [32X function[133X
[33X[0;0Y[2XATOMIC_ADDITION[102X is a low-level operation that atomically adds [3Xvalue[103X to
[3Xatomic_list[index][103X. It returns the value of [3Xatomic_list[index][103X after the
addition has been performed.[133X
[4X[32X Example [32X[104X
[4X[25Xgap>[125X [27Xal := FixedAtomicList([4,5,6]);;[127X[104X
[4X[25Xgap>[125X [27XATOMIC_ADDITION(al, 2, 7);[127X[104X
[4X[28X12[128X[104X
[4X[25Xgap>[125X [27XFromAtomicList(al);[127X[104X
[4X[28X[ 4, 12, 6 ][128X[104X
[4X[32X[104X
[1X5.1-6 COMPARE_AND_SWAP[101X
[33X[1;0Y[29X[2XCOMPARE_AND_SWAP[102X( [3Xatomic_list[103X, [3Xindex[103X, [3Xold[103X, [3Xnew[103X ) [32X function[133X
[33X[0;0Y[2XCOMPARE_AND_SWAP[102X is an atomic operation. It atomically compares
[3Xatomic_list[index][103X to [3Xold[103X and, if they are identical, replaces the value (in
the same atomic step) with [3Xnew[103X. It returns true if the replacement took
place, false otherwise.[133X
[33X[0;0YThe primary use of [2XCOMPARE_AND_SWAP[102X is to implement certain concurrency
primitives; most programmers will not need to use it.[133X
[1X5.2 [33X[0;0YAtomic records and component objects[133X[101X
[33X[0;0YAtomic records are atomic counterparts to plain records. They support
assignment to individual record fields, and conversion to and from plain
records.[133X
[33X[0;0YAssignment semantics can be specified on a per-record basis if the assigned
record field is already populated, allowing either an overwrite, keeping the
existing value, or raising an error.[133X
[33X[0;0YIt is not possible to unbind atomic record elements.[133X
[33X[0;0YLike plain records, atomic records can be converted to component objects
using [10XObjectify[110X.[133X
[1X5.2-1 AtomicRecord[101X
[33X[1;0Y[29X[2XAtomicRecord[102X( [3Xcapacity[103X ) [32X function[133X
[33X[1;0Y[29X[2XAtomicRecord[102X( [3Xrecord[103X ) [32X function[133X
[33X[0;0Y[2XAtomicRecord[102X is used to create a new atomic record. Its single optional
argument is either a positive integer, denoting the intended capacity (i.e.,
number of elements to be held) of the record, in which case a new empty
atomic record with that initial capacity will be created. Alternatively, the
caller can provide a plain record with which to initially populate the
atomic record.[133X
[4X[32X Example [32X[104X
[4X[25Xgap>[125X [27Xr := AtomicRecord(rec( x := 2 ));[127X[104X
[4X[28X<atomic record 1/2 full>[128X[104X
[4X[25Xgap>[125X [27Xr.y := 3;[127X[104X
[4X[28X3[128X[104X
[4X[25Xgap>[125X [27XTaskResult(RunTask(function() return r.x + r.y; end));[127X[104X
[4X[28X5[128X[104X
[4X[25Xgap>[125X [27X[ r.x, r.y ];[127X[104X
[4X[28X[ 2, 3 ][128X[104X
[4X[32X[104X
[33X[0;0YAny atomic record can later grow beyond its initial capacity. There is no
limit to the number of elements it can hold other than available memory.[133X
[1X5.2-2 FromAtomicRecord[101X
[33X[1;0Y[29X[2XFromAtomicRecord[102X( [3Xrecord[103X ) [32X function[133X
[33X[0;0Y[2XFromAtomicRecord[102X returns a plain record copy of the atomic record [3Xrecord[103X.
This copy is shallow; elements of [3Xrecord[103X will not also be copied.[133X
[4X[32X Example [32X[104X
[4X[25Xgap>[125X [27Xr := AtomicRecord();;[127X[104X
[4X[25Xgap>[125X [27Xr.x := 1;; r.y := 2;; r.z := 3;;[127X[104X
[4X[25Xgap>[125X [27XFromAtomicRecord(r);[127X[104X
[4X[28Xrec( x := 1, y := 2, z := 3 )[128X[104X
[4X[32X[104X
[1X5.3 [33X[0;0YReplacement policy functions[133X[101X
[33X[0;0YThere are three functions that set the replacement policy of an atomic
object. All three can also be used with plain lists and records, in which
case an atomic version of the list or record is first created. This allows
programmers to elide [2XAtomicList[102X ([14X5.1-1[114X) and [2XAtomicRecord[102X ([14X5.2-1[114X) calls when
the next step is to change their policy.[133X
[1X5.3-1 MakeWriteOnceAtomic[101X
[33X[1;0Y[29X[2XMakeWriteOnceAtomic[102X( [3Xobj[103X ) [32X function[133X
[33X[0;0Y[2XMakeWriteOnceAtomic[102X takes a list, record, atomic list, atomic record, atomic
positional object, or atomic component object as its argument. If the
argument is a non-atomic list or record, then the function first creates an
atomic copy of the argument. The function then changes the replacement
policy of the object to write-once: if an element of the object is already
bound, then further attempts to assign to it will be ignored.[133X
[1X5.3-2 MakeStrictWriteOnceAtomic[101X
[33X[1;0Y[29X[2XMakeStrictWriteOnceAtomic[102X( [3Xobj[103X ) [32X function[133X
[33X[0;0Y[2XMakeStrictWriteOnceAtomic[102X works like [2XMakeWriteOnceAtomic[102X ([14X5.3-1[114X), except
that the replacement policy is being changed to being strict write-once: if
an element is already bound, then further attempts to assign to it will
raise an error.[133X
[1X5.3-3 MakeReadWriteAtomic[101X
[33X[1;0Y[29X[2XMakeReadWriteAtomic[102X( [3Xobj[103X ) [32X function[133X
[33X[0;0Y[2XMakeReadWriteAtomic[102X is the inverse of [2XMakeWriteOnceAtomic[102X ([14X5.3-1[114X) and
[2XMakeStrictWriteOnceAtomic[102X ([14X5.3-2[114X) in that the replacement policy is being
changed to being rewritable: Elements can be replaced even if they are
already bound.[133X
[1X5.4 [33X[0;0YThread-local records[133X[101X
[33X[0;0YThread-local records allow an easy way to have a separate copy of a record
for each indvidual thread that is accessed by the same name in each thread.[133X
[4X[32X Example [32X[104X
[4X[25Xgap>[125X [27Xr := ThreadLocalRecord();; # create new thread-local record[127X[104X
[4X[25Xgap>[125X [27Xr.x := 99;;[127X[104X
[4X[25Xgap>[125X [27XWaitThread( CreateThread( function()[127X[104X
[4X[25X>[125X [27X r.x := 100;[127X[104X
[4X[25X>[125X [27X Display(r.x);[127X[104X
[4X[25X>[125X [27X end ) );[127X[104X
[4X[28X100[128X[104X
[4X[25Xgap>[125X [27Xr.x;[127X[104X
[4X[28X99[128X[104X
[4X[32X[104X
[33X[0;0YAs can be seen above, even though [10Xr.x[110X is overwritten in the second thread,
it does not affect the value of [10Xr.x| in the first thread[110X[133X
[1X5.4-1 ThreadLocalRecord[101X
[33X[1;0Y[29X[2XThreadLocalRecord[102X( [[3Xdefaults[103X[, [3Xconstructors[103X]] ) [32X function[133X
[33X[0;0Y[2XThreadLocalRecord[102X creates a new thread-local record. It accepts up to two
initial arguments. The [3Xdefaults[103X argument is a record of default values with
which each thread-local copy is initially populated (this happens on demand,
so values are not actually read until needed). The second argument is a
record of constructors; parameterless functions that return an initial value
for the respective element. Constructors are evaluated only once per thread
and only if the respective element is accessed without having previously
been assigned a value.[133X
[4X[32X Example [32X[104X
[4X[25Xgap>[125X [27Xr := ThreadLocalRecord( rec(x := 99),[127X[104X
[4X[25X>[125X [27X rec(y := function() return 101; end));;[127X[104X
[4X[25Xgap>[125X [27Xr.x;[127X[104X
[4X[28X99[128X[104X
[4X[25Xgap>[125X [27Xr.y;[127X[104X
[4X[28X101[128X[104X
[4X[25Xgap>[125X [27XTaskResult(RunTask(function() return r.x; end));[127X[104X
[4X[28X99[128X[104X
[4X[25Xgap>[125X [27XTaskResult(RunTask(function() return r.y; end));[127X[104X
[4X[28X101[128X[104X
[4X[32X[104X
[1X5.4-2 SetTLDefault[101X
[33X[1;0Y[29X[2XSetTLDefault[102X( [3Xrecord[103X, [3Xname[103X, [3Xvalue[103X ) [32X function[133X
[33X[0;0Y[2XSetTLDefault[102X can be used to set the default value of a record field after
its creation. Here, [3Xrecord[103X is a thread-local record, [3Xname[103X is the string of
the field name, and [3Xvalue[103X is the value.[133X
[4X[32X Example [32X[104X
[4X[25Xgap>[125X [27Xr := ThreadLocalRecord();;[127X[104X
[4X[25Xgap>[125X [27XSetTLDefault(r, "x", 314);[127X[104X
[4X[25Xgap>[125X [27Xr.x;[127X[104X
[4X[28X314[128X[104X
[4X[25Xgap>[125X [27XTaskResult(RunTask(function() return r.x; end));[127X[104X
[4X[28X314[128X[104X
[4X[32X[104X
[1X5.4-3 SetTLConstructor[101X
[33X[1;0Y[29X[2XSetTLConstructor[102X( [3Xrecord[103X, [3Xname[103X, [3Xfunc[103X ) [32X function[133X
[33X[0;0Y[2XSetTLConstructor[102X can be used to set the constructor of a thread-local record
field after its creation, similar to [2XSetTLDefault[102X ([14X5.4-2[114X).[133X
[4X[32X Example [32X[104X
[4X[25Xgap>[125X [27Xr := ThreadLocalRecord();;[127X[104X
[4X[25Xgap>[125X [27XSetTLConstructor(r, "x", function() return 2718; end);[127X[104X
[4X[25Xgap>[125X [27Xr.x;[127X[104X
[4X[28X2718[128X[104X
[4X[25Xgap>[125X [27XTaskResult(RunTask(function() return r.x; end));[127X[104X
[4X[28X2718[128X[104X
[4X[32X[104X