I’m using a Channel
to keep a few instances of a struct that I need to use inside a thread (following the instructions in Thread-Safe Storage · OhMyThreads.jl):
using OhMyThreads
nthings = 4
const things = Channel{MyThing}(nthings)
foreach(1:nthings) do _
put!(things, MyThing())
end
tforeach(some_iter) do _
thing = take!(things)
results = thing(some_data)
put!(things, thing)
end
It strikes me that the pattern of take!
ing something, using it, and then put!
ing it back is very similar to the @lock
, the do
block in open
, etc, where I don’t need to explicitly state that I’m unlocking, closing, or putting things back.
Do other people find this pattern common, and would people find it useful if we indeed had a macro or do-block for this specific use-case?
1 Like
This doesn’t look like a job for a macro. A function should be plenty sufficient.
Whether or not something exists, it’s very easy to write this functionality yourself. (Or so I think – I never really work with Channel
so don’t know whether I’m neglecting something important.)
function takeput!(fun, channel)
x = take!(channel)
y = try
fun(x)
finally
put!(channel, x) # always return the value to the channel, even with an error
end
return y
end
The try-finally
part could be replaced with straight code if you don’t need to ensure the value goes back on the channel after an exception.
You would write your tforeach
as
tforeach(some_iter) do _
results = takeput!(thing, things)
end
EDIT: Oops, the code I wrote here swaps the role of the function and argument relative to the original prompt. The poster below fixes my mistake.
Whether or not something exists, it’s very easy to write this functionality yourself.
I agree. I should have put it differently:
Do other people find this pattern common, and would people find it useful if we indeed had a macro or do-block for this specific use-case (I will edit the original post)?
Drawing inspiration from your example, and looking in Base at how the open
do-block looks like, resulted in this:
function borrow(f::Function, c::Channel)
v = take!(c)
try
return f(v)
finally
put!(c, v)
end
end
Which allows us to do things like:
results = borrow(channel) do thing
thing(some_data)
end