Skip to content

Commit 6e5b8bc

Browse files
jkyang92d-torrance
authored andcommitted
A mostly working thread safe augmented assignment operation on hash tables
1 parent 3eb24d8 commit 6e5b8bc

File tree

2 files changed

+142
-7
lines changed

2 files changed

+142
-7
lines changed

M2/Macaulay2/d/evaluate.d

Lines changed: 94 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,55 @@ maybeUnlock(x:HashTableOrNull):void := (
12851285
if !y.beingInitialized then unlock(y.mutex))
12861286
else nothing);
12871287
1288+
--what about mutable lists?
1289+
--assumes that the table has been locked on
1290+
hashTableAugmentedAssignmentFun(lhs:binaryCode, oper1: Symbol, oper:Symbol, rexpr:Expr, rpos:Position):Expr := (
1291+
lexpr := nullE;
1292+
key := nullE;
1293+
table := eval(lhs.lhs);
1294+
when table
1295+
is Error do return table
1296+
is hashTable:HashTable do (
1297+
if lhs.f == DotS.symbol.binary then (
1298+
--this replicates dotfun from actors5.d but using the non-locking variation of lookup1force
1299+
when lhs.rhs
1300+
is r:globalSymbolClosureCode do (
1301+
key = Expr(SymbolClosure(globalFrame,r.symbol));
1302+
lexpr = lookup1forceNoLock(hashTable, key))
1303+
else nothing) --TODO correct error message printErrorMessageE(rhs,"expected a symbol"))
1304+
else if lhs.f == SharpS.symbol.binary then (
1305+
--TODO
1306+
key = eval(lhs.rhs);
1307+
lexpr = lookup1forceNoLock(hashTable, key)
1308+
)
1309+
else (
1310+
--this case should be impossible
1311+
return nullE;
1312+
);
1313+
meth := lookup(Class(lexpr), Expr(SymbolClosure(globalFrame, oper1)));
1314+
if meth != nullE then (
1315+
r := applyEEE(meth,lexpr,rexpr);--TODO what if meth tries to assign to the same hash table
1316+
--check if the returned value is the symbol "Default"
1317+
when r
1318+
is s:SymbolClosure do (
1319+
if (s.symbol.word.name === "Default") then nothing
1320+
else (
1321+
return r
1322+
)
1323+
)
1324+
else (
1325+
return r;
1326+
)
1327+
);
1328+
left := evaluatedCode(lexpr, codePosition(Code(lhs)));
1329+
right := evaluatedCode(rexpr, rpos);
1330+
r := eval(Code(binaryCode(oper.binary, Code(left), right, rpos)));
1331+
--r := oper.binary(Code(left),Code(right));
1332+
storeInHashTableNoLock(hashTable,key,hash(key),r);
1333+
r)
1334+
else WrongArgHashTable(1)
1335+
);
1336+
12881337
augmentedAssignmentFun(x:augmentedAssignmentCode):Expr := (
12891338
when lookup(x.oper.word, augmentedAssignmentOperatorTable)
12901339
is null do buildErrorPacket("unknown augmented assignment operator")
@@ -1293,17 +1342,58 @@ augmentedAssignmentFun(x:augmentedAssignmentCode):Expr := (
12931342
table := maybeLock(x.lhs);
12941343
-- evaluate the left-hand side first
12951344
lexpr := nullE;
1345+
-- this will eventually hold the value to assign
12961346
if s.word.name === "??" -- x ??= y is treated like x ?? (x = y)
12971347
then (
1348+
maybeUnlock(table);
12981349
e := nullify(x.lhs);
12991350
when e
1300-
is Nothing do nothing
1351+
is Nothing do (
1352+
return when x.lhs
1353+
is y:globalMemoryReferenceCode do (
1354+
r := eval(x.rhs);
1355+
when r is e:Error do r
1356+
else globalAssignment(y.frameindex, x.info, r))
1357+
is y:localMemoryReferenceCode do (
1358+
r := eval(x.rhs);
1359+
when r is e:Error do r
1360+
else localAssignment(y.nestingDepth, y.frameindex, r))
1361+
is y:threadMemoryReferenceCode do (
1362+
r := eval(x.rhs);
1363+
when r is e:Error do r
1364+
else globalAssignment(y.frameindex, x.info, r))
1365+
is y:binaryCode do (
1366+
r := x.rhs;
1367+
if y.f == DotS.symbol.binary || y.f == SharpS.symbol.binary
1368+
then (
1369+
z := AssignElemFun(y.lhs, y.rhs, r);
1370+
z)
1371+
else InstallValueFun(CodeSequence(
1372+
convertGlobalOperator(x.info), y.lhs, y.rhs, r)))
1373+
is y:adjacentCode do (
1374+
r := x.rhs;
1375+
InstallValueFun(CodeSequence(
1376+
convertGlobalOperator(AdjacentS.symbol), y.lhs, y.rhs, r)))
1377+
is y:unaryCode do (
1378+
r := x.rhs;
1379+
UnaryInstallValueFun(convertGlobalOperator(x.info), y.rhs, r))
1380+
else buildErrorPacket(
1381+
"augmented assignment not implemented for this code")
1382+
)
13011383
else (
1302-
maybeUnlock(table);
13031384
return e))
1304-
else lexpr = eval(x.lhs);
1385+
else (
1386+
when table is hashTable:HashTable do (
1387+
when x.lhs is y:binaryCode do (
1388+
r := hashTableAugmentedAssignmentFun(y, x.oper, s, eval(x.rhs), codePosition(x.rhs));
1389+
unlock(hashTable.mutex);
1390+
return r)
1391+
else nothing; --this case should be impossible
1392+
)
1393+
else nothing;
1394+
lexpr = eval(x.lhs)
1395+
);
13051396
when lexpr is e:Error do (
1306-
maybeUnlock(table);
13071397
return lexpr)
13081398
else nothing;
13091399
-- check if user-defined method exists
@@ -1319,10 +1409,8 @@ augmentedAssignmentFun(x:augmentedAssignmentCode):Expr := (
13191409
is s:SymbolClosure do (
13201410
if s.symbol.word.name === "Default" then nothing
13211411
else (
1322-
maybeUnlock(table);
13231412
return r))
13241413
else (
1325-
maybeUnlock(table);
13261414
return r));
13271415
-- if not, use default behavior
13281416
left := evaluatedCode(lexpr, codePosition(x.lhs));
@@ -1344,7 +1432,6 @@ augmentedAssignmentFun(x:augmentedAssignmentCode):Expr := (
13441432
if y.f == DotS.symbol.binary || y.f == SharpS.symbol.binary
13451433
then (
13461434
z := AssignElemFun(y.lhs, y.rhs, r);
1347-
maybeUnlock(table);
13481435
z)
13491436
else InstallValueFun(CodeSequence(
13501437
convertGlobalOperator(x.info), y.lhs, y.rhs, r)))

M2/Macaulay2/d/hashtables.dd

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,38 @@ export remove(x:HashTable,key:Expr):Expr := (
107107
p = p.next);
108108
if !x.beingInitialized then unlock(x.mutex);
109109
ret);
110+
111+
export storeInHashTableNoLock(x:HashTable,key:Expr,h:hash_t,value:Expr):Expr := (
112+
if !x.Mutable then return buildErrorPacket("attempted to modify an immutable hash table");
113+
hmod := int(h & (length(x.table)-1));
114+
p := x.table.hmod;
115+
while p != p.next do (
116+
if p.key == key || equal(p.key,key)==True
117+
then (
118+
p.value = value;
119+
if !x.beingInitialized then unlock(x.mutex);
120+
return value);
121+
p = p.next);
122+
if 4 * x.numEntries == 3 * length(x.table) -- SEE ABOVE
123+
then (
124+
enlarge(x);
125+
hmod = int(h & (length(x.table)-1));
126+
);
127+
-- Old comments:
128+
-- In the following statement, a new entry is adding by atomically replacing the pointer x.table.hmod.
129+
-- This will not confuse readers that don't lock the hash table, because once the load x.table.hmod, they
130+
-- will have a linked list that stays the same, is shortened, or has a value changed, and contains no
131+
-- duplicate entries. Adding a new entry by appending to the end of the list, on the other hand, might
132+
-- cause a reader to encounter the same key twice, if the key is removed and added while the reader is
133+
-- traversing the list.
134+
x.table.hmod = KeyValuePair(key,h,value,x.table.hmod);
135+
--Do not increment numEntries until after new value pair has been inserted to maintain consistency.
136+
--It is necessary to throw in a compilerBarrier to ensure that there is no memory reordering
137+
-- compilerBarrier();
138+
x.numEntries = x.numEntries + 1;
139+
--Note: x.numEntries may be inconsistent with the number of entries in the table if accessed from another thread
140+
value);
141+
110142
export storeInHashTable(x:HashTable,key:Expr,h:hash_t,value:Expr):Expr := (
111143
if !x.Mutable then return buildErrorPacket("attempted to modify an immutable hash table");
112144
if !x.beingInitialized then lockWrite(x.mutex);
@@ -327,6 +359,22 @@ KeyNotFound(object:string, key:Expr):Expr := (
327359
is str:stringCell do msg = str.v else nothing);
328360
buildErrorPacket(msg));
329361

362+
--Don't lock the table, used for augmented assignments
363+
export lookup1forceNoLock(object:HashTable, key:Expr, keyhash:hash_t):Expr := (
364+
res := notfoundE;
365+
keymod := int(keyhash & (length(object.table)-1));
366+
bucket := object.table.keymod;
367+
while bucket != bucket.next do (
368+
if bucket.key == key || bucket.hash == keyhash && equal(bucket.key,key)==True then (
369+
res = bucket.value;
370+
break);
371+
bucket = bucket.next;
372+
);
373+
if res != notfoundE then res
374+
else KeyNotFound("hash table", key)
375+
);
376+
export lookup1forceNoLock(object:HashTable, key:Expr):Expr := lookup1forceNoLock(object,key,hash(key));
377+
330378
export lookup1(object:HashTable,key:Expr,keyhash:hash_t):Expr := (
331379
-- warning: can return notfoundE, which should not be given to the user
332380
res := notfoundE;

0 commit comments

Comments
 (0)