HPC-GAP allows multiple threads to access data shared between them; to avoid common concurrency errors, such as race
conditions, it partitions GAP objects into regions. Access to regions is regulated so that no two threads can modify
objects in the same region at the same time and so that objects that are being read by one thread cannot concurrently be
modified by another.
Each thread has an associated thread-local region. When a thread implicitly or explicitly creates a new object, that
object initially belongs to the thread's thread-local region.
<P/>
Only the thread can read or modify objects in its thread-local region. For other threads to access an object, that
object has to be migrated into a different region first.
Shared regions are explicitly created through the <Ref Func="ShareObj"/> and <Ref Func="ShareSingleObj"/> primitives
(see below). Multiple threads can access them concurrently, but accessing them requires that a thread uses an
<C>atomic</C> statement to acquire a read or write lock beforehand.
<P/>
See the section on <C>atomic</C> statements (<Ref Sect="The atomic statement."/>) for details.
</Section>
<Section Label="Ordering of shared regions">
<Heading>Ordering of shared regions</Heading>
Shared regions are by default ordered; each shared region has an associated numeric precedence level. Regions can
generally only be locked in order of descending precedence. The purpose of this mechanism is to avoid accidental
deadlocks.
<P/>
The ordering requirement can be overridden in two ways: regions with a negative precedence are excluded from it. This
exception should be used with care, as it can lead to deadlocks.
<P/>
Alternatively, two or more regions can be locked simultaneously via the <C>atomic</C> statement. In this case, the
ordering of these regions relative to each other can be arbitrary.
</Section>
<Section Label="The public region">
<Heading>The public region</Heading>
A special public region contains objects that only permit atomic operations. These include, in particular, all immutable
objects (immutable in the sense that their in-memory representation cannot change).
<P/>
All threads can access objects in the public region at all times without needing to acquire a read- or write-lock
beforehand.
The read-only region is another special region that contains objects that are only meant to be read; attempting to
modify an object in that region will result in a runtime error. To obtain a modifiable copy of such an object, the <Ref
Func="CopyRegion"/> primitive can be used.
</Section>
<Section Label="Migrating objects between regions">
<Heading>Migrating objects between regions</Heading>
Objects can be migrated between regions using a number of functions. In order to migrate an object, the current thread
must have exclusive access to that object; the object must be in its thread-local region or it must be in a shared
region for which the current thread holds a write lock.
<P/>
The <Ref Func="ShareObj"/> and <Ref Func="ShareSingleObj"/> functions create a new shared region and migrate their
respective argument to that region; <C>ShareObj</C> will also migrate all subobjects that are within the same region,
while <C>ShareSingleObj</C> will leave the subobjects unaffected.
<P/>
The <Ref Func="MigrateObj"/> and <Ref Func="MigrateSingleObj"/> functions migrate objects to an existing region. The
first argument of either function is the object to be migrated; the second is either a region (as returned by the <Ref
Func="RegionOf"/> function) or an object whose containing region the first argument is to be migrated to.
<P/>
The current thread needs exclusive access to the target region (denoted by the second argument) for the operation to
succeed. If successful, the first argument will be in the same region as the second argument afterwards. In the case of
<Ref Func="MigrateObj"/>, all subobjects within the same region as the first argument will also be migrated to the target
region.
<P/>
Finally, <Ref Func="AdoptObj"/> and <Ref Func="AdoptSingleObj"/> are special cases of <Ref Func="MigrateObj"/> and
<Ref Func="MigrateSingleObj"/>, where the target region is the thread-local region of the current thread.
<P/>
To migrate objects to the read-only region, one can use <Ref Func="MakeReadOnlyObj"/> and <Ref Func="MakeReadOnlySingleObj"/>.
The first migrates its argument and all its subjobjects that are within the same region to the read-only region; the
second migrates only the argument itself, but not its subobjects.
<P/>
It is generally not possible to migrate objects explicitly to the public region; only objects with purely atomic
operations can be made public and that is done automatically when they are created.
<P/>
The exception are immutable objects. When <Ref BookName="ref" Func="MakeImmutable"/> is used, its argument is automatically moved to the public region.
Regions can be given names, either explicitly via <Ref Func="SetRegionName"/> or when they are created via <Ref
Func="ShareObj"/> and <Ref Func="ShareSingleObj"/>. Thread-local regions, the public, and the readonly region are given
names by default.
<P/>
Multiple regions can have the same name.
</Section>
<Section Label="Controlling access to regions">
<Heading>Controlling access to regions</Heading>
If either GAP code or a kernel primitive attempts to access an object that it is not allowed to access according to
these semantics, either a "write guard error" (for a failed write access) or a "read guard error"
(for a failed read access) will be raised. The global variable <C>LastInaccessible</C> will contain the object that
caused such an error.
<P/>
One exception is that threads can modify objects in regions that they have only read access (but not write access) to
using write-once functions - see section <Ref Sect="Write-once functionality"/>.
<P/>
To inspect objects whose contents lie in other regions (and therefore cannot be displayed by
<Ref BookName="ref" Func="PrintObj"/> or <Ref BookName="ref" Func="ViewObj"/>,
the functions <Ref Func="ViewShared"/> and <Ref Func="UNSAFE_VIEW"/> can be used.
</Section>
<Section Label="Functions relating to regions">
<Heading>Functions relating to regions</Heading>
<Description>
The function <Ref Func="NewRegion"/> creates a new shared region.
If the optional argument <A>name</A> is provided, then the
name of the new region will be set to <A>name</A>.
<Example><![CDATA[
gap> NewRegion("example region");
<region: example region>
]]></Example>
<Ref Func="NewRegion"/> will create a region with a high precedence level. It is intended to be called by user code. The exact
precedence level can be adjusted with <A>prec</A>, which must be an integer in the range <C>[-1000..1000]</C>;
<A>prec</A> will be added to the normal precedence level.
</Description>
</ManSection>
<Description>
<Ref Func="NewLibraryRegion"/> functions like <Ref Func="NewRegion"/>,
except that the precedence of the region it creates is below
that of <Ref Func="NewRegion"/>. It is intended to be used by user libraries and GAP packages.
</Description>
</ManSection>
<Description>
<Ref Func="NewSystemRegion"/> functions like <Ref Func="NewRegion"/>,
except that the precedence of the region it creates is below
that of <Ref Func="NewLibraryRegion"/>. It is intended to be used by the standard GAP library.
</Description>
</ManSection>
<Description>
<Ref Func="NewKernelRegion"/> functions like <Ref Func="NewRegion"/>, except that the precedence of the region it creates is below
that of <Ref Func="NewSystemRegion"/>. It is intended to be used by the GAP kernel, and GAP library code that interacts closely
with the kernel.
</Description>
</ManSection>
<Description>
<Ref Func="NewInternalRegion"/> functions like <Ref Func="NewRegion"/>, except that the precedence of the region it creates is the
lowest available. It is intended to be used for regions that are self-contained; i.e. no function that uses such a
region may lock another region while accessing it. The precedence level of an internal region cannot be adjusted.
</Description>
</ManSection>
<Description>
<Ref Func="NewLibraryRegion"/> functions like <Ref Func="NewRegion"/>, except that the precedence of the region it creates is
negative. It is thus exempt from normal ordering and deadlock checks.
</Description>
</ManSection>
<Description>
The <Ref Func="ShareObj"/> function creates a new shared region and migrates the object and all its subobjects to that region.
If the optional argument <A>name</A> is provided, then the name of the new region is set to <A>name</A>.
<P/>
<Ref Func="ShareObj"/> will create a region with a high precedence level. It is intended to be called by user code. The actual
precedence level can be adjusted by the optional <A>prec</A> argument in the same way as for <Ref Func="NewRegion"/>.
</Description>
</ManSection>
<Description>
<Ref Func="ShareLibraryObj"/> functions like <Ref Func="ShareObj"/>, except that the precedence of the region it creates is below that
of <Ref Func="ShareObj"/>. It is intended to be used by user libraries and GAP packages.
</Description>
</ManSection>
<Description>
<Ref Func="ShareSystemObj"/> functions like <Ref Func="ShareObj"/>, except that the precedence of the region it creates is below that
of <Ref Func="ShareLibraryObj"/>. It is intended to be used by the standard GAP library.
</Description>
</ManSection>
<Description>
<Ref Func="ShareKernelObj"/> functions like <Ref Func="ShareObj"/>, except that the precedence of the region it creates is below that
of <Ref Func="ShareSystemObj"/>. It is intended to be used by the GAP kernel, and GAP library code that interacts closely with
the kernel.
</Description>
</ManSection>
<Description>
<Ref Func="ShareInternalObj"/> functions like <Ref Func="ShareObj"/>, except that the precedence of the region it creates is the
lowest available. It is intended to be used for regions that are self-contained; i.e. no function that uses such a
region may lock another region while accessing it.
</Description>
</ManSection>
<Description>
<Ref Func="ShareSpecialObj"/> functions like <Ref Func="ShareObj"/>, except that the precedence of the region it creates is negative.
It is thus exempt from normal ordering and deadlock checks.
</Description>
</ManSection>
<Description>
The <Ref Func="ShareSingleObj"/> function creates a new shared region and migrates the object, but not its subobjects, to that
region. If the optional argument <A>name</A> is provided, then the name of the new region is set to <A>name</A>.
<Ref Func="ShareSingleObj"/> will create a region with a high precedence level. It is intended to be called by user code. The
actual precedence level can be adjusted by the optional <A>prec</A> argument in the same way as for <Ref Func="NewRegion"/>.
</Description>
</ManSection>
<Description>
<Ref Func="ShareSingleLibraryObj"/> functions like <Ref Func="ShareSingleObj"/>, except that the precedence of the region it creates
is below that of <Ref Func="ShareSingleObj"/>. It is intended to be used by user libraries and GAP packages.
</Description>
</ManSection>
<Description>
<Ref Func="ShareSingleSystemObj"/> functions like <Ref Func="ShareSingleObj"/>, except that the precedence of the region it creates is
below that of <Ref Func="ShareSingleLibraryObj"/>. It is intended to be used by the standard GAP library.
</Description>
</ManSection>
<Description>
<Ref Func="ShareSingleKernelObj"/> functions like <Ref Func="ShareSingleObj"/>, except that the precedence of the region it creates is
below that of <Ref Func="ShareSingleSystemObj"/>. It is intended to be used by the GAP kernel, and GAP library code that
interacts closely with the kernel.
</Description>
</ManSection>
<Description>
<Ref Func="ShareSingleInternalObj"/> functions like <Ref Func="ShareSingleObj"/>, except that the precedence of the region it creates
is the lowest available. It is intended to be used for regions that are self-contained; i.e. no function that uses such
a region may lock another region while accessing it.
</Description>
</ManSection>
<Description>
<Ref Func="ShareSingleLibraryObj"/> functions like <Ref Func="ShareSingleObj"/>, except that the precedence of the region it creates
is negative. It is thus exempt from normal ordering and deadlock checks.
</Description>
</ManSection>
<Description>
The <Ref Func="MigrateObj"/> function migrates <A>obj</A> (and all subobjects contained within the same region) to the region
denoted by the <A>target</A> argument. Here, <A>target</A> can either be a region object returned by <Ref Func="RegionOf"/> or
a normal gap object. If <A>target</A> is a normal gap object, <A>obj</A> will be migrated to the region containing
<C>target</C>.
<P/>
For the operation to succeed, the current thread must have exclusive access to the target region and the object being
migrated.
</Description>
</ManSection>
<Description>
The <Ref Func="MigrateSingleObj"/> function works like <Ref Func="MigrateObj"/>, except that it does not migrate the subobjects
of <C>obj</C>.
</Description>
</ManSection>
<Description>
The <Ref Func="LockAndMigrateObj"/> function works like <Ref Func="MigrateObj"/>, except that it will automatically try to
acquire a lock for the region containing <A>target</A> if it does not have one already.
</Description>
</ManSection>
<Description>
The <Ref Func="IncorporateObj"/> function allows convenient migration to a shared list or record. If <A>target</A> is a list,
then <Ref Func="IncorporateObj"/> is equivalent to:
<Example><![CDATA[
IncorporateObj := function(target, index, value)
atomic value do
target[index] := MigrateObj(value, target)
od;
end;
]]></Example>
If <A>target</A> is a record, then it is equivalent to:
<Example><![CDATA[
IncorporateObj := function(target, index, value)
atomic value do
target.(index) := MigrateObj(value, target)
od;
end;
]]></Example>
The intended purpose is the population of a shared list or record with values after its creation.
<Description>
<Ref Func="AtomicIncorporateObj"/> extends <Ref Func="IncorporateObj"/> by also
locking the target. I.e., for a list, it is equivalent to:
<Example><![CDATA[
AtomicIncorporateObj := function(target, index, value)
atomic target, value do
target[index] := MigrateObj(value, target)
od;
end;
]]></Example>
If <C>target</C> is a record, then it is equivalent to:
<Example><![CDATA[
AtomicIncorporateObj := function(target, index, value)
atomic value do
target.(index) := MigrateObj(value, target)
od;
end;
]]></Example>
</Description>
</ManSection>
<ManSection>
<Func Name="AdoptObj"
Arg='obj'/>
<Description>
The <Ref Func="AdoptObj"/> function migrates <A>obj</A> (and all its subobjects contained within the same region) to the
thread's current region. It requires exclusive access to <A>obj</A>.
<Example><![CDATA[
gap> l := ShareObj([1,2,3]);;
gap> IsThreadLocal(l);
false
gap> atomic l do AdoptObj(l); od;
gap> IsThreadLocal(l);
true
]]></Example>
</Description>
</ManSection>
<Description>
The <Ref Func="AdoptSingleObj"/> function works like <Ref Func="AdoptObj"/>, except that it does not migrate the subobjects of
<A>obj</A>.
</Description>
</ManSection>
<Description>
The <Ref Func="LockAndAdoptObj"/> function works like <Ref Func="AdoptObj"/>, except that it will attempt acquire an exclusive
lock for the region containing <A>obj</A> if it does not have one already.
</Description>
</ManSection>
<ManSection>
<Func Name="CopyRegion"
Arg='obj'/>
<Description>
The <Ref Func="CopyRegion"/> function performs a structural copy of <A>obj</A>. The resulting objects will be located in the
current thread's thread-local region. The function returns the copy as its result.
<Description>
The <Ref Func="IsThreadLocal"/> function returns true if its argument is an object in the current thread's thread-local
region, false otherwise.
<Description>
The <Ref Func="IsShared"/> function returns true if its argument is an object in a shared region. Note that if the current
thread does not hold a lock on that shared region, another thread can migrate <A>obj</A> to a different region before
the result is being evaluated; this can lead to race conditions. The function is intended primarily for debugging, not
to build actual program logic around.
</Description>
</ManSection>
<Description>
The <Ref Func="MakeReadOnlyObj"/> function migrates <A>obj</A> and all its subobjects that are within the same region as
<A>obj</A> to the read-only region. It returns <A>obj</A>.
</Description>
</ManSection>
<Description>
The <Ref Func="MakeReadOnlySingleObj"/> function migrates <A>obj</A>, but not any of its subobjects, to the read-only region. It
returns <A>obj</A>.
</Description>
</ManSection>
<Description>
The <Ref Func="ClearRegionName"/> function clears the name of the region of <A>obj</A> to <A>name</A>.
</Description>
</ManSection>
<ManSection>
<Func Name="RegionName"
Arg='obj'/>
<Description>
The <Ref Func="RegionName"/> function returns the name of the region of <A>obj</A>. If that region does not have a name,
<K>fail</K> will be returned.
</Description>
</ManSection>
<ManSection>
<Func Name="ViewShared"
Arg='obj'/>
<Description>
The <Ref Func="ViewShared"/> function allows the inspection of objects in shared regions. It will try to lock the region and
then call <C>ViewObj(obj)</C>. If it cannot acquire a lock for the region, it will simply display the normal description
of the object.
</Description>
</ManSection>
<ManSection>
<Func Name="UNSAFE_VIEW"
Arg='obj'/>
<Description>
The <Ref Func="UNSAFE_VIEW"/> function allows the inspection of any object in the system, regardless of whether the current
thread has access to the region containing it. It should be used with care: If the object inspected is being modified by
another thread concurrently, the resulting behavior is undefined.
<P/>
Moreover, the function works by temporarily disabling read and write guards for regions, so other threads may corrupt
memory rather than producing errors.
<P/>
It is generally safe to use if all threads but the current one are paused.
</Description>
</ManSection>
<Index Key="atomic" Subkey="no value"><K>atomic</K></Index>
The <C>atomic</C> statement ensures exclusive or read-only access to one or more shared regions for statements within
its scope. It has the following syntax:
Each expression is evaluated and the region containing the resulting object is locked with either a read-write or
read-only lock, depending on the keyword preceding the expression. If neither the <C>readwrite</C> nor the
<C>readonly</C> keyword was provided, read-write locks are used by default.
Examples:
<Example><![CDATA[
gap> l := ShareObj([1,2,3]);;
gap> atomic readwrite l do l[3] := 9; od;
gap> atomic l do l[2] := 4; od;
gap> atomic readonly l do Display(l); od;
[ 1, 4, 9 ]
]]></Example>
<Example><![CDATA[
gap> l := ShareObj([1,2,3,4,5]);;
gap> l2 := ShareObj([6,7,8]);;
gap> atomic readwrite l, readonly l2 do
> for i in [1..3] do l[i] := l2[i]; od;
> l3 := AdoptObj(l);
> od;
gap> l3;
[ 6, 7, 8, 4, 5 ]
]]></Example>
Atomic statements must observe region ordering. That means that the highest precedence level of a region locked by an
atomic statement must be less than the lowest precedene level of a region that is locked by the same thread at the time
the atomic statement is executed.
</Subsection>
</Section>
<Section Label="Atomic functions">
<Heading>Atomic functions</Heading>
Instead of atomic regions, entire functions can be declared to be atomic. This has the same effect as though the
function's body were enclosed in an atomic statement. Function arguments can be declared either <C>readwrite</C> or
<C>readonly</C>; they will be locked in the same way as for a lock statement. If a function argument is preceded by
neither <C>readwrite</C> nor <C>readonly</C>, the corresponding object will not be locked.
There is an exception to the rule that objects can only be modified if a thread has write access to a region. A limited
sets of objects can be modified using the "bind once" family of functions. These allow the modifications of
objects to which a thread has read access in a limited fashion.
<P/>
For reasons of implementation symmetry, these functions can also be used on the atomic versions of these objects.
<P/>
Implementation note: The functionality is not currently available for component objects.
<Description>
<Ref Func="BindOnce"/> modifies <A>obj</A>, which can be a positional object, atomic positional object, component object, or
atomic component object. It inspects <C>obj![index]</C> for the positional versions or <C>obj!.(index)</C> for the
component versions. If the respective element is not yet bound, <A>value</A> is assigned to that element. Otherwise, no
modification happens. The test and modification occur as one atomic step. The function returns the value of the element;
i.e. the old value if the element was bound and <A>value</A> if it was unbound.
<P/>
The intent of this function is to allow concurrent initialization of objects, where multiple threads may attempt to set
a value concurrently. Only one will succeed; all threads can then use the return value of <Ref Func="BindOnce"/> as the
definitive value of the element. It also allows for the lazy initialization of objects in the read-only region.
<P/>
The current thread needs to have at least read access to <A>obj</A>, but does not require write access.
</Description>
</ManSection>
<Description>
<Ref Func="TestBindOnce"/> works like <Ref Func="BindOnce"/>, except that it returns <K>true</K> if the value could be bound and
<K>false</K> otherwise.
</Description>
</ManSection>
<Description>
<Ref Func="BindOnceExpr"/> works like <Ref Func="BindOnce"/>, except that it evaluates the parameterless function <A>expr</A> to
determine the value. It will only evaluate <A>expr</A> if the element is not bound.
<P/>
For positional objects, the implementation works as follows:
<Example><![CDATA[
BindOnceExprPosObj := function(obj, index, expr)
if not IsBound(obj![index]) then
return BindOnce(obj, index, expr());
else
return obj![index]);
fi;
end;
]]></Example>
The implementation for component objects works analogously.
<P/>
The intent is to avoid unnecessary computations if the value is already bound. Note that this cannot be avoided
entirely, because <C>obj![index]</C> or <C>obj!.(index)</C> can be bound while <A>expr</A> is evaluated, but it can
minimize such occurrences.
</Description>
</ManSection>
<Description>
<Ref Func="TestBindOnceExpr"/> works like <Ref Func="BindOnceExpr"/>, except that it returns <K>true</K> if the value could be bound
and <K>false</K> otherwise.
</Description>
</ManSection>
<Description>
<Ref Func="StrictBindOnce"/> works like <Ref Func="BindOnce"/>, except that it raises an error if the element is already bound. This
is intended for cases where a read-only object is initialized, but where another thread trying to initialize it
concurrently would be an error.
</Description>
</ManSection>
</Section>
</Chapter>
¤ Dauer der Verarbeitung: 0.51 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.