I recently stumbled upon a combination of lock
and do
- similar to open
and do
.
lock(x.lock) do
# do something unsafe here
end
It is unclear that the do-block
syntax automatically calls unlock after the work inside the block is done (at least, I cannot derive that from the documentation - and it doesn’t feel right to extrapolate the open/close behavior to this). The lock
and unlock
matching requirement is pretty clear from the documentation.
Context: I am reading lots of code these days, and I encountered the behavior above in the LRUCache package.
Am I missing something? Maybe some more general principle about the do-block
?
To remove any ambiguity, can I safely use lock
+ do-block
syntax in my code?
there is nothing particularly special about do
notation, it is simply a syntax trick to create an anonymous function and pass it as the first argument of the preceding call (for any call, not just lock
). you can read the docs for lock(f::Function, lock)
here:
lock(f::Function, lock)
Acquire the lock, execute f with the lock held, and release the lock when f
returns. If the lock is already locked by a different task/thread, wait for
it to become available.
When this function returns, the lock has been released, so the caller should
not attempt to unlock it.
1 Like
I would disagree: The do
-syntax is quite special. It creates an anonymous function and passes it as the first argument to the preceding function.
https://docs.julialang.org/en/v1/manual/functions/#man-anonymous-functions
Thanks for the instant answer - you are right - I was looking at the wrong signature (I was biased - maybe because in the same codebase, it also uses lock/unlock matching when do-block
is not used).
Just to explain that, I believe manual try-finally blocks are used in that codebase when we want to return from inside that block, e.g. in https://github.com/JuliaCollections/LRUCache.jl/blob/618677182498ceeaae16dcdd4f49aad29b5c4c0b/src/LRUCache.jl#L81-L88. We can’t do that bit with the do-block form, since that would just define the return for the anonymous function, not the outer function.
Later versions of Julia have a macro form Base.@lock
(which is exported on Julia 1.9). This is nice because it does not need to define an anonymous function (such closures can cause performance issues in some cases, xref #15276), and one can return
from it.
1 Like
Thanks - I can see here the return
from the anonymous function and also the dedicated return
from the outer one.
However, this was a lame omission on my part: I wasn’t paying enough attention to the different signatures. The more frequent syntax is f(x) do y
- however, because the anonymous function has no argument, it seems that f(x) do
is also legitimate (I don’t think I encountered that before).
Thanks for the clarifications.
1 Like