Torsten Anders wrote:
> Dear Jorge, dear Raphael,
>
> thank you both for your kind reply.
You're welcome!
> In particular, I found it
> important to learn about the use of Value.failed!
That's very important indeed. I just didn't mention that because it really
does not apply to your case. Anyway, Raphael's advice is always worth
taking :o)
> Brief background info: I want to use ConcurrentFind in CSPs quasi as
> some form of a selection constraint for arbitrary values, more
> specifically for objects containing variables.
Let's continue talking about your problem, but just a word of warning
(sorry if redundant): you should not rely much on devices like
ConcurrentFind as they yield no constraint propagation. But it's true
they're useful sometimes.
> %% Inefficient version: after ConcurrentFind already found an
> element, concurrent threads may still continue running.
> %% */
> proc {ConcurrentFind Xs F ?Result}
> {ForAll Xs proc {$ X}
> thread
> %% blocks until X is sufficiently determined
> if {F X} andthen {IsFree Result}
> then Result = X
> end
> end
> end}
> end
>
You see, here you already have a race condition. And this innocent looking
procedure might well produce (unwanted) failure. Imagine that there are
two threads that have just finished {F X} with True, for different Xs. One
thread tests {IsFree Result} and is preempted just after that; while the
other is activated and also gets True in {IsFree Result}. Mess is likely
to ensue.
The correct way to write ConcurrentFind is use some mutual exclusion
device when accessing Result (as in my Spawner example). Here I'll use a
more explicit such device (there I used a cell to perform mutual
exclusion):
proc {ConcurrentFind Xs F ?Result}
L = {NewLock}
in
{ForAll Xs proc {$ X}
thread
%% blocks until X is sufficiently determined
if {F X} then
lock L then
if {IsFree Result} then Result = X end
end
end
end
end}
end
Now the other problem with your ConcurrentFind2 is that you let threads
die (i.e. let them finish the statement in the 'thread' block) and then
call Thread.terminate for dead threads. You might solve this either by
catching any kernel(deadThread <SomeThread>) exceptions raised, e.g. by
using:
proc {SilentTerminate Thrd}
try
{Thread.terminate Thrd}
catch kernel(deadThread _) then
skip
end
end
instead of Thread.terminate alone.
Another way (the way I used in Spanwer) is simply not let any threads to
terminate, having them all hang on a {Wait _} statement, as in.
declare
proc {ConcurrentFind2 Xs F ?Result}
L = {NewLock}
Threads = {Map Xs proc {$ X T}
thread
T = {Thread.this}
if {F X} then
lock L then
if {IsFree Result} then Result = X end
end
end
{Wait _}
end
end}
in
{Wait Result}
{ForAll Threads Thread.terminate}
end
Cheers,
Jorge.
_________________________________________________________________________________
mozart-users mailing list
[email protected]
http://www.mozart-oz.org/mailman/listinfo/mozart-users