  
  [1X11 [33X[0;0YLow-level functionality[133X[101X
  
  [33X[0;0YThe  functionality described in this section should only be used by experts,
  and even by those only with caution (especially the parts that relate to the
  memory model).[133X
  
  [33X[0;0YNot  only  is  it possible to crash or hang the GAP kernel, it can happen in
  ways  that are very difficult to reproduce, leading to software defects that
  are  discovered  only  long  after  deployment  of a package and then become
  difficult to correct.[133X
  
  [33X[0;0YThe  performance  benefit  of  using  these primitives is generally minimal;
  while  concurrency  can induce some overhead, the benefit from micromanaging
  concurrency in an interpreted language such as GAP is likely to be small.[133X
  
  [33X[0;0YThese  low-level  primitives  exist  primarily  for  the  benefit  of kernel
  programmers;  it  allows  them  to prototype new kernel functionality in GAP
  before implementing it in C.[133X
  
  
  [1X11.1 [33X[0;0YExplicit lock and unlock primitives[133X[101X
  
  [33X[0;0YThe  [2XLOCK[102X  ([14X11.1-1[114X)  operation  combined with [2XUNLOCK[102X ([14X11.1-3[114X) is a low-level
  interface for the functionality of the statement.[133X
  
  [1X11.1-1 LOCK[101X
  
  [33X[1;0Y[29X[2XLOCK[102X( [[3Xarg_1[103X, [3X...[103X, [3Xarg_n[103X] ) [32X function[133X
  
  [33X[0;0Y[2XLOCK[102X  takes zero or more pairs of parameters, where each is either an object
  or  a  boolean  value. If an argument is an object, the region containing it
  will  be  locked.  If an argument is the boolean value [9Xfalse[109X, all subsequent
  locks  will  be  read locks; if it is the boolean value [9Xtrue[109X, all subsequent
  locks will be write locks. If the first argument is not a boolean value, all
  locks until the first boolean value will be write locks.[133X
  
  [33X[0;0YLocks  are  managed internally as a stack of locked regions; [2XLOCK[102X returns an
  integer  indicating  a pointer to the top of the stack; this integer is used
  later  by  the  [2XUNLOCK[102X ([14X11.1-3[114X) operation to unlock locks on the stack up to
  that position. If [2XLOCK[102X should fail for some reason, it will return [9Xfail[109X.[133X
  
  [33X[0;0YCalling [2XLOCK[102X with no parameters returns the current lock stack pointer.[133X
  
  [1X11.1-2 TRYLOCK[101X
  
  [33X[1;0Y[29X[2XTRYLOCK[102X( [[3Xarg_1[103X, [3X...[103X, [3Xarg_n[103X] ) [32X function[133X
  
  [33X[0;0Y[2XTRYLOCK[102X  works  similarly  to [2XLOCK[102X ([14X11.1-1[114X). If it cannot acquire all region
  locks,  it  returns  [9Xfail[109X  and  does  not  lock  any regions. Otherwise, its
  semantics are identical to [2XLOCK[102X ([14X11.1-1[114X).[133X
  
  [1X11.1-3 UNLOCK[101X
  
  [33X[1;0Y[29X[2XUNLOCK[102X( [3Xstackpos[103X ) [32X function[133X
  
  [33X[0;0Y[2XUNLOCK[102X  unlocks  all regions on the stack at [3Xstackpos[103X or higher and sets the
  stack pointer to [3Xstackpos[103X.[133X
  
  [4X[32X  Example  [32X[104X
    [4X[25Xgap>[125X [27Xl1 := ShareObj([1,2,3]);;[127X[104X
    [4X[25Xgap>[125X [27Xl2 := ShareObj([4,5,6]);;[127X[104X
    [4X[25Xgap>[125X [27Xp := LOCK(l1);[127X[104X
    [4X[28X0[128X[104X
    [4X[25Xgap>[125X [27XLOCK(l2);[127X[104X
    [4X[28X1[128X[104X
    [4X[25Xgap>[125X [27XUNLOCK(p); # unlock both RegionOf(l1) and RegionOf(l2)[127X[104X
    [4X[25Xgap>[125X [27XLOCK(); # current stack pointer[127X[104X
    [4X[28X0[128X[104X
  [4X[32X[104X
  
  
  [1X11.2 [33X[0;0YHash locks[133X[101X
  
  [33X[0;0YHPC-GAP  supports  [13Xhash locks[113X; internally, the kernel maintains a fixed size
  array  of  locks;  objects  are mapped to a lock via hash function. The hash
  function  is  based  on  the  object reference, not its contents (except for
  short integers and finite field elements).[133X
  
  [4X[32X  Example  [32X[104X
    [4X[25Xgap>[125X [27Xl := [ 1, 2, 3];;[127X[104X
    [4X[25Xgap>[125X [27Xf := l -> Sum(l);;[127X[104X
    [4X[25Xgap>[125X [27XHASH_LOCK(l);   # lock 'l'[127X[104X
    [4X[25Xgap>[125X [27Xf(l);           # do something with 'l'[127X[104X
    [4X[28X6[128X[104X
    [4X[25Xgap>[125X [27XHASH_UNLOCK(l); # unlock 'l'[127X[104X
  [4X[32X[104X
  
  [33X[0;0YHash  locks  should only be used for very short operations, since there is a
  chance  that  two  concurrently  locked  objects map to the same hash value,
  leading to unnecessary contention.[133X
  
  [33X[0;0YHash  locks are unrelated to the locks used by the [10Xatomic[110X statements and the
  [2XLOCK[102X ([14X11.1-1[114X) and [2XUNLOCK[102X ([14X11.1-3[114X) primitives.[133X
  
  [1X11.2-1 HASH_LOCK[101X
  
  [33X[1;0Y[29X[2XHASH_LOCK[102X( [3Xobj[103X ) [32X function[133X
  
  [33X[0;0Y[2XHASH_LOCK[102X  obtains  the  read-write  lock for the hash value associated with
  [10Xobj[110X.[133X
  
  [1X11.2-2 HASH_UNLOCK[101X
  
  [33X[1;0Y[29X[2XHASH_UNLOCK[102X( [3Xobj[103X ) [32X function[133X
  
  [33X[0;0Y[2XHASH_UNLOCK[102X  releases the read-write lock for the hash value associated with
  [10Xobj[110X.[133X
  
  [1X11.2-3 HASH_LOCK_SHARED[101X
  
  [33X[1;0Y[29X[2XHASH_LOCK_SHARED[102X( [3Xobj[103X ) [32X function[133X
  
  [33X[0;0Y[2XHASH_LOCK_SHARED[102X  obtains  the  read-only lock for the hash value associated
  with [10Xobj[110X.[133X
  
  [1X11.2-4 HASH_UNLOCK_SHARED[101X
  
  [33X[1;0Y[29X[2XHASH_UNLOCK_SHARED[102X( [3Xobj[103X ) [32X function[133X
  
  [33X[0;0Y[2XHASH_UNLOCK_SHARED[102X releases the read-only lock for the hash value associated
  with [10Xobj[110X.[133X
  
  
  [1X11.3 [33X[0;0YMigration to the public region[133X[101X
  
  [33X[0;0YHPC-GAP  allows  migration  of  arbitrary objects to the public region. This
  functionality  is  potentially  dangerous;  for  example, if two threads try
  resize a plain list simultaneously, this can result in memory corruption.[133X
  
  [33X[0;0YAccordingly,  such  data  should never be accessed except through operations
  that protect accesses through locks, memory barriers, or other mechanisms.[133X
  
  [1X11.3-1 MAKE_PUBLIC[101X
  
  [33X[1;0Y[29X[2XMAKE_PUBLIC[102X( [3Xobj[103X ) [32X function[133X
  
  [33X[0;0Y[2XMAKE_PUBLIC[102X makes [3Xobj[103X and all its subobjects members of the public region.[133X
  
  [1X11.3-2 MAKE_PUBLIC_NORECURSE[101X
  
  [33X[1;0Y[29X[2XMAKE_PUBLIC_NORECURSE[102X( [3Xobj[103X ) [32X function[133X
  
  [33X[0;0Y[2XMAKE_PUBLIC_NORECURSE[102X  makes  [3Xobj[103X,  but not any of its subobjects members of
  the public region.[133X
  
  
  [1X11.4 [33X[0;0YMemory barriers[133X[101X
  
  [33X[0;0YThe  memory  models  of some processors do no guarantee that read and writes
  reflect  accesses  to  main  memory in the same order in which the processor
  performed  them;  for  example,  code  may  write  variable [10Xv1[110X first, and [10Xv2[110X
  second;  but the cache line containing [10Xv2[110X is flushed to main memory first so
  that other processors see the change to [10Xv2[110X before the change to [10Xv1[110X.[133X
  
  [33X[0;0YMemory  barriers can be used to prevent such counter-intuitive reordering of
  memory accesses.[133X
  
  [1X11.4-1 ORDERED_WRITE[101X
  
  [33X[1;0Y[29X[2XORDERED_WRITE[102X( [3Xexpr[103X ) [32X function[133X
  
  [33X[0;0YThe  [2XORDERED_WRITE[102X  function  guarantees that all writes that occur prior to
  its  execution  or  during  the  evaluation  of [3Xexpr[103X become visible to other
  processors before any of the code executed after.[133X
  
  [33X[0;0YExample:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[25Xgap>[125X [27Xy:=0;; f := function() y := 1; return 2; end;;[127X[104X
    [4X[25Xgap>[125X [27Xx := ORDERED_WRITE(f());[127X[104X
    [4X[28X2[128X[104X
  [4X[32X[104X
  
  [33X[0;0YHere,  the  write barrier ensure that the assignment to [10Xy[110X that occurs during
  the  call  of  [10Xf()[110X becomes visible to other processors before or at the same
  time as the assignment to [10Xx[110X.[133X
  
  [33X[0;0YThis can also be done differently, with the same semantics:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[25Xgap>[125X [27Xt := f();; # temporary variable[127X[104X
    [4X[25Xgap>[125X [27XORDERED_WRITE(0);; # dummy argument[127X[104X
    [4X[25Xgap>[125X [27Xx := t;[127X[104X
    [4X[28X2[128X[104X
  [4X[32X[104X
  
  [1X11.4-2 ORDERED_READ[101X
  
  [33X[1;0Y[29X[2XORDERED_READ[102X( [3Xexpr[103X ) [32X function[133X
  
  [33X[0;0YConversely,  the  [2XORDERED_READ[102X function ensures that reads that occur before
  its call or during the evaluation of [3Xexpr[103X are not reordered with respects to
  memory reads occurring after it.[133X
  
  
  [1X11.5 [33X[0;0YObject manipulation[133X[101X
  
  [33X[0;0YThere are two new functions to exchange a pair of objects.[133X
  
  [1X11.5-1 SWITCH_OBJ[101X
  
  [33X[1;0Y[29X[2XSWITCH_OBJ[102X( [3Xobj1[103X, [3Xobj2[103X ) [32X function[133X
  
  [33X[0;0Y[2XSWITCH_OBJ[102X  exchanges its two arguments. All variables currently referencing
  [3Xobj1[103X  will  reference  [3Xobj2[103X  instead after the operation completes, and vice
  versa. Both objects stay within their previous regions.[133X
  
  [4X[32X  Example  [32X[104X
    [4X[25Xgap>[125X [27Xa := [ 1, 2, 3];;[127X[104X
    [4X[25Xgap>[125X [27Xb := [ 4, 5, 6];;[127X[104X
    [4X[25Xgap>[125X [27XSWITCH_OBJ(a, b);[127X[104X
    [4X[25Xgap>[125X [27Xa;[127X[104X
    [4X[28X[ 4, 5, 6 ][128X[104X
    [4X[25Xgap>[125X [27Xb;[127X[104X
    [4X[28X[ 1, 2, 3 ][128X[104X
  [4X[32X[104X
  
  [33X[0;0YThe   function   requires  exclusive  access  to  both  objects,  which  may
  necessitate using an atomic statement, e.g.:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[25Xgap>[125X [27Xa := ShareObj([ 1, 2, 3]);;[127X[104X
    [4X[25Xgap>[125X [27Xb := ShareObj([ 4, 5, 6]);;[127X[104X
    [4X[25Xgap>[125X [27Xatomic a, b do SWITCH_OBJ(a, b); od;[127X[104X
    [4X[25Xgap>[125X [27Xatomic readonly a do Display(a); od;[127X[104X
    [4X[28X[ 4, 5, 6 ][128X[104X
    [4X[25Xgap>[125X [27Xatomic readonly b do Display(b); od;[127X[104X
    [4X[28X[ 1, 2, 3 ][128X[104X
  [4X[32X[104X
  
  [1X11.5-2 FORCE_SWITCH_OBJ[101X
  
  [33X[1;0Y[29X[2XFORCE_SWITCH_OBJ[102X( [3Xobj1[103X, [3Xobj2[103X ) [32X function[133X
  
  [33X[0;0Y[2XFORCE_SWITCH_OBJ[102X  works  like  [2XSWITCH_OBJ[102X  ([14X11.5-1[114X), except that it can also
  exchange objects in the public region:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[25Xgap>[125X [27Xa := ShareObj([ 1, 2, 3]);;[127X[104X
    [4X[25Xgap>[125X [27Xb := MakeImmutable([ 4, 5, 6]);;[127X[104X
    [4X[25Xgap>[125X [27Xatomic a do FORCE_SWITCH_OBJ(a, b); od;[127X[104X
    [4X[25Xgap>[125X [27Xa;[127X[104X
    [4X[28X[ 4, 5, 6 ][128X[104X
  [4X[32X[104X
  
  [33X[0;0YThis  function  should  be  used  with  extreme caution and only with public
  objects  for  which  only  the  current  thread  has a reference. Otherwise,
  undefined  behavior  and crashes can result from other threads accessing the
  public object concurrently.[133X
  
