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

abstract class $MUTEX < $LOCK is end;

-- Slow version of the mutex, uses the generic single lock manager.
class SMUTEX < $MUTEX is
   -- A MUTEX is a MUTual EXclusion lock.  It can be locked only
   -- one thread at a time, although a single thread may lock it
   -- repeatedly without deadlock.
   include MUTEX_INCLUDE;
   create:SAME is
      r ::= new;
      LM_SINGLE_LOCK_MANAGER::create(r);
      --#OUT + (" SMUTEX created, ID: " + SYS::id(r) + ".\n");
      return r;
   end;
end;

class MUTEX < $MUTEX, $LM_SINGLE_LOCK_MANAGER is include QMUTEX; end;
--class MUTEX < $MUTEX is include SMUTEX; end;
class CMUTEX < $MUTEX is include SMUTEX; end;

---- MUTEX combines lock and lock manager.
---- Faster than CMUTEX because of customized manager.
class QMUTEX < $MUTEX, $LM_SINGLE_LOCK_MANAGER is
   -- A MUTEX is a MUTual EXclusion lock.  It can be locked only
   -- one thread at a time, although a single thread may lock it
   -- repeatedly without deadlock.
   include LM_SINGLE_MUTEX_MANAGER_INCLUDE;
   create:SAME is
      r ::= new;
      r.mutex_init;
      --#OUT + (" MUTEX created, ID: " + SYS::id(r) + ".\n");
      return r;
   end;

   mutex_init is
      lm_init;
      locked_by := THREAD_ID::nil;
      no_thread_waiting := true;
      managed_locks := |self|;
      my_manager := self;
      init_queue;
      managed_queues := |my_queue|;
   end;
end;
   
class MUTEX_INCLUDE < $MUTEX is
   -- A MUTEX is a MUTual EXclusion lock.  This class is supposed
   -- to be included in another MUTEX like class
   include LOCK_INCLUDE;

   private attr locked_by:THREAD_ID;
   private attr locked:INT;
   is_locked:BOOL is return locked>0; end;
   

   acquirable(tid:THREAD_ID):BOOL is
      --#OUT +("acquirable("+tid.str+"): "+locked+" on lock "+SYS::id(self)+"\n");
      --#OUT +("acquirable("+tid.str+"): "+locked_by.str+" on lock "+SYS::id(self)+"\n");
      --#OUT +("acquirable("+tid.str+")="+(locked=0 or locked_by=tid)+
      --       " on lock "+SYS::id(self)+".\n");
      --#OUT+("acquirable: locked_by("+locked_by.str+") /= tid ("+tid.str+").\n");
      --#OUT+("acquirable: locked = "+locked.str+".\n");
      --#OUT.flush;
      return locked=0 or locked_by=tid;
   end;

   acquire(tid:THREAD_ID) 
   pre locked=0 or locked_by=tid 
   is
      --#OUT+("acquire("+tid.str+") on lock "+SYS::id(self)+".\n");
      locked:=locked+1;
      locked_by:=tid;
      --#OUT +("acquire("+tid.str+"): "+locked+" on lock "+SYS::id(self)+"\n");
      --#OUT +("acquire("+tid.str+"): "+locked_by.str+" on lock "+SYS::id(self)+"\n");
   end;

   release(tid:THREAD_ID) 
   pre locked>0 and locked_by=tid
   is
      --#OUT+("release("+tid.str+")\n");
   --release(tid:THREAD_ID) 
   --pre locked>0
   --is
      --if locked_by /= tid then
      --   #OUT+("release: locked_by("+locked_by.str+") /= tid ("+tid.str+").\n");
      --end;
      --if locked <= 0 then
      --   #OUT+("release: locked = "+locked.str+".\n");
      --end;
      --#OUT+("release("+tid.str+") on lock "+SYS::id(self)+".\n");
      locked:=locked-1;
      --#OUT+("release("+tid.str+"): "+locked+" on lock "+SYS::id(self)+"\n");
      --#OUT+("release("+tid.str+"): "+locked_by.str+" on lock "+SYS::id(self)+"\n");
      if locked=0 then locked_by:=THREAD_ID::nil; end;
   end;

   wait_for(tid:THREAD_ID):ARRAY{THREAD_ID} is
      if locked>0 and tid/=locked_by then
	 return |locked_by|;
      end;
      return void;
   end;
end;
   
-- vim:sw=3:nosmartindent


