diff --git a/M2/Macaulay2/d/binding.d b/M2/Macaulay2/d/binding.d index 4b8d5aaa04f..97c60b07dff 100644 --- a/M2/Macaulay2/d/binding.d +++ b/M2/Macaulay2/d/binding.d @@ -318,6 +318,7 @@ bumpPrecedence(); export breakpointS := special("breakpoint", unaryop, precSpace, wide); export profileS := special("profile", unaryop, precSpace, wide); export shieldS := special("shield", unaryop, precSpace, wide); + export threadLockS := special("threadLock", unaryop, precSpace, wide); export throwS := special("throw", nunaryop, precSpace, wide); export returnS := special("return", nunaryop, precSpace, wide); export breakS := special("break", nunaryop, precSpace, wide); diff --git a/M2/Macaulay2/d/pthread.d b/M2/Macaulay2/d/pthread.d index acd635f8e95..88069e7fb9c 100644 --- a/M2/Macaulay2/d/pthread.d +++ b/M2/Macaulay2/d/pthread.d @@ -3,6 +3,7 @@ use evaluate; use expr; header "#include \"../system/supervisorinterface.h\""; +header "#include "; -- for EBUSY taskCreatePush(f:function(TaskCellBody):null,tb:TaskCellBody) ::= Ccode(taskPointer, @@ -231,6 +232,20 @@ export getIOThreadMode(e:Expr):Expr := ( else WrongArg("a file or ()")); setupfun("getIOThreadMode", getIOThreadMode); +-- TODO: what if we cancel a task before it unlocks the mutex? +mutex := newMutex; +threadLock(c:Code):Expr := ( + r := 0; + while (r = trylock(mutex); r == Ccode(int, "EBUSY")) do ( + f := eval(dummyCode); -- handle interrupts + when f is Error do return f else nothing); + if r == 0 then ( + e := eval(c); + unlock(mutex); + e) + else buildErrorPacketErrno("threadLock", r)); +setupop(threadLockS, threadLock); + -- Local Variables: -- compile-command: "echo \"make: Entering directory \\`$M2BUILDDIR/Macaulay2/d'\" && make -C $M2BUILDDIR/Macaulay2/d pthread.o " -- End: diff --git a/M2/Macaulay2/d/pthread0.d b/M2/Macaulay2/d/pthread0.d index d6d2d6607cc..1210ffb168f 100644 --- a/M2/Macaulay2/d/pthread0.d +++ b/M2/Macaulay2/d/pthread0.d @@ -30,6 +30,7 @@ export SpinLock := atomicType "struct spinlockStructure"; export init(x:ThreadMutex) ::= Ccode(int, "pthread_mutex_init(&(",lvalue(x),"),NULL)"); export destroy(x:ThreadMutex) ::= Ccode(int, "pthread_mutex_destroy(&(",lvalue(x),"))"); export lock(x:ThreadMutex) ::= Ccode(int, "pthread_mutex_lock(&(",lvalue(x),"))"); +export trylock(x:ThreadMutex) ::= Ccode(int, "pthread_mutex_trylock(&(",lvalue(x),"))"); export unlock(x:ThreadMutex) ::= Ccode(int, "pthread_mutex_unlock(&(",lvalue(x),"))"); export getthreadself() ::= Ccode(Thread, "pthread_self()"); diff --git a/M2/Macaulay2/m2/exports.m2 b/M2/Macaulay2/m2/exports.m2 index 72f7184cc7f..4b551637a70 100644 --- a/M2/Macaulay2/m2/exports.m2 +++ b/M2/Macaulay2/m2/exports.m2 @@ -1203,6 +1203,7 @@ export { "texMath", "then", "threadLocal", + "threadLock", "throw", "time", "times", diff --git a/M2/Macaulay2/tests/normal/threads.m2 b/M2/Macaulay2/tests/normal/threads.m2 index 312a42b3431..8a4943d6286 100644 --- a/M2/Macaulay2/tests/normal/threads.m2 +++ b/M2/Macaulay2/tests/normal/threads.m2 @@ -85,6 +85,13 @@ assert Equation(getIOThreadMode f, 2) removeFile fn +-- threadLock +-- mutable lists are *not* thread safe +-- if they ever become thread safe, then change this test +x = new MutableList +taskResult \ apply(1000, i -> schedule(() -> threadLock x##x = null)); +assert Equation(#x, 1000) + -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/packages/Macaulay2Doc/test threads.out" -- End: