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

-- psather.sa: $LOCK, THREAD_ID, MUTEX and GATES

class GATE < $ATTACH, $MUTEX, $COND_LOCK is
   const mutex_t,empty_t,not_empty_t,threads_t,no_threads_t;
  
    include COND_LOCK;
    include ATTACH_INCLUDE{INT}
	  threads_attached->threads_attached,death->attach_death,init->attach_init;

   private attr count:INT;
   
   readonly attr empty:$MUTEX;
   readonly attr not_empty:$MUTEX;
   readonly attr no_threads:$MUTEX;
   readonly attr threads:$MUTEX;

   create:SAME is
      r::=new;
      r.init_cond_lock;
      r.locked_by:=THREAD_ID::nil;
      r.locked:=0;
      r.attach_init;
      r.empty:=#COND_LOCK_HELPER(r,empty_t);
      r.not_empty:=#COND_LOCK_HELPER(r,not_empty_t);
      r.threads:=#COND_LOCK_HELPER(r,threads_t);
      r.no_threads:=#COND_LOCK_HELPER(r,no_threads_t);
      LM_MULTIPLE_LOCK_MANAGER::create(|r,r.empty,r.not_empty,
					r.threads,r.no_threads|);
      return r;
   end;

   death is
      lock when self then
	 count:=count+1;
	 attach_death;
      end;
   end;

   private test_cond(cond:INT):BOOL is
      case cond
      when empty_t then return count=0;
      when not_empty_t then return count/=0;
      when threads_t then return threads_attached/=0;
      when no_threads_t then return threads_attached=0;
      else end;
      return true;
   end;

   size:INT is
      return count;
   end;  
  
   has_thread:BOOL is
      return threads_attached/=0;
   end;
  
   set is
      lock when self then count:=count.max(1); end;
   end;
   
   get is
      lock when not_empty then end;
   end;
   
   enqueue is
      lock when self then count:=count+1; end;
   end;
   
   dequeue is
      lock when not_empty then count:=count-1; end;
   end;
end;

class GATE{T} < $ATTACH{T}, $QUEUE{T}, $COND_LOCK is
   const mutex_t,empty_t,not_empty_t,threads_t,no_threads_t;

   include COND_LOCK;
   include ATTACH_INCLUDE{INT} death->private_death;

   private attr gate_queue:A_QUEUE{T};

   readonly attr empty:COND_LOCK_HELPER;
   readonly attr not_empty:COND_LOCK_HELPER;
   readonly attr no_threads:COND_LOCK_HELPER;
   readonly attr threads:COND_LOCK_HELPER;

   create:SAME is
      r::=new;
      r.init_cond_lock;
      r.locked_by:=THREAD_ID::nil;
      r.locked:=0;
      r.init;
      r.gate_queue:=#;
      r.empty:=#(r,empty_t);
      r.not_empty:=#(r,not_empty_t);
      r.threads:=#(r,threads_t);
      r.no_threads:=#(r,no_threads_t);
      LM_MULTIPLE_LOCK_MANAGER::create(|r,r.empty,r.not_empty,
					r.threads,r.no_threads|);
      return r;
   end;

   death(v:T) is
      lock when self then
	 gate_queue.enq(v);
	 private_death;
      end;
   end;

   private test_cond(cond:INT):BOOL is
      case cond
      when empty_t then return size=0;
      when not_empty_t then return size/=0;
      when threads_t then return threads_attached/=0;
      when no_threads_t then return threads_attached=0;
      else end;
      return true;
   end;

   size:INT is
      return gate_queue.size;
   end;  
   is_empty:BOOL is return size=0; end;
  
   has_thread:BOOL is
      return threads_attached/=0;
   end;
  
   set(v:T) is
      lock when self then 
	 if gate_queue.size=0 then
	    gate_queue.enq(v);
	 else
	    d::=gate_queue.remove;
   	    o::=#A_QUEUE{T}(gate_queue);
	    loop while!(gate_queue.size>0); d:=gate_queue.remove; end;
	    gate_queue.enq(v);
	    loop gate_queue.enq(o.elt!); end;
	 end;
      end;
   end;
   
   get:T is
      lock when not_empty then 
         return gate_queue.top;
      end;
   end;
   top:T is return get; end;

   current:T is return get; end;
   
   enq(v:T) is enqueue(v); end;
   enqueue(v:T) is
      lock when self then 
         gate_queue.enq(v);
      end;
   end;
   
   dequeue:T is
      lock when not_empty then 
         return gate_queue.remove;
      end;
   end;

   remove:T is return dequeue; end;
   
   str:STR is
      return "GATE: threads:"+threads_attached+", elems: "+gate_queue.str+"\n";
   end;

   has(e:T):BOOL is
      lock when self then
         return gate_queue.has(e);
      end;
   end;

   -- copy copies only the queue, the threads are neither copied
   -- nor reattached.
   copy:SAME
   is
      lock self then
	 r:SAME:=#;
	 r.gate_queue:=gate_queue.copy;
	 return r;
      end;
   end;

   clear 
   is
      lock self then
	 gate_queue:=#;
      end;
   end;

   elt!:T is
      loop
         e:T;
	 lock when self then e:=gate_queue.elt!; end;
	 yield e;
      end;
   end;
end; -- class GATE{T}

-- vim:sw=3:nosmartindent
