Jorge Marques Pelizzoni wrote:
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)

You're right. I focused too much on the message title. In fact your problem is not really about exceptions...

%% 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.

Correct!  IsFree is an evil function: it looks nice, but it's not!

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):
(...)

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

I like the trick to never let a thread terminate normally. But I don't like locks... Here is an alternative solution, that uses a port instead of a lock. The threads do not bind Result themselves, instead they send the value on the port if it the test is positive. The first value sent on the port is taken as the ConcurrentFind's result. No need for explicit mutual exclusion, and no need for IsFree.

proc {ConcurrentFind3 Xs F ?Result}
   S P={NewPort S}
   Threads = {Map Xs proc {$ X T}
                        thread
                           T={Thread.this}
                           if {F X} then {Send P X} end
                           {Wait _}
                        end
                     end}
in
   Result = S.1     % wait for first value sent
   {ForAll Threads Thread.terminate}
end


Cheers,
raph
_________________________________________________________________________________
mozart-users mailing list                               
[email protected]
http://www.mozart-oz.org/mailman/listinfo/mozart-users

Reply via email to