------------------------->  GNU Sather - sourcefile  <-------------------------
-- Copyright (C) 199x by International Computer Science Institute            --
-- This file is part of the GNU Sather library. It is free software; you may --
-- redistribute  and/or modify it under the terms of the GNU Library General --
-- Public  License (LGPL)  as published  by the  Free  Software  Foundation; --
-- either version 2 of the license, or (at your option) any later version.   --
-- This  library  is distributed  in the  hope that it will  be  useful, but --
-- WITHOUT ANY WARRANTY without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. See Doc/LGPL for more details.       --
-- The license text is also available from:  Free Software Foundation, Inc., --
-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA                     --
-------------->  Please email comments to <bug-sather@gnu.org>  <--------------


class RENDEZVOUS_HELPER < $LOCK is
   include LOCK_INCLUDE;
   readonly attr primary:$LOCK;

   create(b:$LOCK):SAME is
      r::=new;
      r.primary:=b;
      return r;
   end;
   acquirable(tid:THREAD_ID):BOOL is
      return primary.acquirable(tid);
   end;

   acquire(tid:THREAD_ID) is return; end;
   release(tid:THREAD_ID) is return; end;
   request(tid:THREAD_ID) is primary.request(tid); return end;
   cancel_request(tid:THREAD_ID) is primary.cancel_request(tid); return end;
   wait_for(tid:THREAD_ID):ARRAY{THREAD_ID} is return primary.wait_for(tid); end;
   combinations:ARRAY{ARRAY{$LOCK}} is return primary.combinations; end;
end;

class RENDEZVOUS_LOCK < $MUTEX is
   -- A Rendezvous lock has two different locks that can be used,
   -- r1 and r2. It guarantees that each time exactly one thread gets
   -- r1 and one r2. It is possible to lock self, in this case no 
   -- rendezvous will be allowed anymore. Note that several threads
   -- can lock r1 or r2 simultaneously.
  
   include MUTEX_INCLUDE;
   readonly attr r1,r2:$LOCK;
   
   create:SAME is
      r::=new;
      r.locked_by:=THREAD_ID::nil;
      r.locked:=0;
      r.r1:=#RENDEZVOUS_HELPER(r);
      r.r2:=#RENDEZVOUS_HELPER(r);
      return r;
   end;

   combinations:ARRAY{ARRAY{$LOCK}} is 
      return ||self|,|r1,r2||;
   end;
end;
   
class RENDEZVOUS_LOCK{T} < $MUTEX is
   -- A Rendezvous lock has two different locks that can be used,
   -- r1 and r2. It guarantees that each time exactly one thread gets
   -- r1 and one r2. It is possible to lock self, in this case no 
   -- rendezvous will be allowed anymore. Note that several threads
   -- can lock r1 or r2 simultaneously.
  
   include MUTEX_INCLUDE;
   private attr rr1,rr2:$LOCK;
   private attr tmap,dmap:FMAP{THREAD_ID,T};
   private attr ilck,dilck:SPINLOCK;
   private attr uses_r1,uses_r1e,uses_r2,uses_r2e:BOOL;
   
   create:SAME is
      return create(20);
   end;

   create(n:INT):SAME is
      r::=new;
      r.locked_by:=THREAD_ID::nil;
      r.locked:=0;
      r.rr1:=#RENDEZVOUS_HELPER(r);
      r.rr2:=#RENDEZVOUS_HELPER(r);
      r.ilck:=#;
      r.dilck:=#;
      return r;
   end;

   t_nil:T is
      t:T;
      typecase t when $NIL then return t.nil;
      else  return void;   end;
   end;

   r1:$LOCK pre ~uses_r1e is
      uses_r1:=true;
      return rr1;
   end;

   r2:$LOCK pre ~uses_r2e is
      uses_r2:=true;
      return rr2;
   end;

   r1(v:T):$LOCK pre ~uses_r1 is
      uses_r1e:=true;
      dilck.lck;
      SYS::import;
      dmap:=dmap.insert(THREAD_ID::me,v);
      SYS::export;
      dilck.unlck;
      return rr1;
   end;

   r2(v:T):$LOCK pre ~uses_r2 is
      uses_r2e:=true;
      dilck.lck;
      SYS::import;
      dmap:=dmap.insert(THREAD_ID::me,v);
      SYS::export;
      dilck.unlck;
      return rr2;
   end;

   get:T pre (uses_r2e or uses_r1e) and tmap.test(THREAD_ID::me) is
      ilck.lck;
      SYS::import;
      t::=tmap.get(THREAD_ID::me);
      tmap:=tmap.delete(THREAD_ID::me);
      SYS::export;
      ilck.unlck;
      return t;
   end;

   combinations:ARRAY{ARRAY{$LOCK}} is 
      return ||self|,|rr1,rr2||;
   end;

   combined_with(lcks:ARRAY{$LOCK},tids:ARRAY{THREAD_ID}) 
   is
      if lcks.asize=2 then
         ilck.lck;
	 dilck.lck;
	 SYS::import;
	 t1::=dmap.get(tids[0]);
	 t2::=dmap.get(tids[1]);
	 dmap:=dmap.delete(tids[0]).delete(tids[1]);
	 tmap:=tmap.insert(tids[0],t2).insert(tids[1],t1);
	 SYS::export;
	 dilck.unlck;
	 ilck.unlck;
      end;
   end;
end;

-- vim:sw=3:nosmartindent
