diff --git a/M2/Macaulay2/d/binding.d b/M2/Macaulay2/d/binding.d index a2b9049d0c7..a34e8adb3c8 100644 --- a/M2/Macaulay2/d/binding.d +++ b/M2/Macaulay2/d/binding.d @@ -321,6 +321,7 @@ bumpPrecedence(); export profileS := special("profile", unaryop, precSpace, wide); export shieldS := special("shield", unaryop, precSpace, wide); export trapS := special("trap", unaryop, precSpace, wide); + export withLockS := special("withLock", 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 f047ee10a1b..06d04c51355 100644 --- a/M2/Macaulay2/d/pthread.d +++ b/M2/Macaulay2/d/pthread.d @@ -279,6 +279,20 @@ unlock(e:Expr):Expr := ( else WrongArgMutex()); setupfun("unlock0", unlock); +withLock(c:Code):Expr := ( + when c + is a:sequenceCode do ( + if length(a.x) == 2 then ( + e := eval(a.x.0); + l := lock(e); + when l is Error do return l else nothing; + r := eval(a.x.1); + u := unlock(e); + when u is Error do u else r) + else WrongNumArgs(2)) + else WrongNumArgs(2)); +setupop(withLockS, withLock); + -- Local Variables: -- compile-command: "echo \"make: Entering directory \\`$M2BUILDDIR/Macaulay2/d'\" && make -C $M2BUILDDIR/Macaulay2/d pthread.o " -- End: diff --git a/M2/Macaulay2/m2/exports.m2 b/M2/Macaulay2/m2/exports.m2 index 2e35f433c19..b5eb2021310 100644 --- a/M2/Macaulay2/m2/exports.m2 +++ b/M2/Macaulay2/m2/exports.m2 @@ -1278,6 +1278,7 @@ export { "when", "while", "width", + "withLock", "wrap", "xor", "youngest", diff --git a/M2/Macaulay2/packages/Macaulay2Doc/doc_mutex.m2 b/M2/Macaulay2/packages/Macaulay2Doc/doc_mutex.m2 index d9cc1315d0e..11251d0c8fe 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/doc_mutex.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/doc_mutex.m2 @@ -47,6 +47,7 @@ doc /// (lock, Mutex) (tryLock, Mutex) (unlock, Mutex) + symbol withLock /// doc /// @@ -136,3 +137,29 @@ doc /// (lock, Mutex) (tryLock, Mutex) /// + +doc /// + Key + symbol withLock + Headline + evaluate an expression while holding a mutex lock + Usage + withLock(m, c) + Inputs + m:Mutex + c: -- code to evaluate + Description + Text + @M2CODE "withLock"@ is a keyword that locks the mutex @VAR "m"@, evaluates + the code @VAR "c"@, unlocks the mutex, and returns the result of + evaluating @VAR "c"@. + + Using @M2CODE "withLock"@ is equivalent to surrounding the code with + calls to @TO lock@ and @TO unlock@, but with the added guarantee that the + mutex is unlocked even if the code raises an error. + Example + m = new Mutex + last trap withLock(m, (print last trap tryLock m; 1/0)) + tryLock m + unlock m +/// diff --git a/M2/Macaulay2/packages/Macaulay2Doc/ov_threads.m2 b/M2/Macaulay2/packages/Macaulay2Doc/ov_threads.m2 index 04ecae20005..34417deaa63 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/ov_threads.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/ov_threads.m2 @@ -27,6 +27,8 @@ Node SeeAlso "parallelism in engine computations" "elapsedTime" + AtomicInt + Mutex Description Text The simplest way to run computations in parallel is to use @ TO parallelApply @. This works diff --git a/M2/Macaulay2/tests/normal/threads.m2 b/M2/Macaulay2/tests/normal/threads.m2 index 87a34b7fdb0..5a549c88527 100644 --- a/M2/Macaulay2/tests/normal/threads.m2 +++ b/M2/Macaulay2/tests/normal/threads.m2 @@ -101,6 +101,10 @@ assert try tryLock m then true else false assert try tryLock m then false else true unlock m +withLock(m, assert try tryLock m then false else true) +assert try tryLock m then true else false +unlock m + -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/packages/Macaulay2Doc/test threads.out" -- End: