-- (C) Copyright International Business Machines Corporation 23 January 
-- 1990.  All Rights Reserved. 
--  
-- See the file USERAGREEMENT distributed with this software for full 
-- terms and conditions of use. 
-- SCCS Info: @(#)cachemaster.pp	1.5 3/13/90

#ifdef DEBUG
#define DEBUGSTRING(x) print charstring#x;
#define DEBUGOBJECT(x) print x;
#else
#define DEBUGSTRING(x) 
#define DEBUGOBJECT(x) 
#endif

#ifdef OBJECTCACHE

#define MODULE objectCache
#define IOMODULE objectio
#define IMPMOD objectCacheImp
#define READFUNC readObject_Func
#define READQ readObject_Q
#define WRITEFUNC writeObject_Func
#define WRITEQ writeObject_Q
#define READINTF readObject_Intf
#define WRITEINTF writeObject_Intf
#define NAMEFIELD filename
#define OBJECTFIELD object
#define OBJECTTYPE polymorph

#else

#define MODULE programCache
#define IOMODULE load
#define IMPMOD programCacheImp
#define READFUNC load_Func
#define READQ load_Q
#define WRITEFUNC storeFunc
#define WRITEQ storeQ
#define READINTF load_Intf
#define WRITEINTF storeIntf
#define NAMEFIELD modulename
#define OBJECTFIELD program
#define OBJECTTYPE program

#endif


MODULE: using (predefined, IOMODULE, MODULE, IMPMOD, common, unix, unixStat)
process (initQ: initCacheQ)

declare
  init: initCache;

  read: READFUNC;
  cacheReadQ: READQ;
  write: WRITEFUNC;
  cacheWriteQ: WRITEQ;

  stat: statMtimeFunc;
  cache: cache;

begin
  receive init from initQ;
  read := init.read;			-- copy capabilities
  write := init.write;
  stat := init.stat;
  new cacheReadQ;			-- create new services...
  connect init.read to cacheReadQ;	--  ...bind to them...
  new cacheWriteQ;
  connect init.write to cacheWriteQ;
  return init;				--  ...and return

  new cache;

  while boolean#'true' repeat
    select
----- read -------------------------------------------------------------------
      event cacheReadQ 
	block
	  declare
	    cacheRead: READINTF;
	    entry: cacheEntry;
	  begin
	    receive cacheRead from cacheReadQ;	-- if file is cached,
	    DEBUGOBJECT(cacheRead.NAMEFIELD)
	    entry <- cacheEntry#(e in cache	--  return copy of cached entry
		where (boolean#(e.fileName = cacheRead.NAMEFIELD)));
	    if boolean#(entry.mtime = long#(stat(entry.fileName))) then
	        cacheRead.OBJECTFIELD <- entry.object;
		DEBUGSTRING("read from cache")
		return cacheRead;
	      else 
		block		/* cache entry exists, but is invalid */
		  begin
		    remove entry from e in cache
		       where (boolean#(e.fileName = cacheRead.NAMEFIELD));
		    entry.mtime := long#(stat(entry.Filename));
		    entry.object := OBJECTTYPE#(read(entry.fileName));
		    cacheRead.OBJECTFIELD := entry.object;
		    insert entry into cache;
		    DEBUGSTRING("reread from disk")
		    return cacheRead;
		  
		  on (READINTF.file_not_found)	-- file doesn't exist.
		    DEBUGSTRING("no longer findable")
		    return cacheRead exception file_not_found;

#ifdef OBJECTCACHE
		  on (READINTF.file_not_readable)	-- file can't be read.
		    DEBUGSTRING("no longer readable")
		    return cacheRead exception file_not_readable;
#endif
		  end block;
	      end if;


	  on (NotFound)				-- file is not cached.  so,
	    block begin				--  read it in, keep a copy in
	      new entry;			--  the cache, and return it.
	      entry.fileName := cacheRead.NAMEFIELD;
	      entry.object <- OBJECTTYPE#(read(entry.fileName));
	      entry.mtime := long#(stat(entry.fileName));
	      cacheRead.OBJECTFIELD := entry.object;
	      insert entry into cache;
	      DEBUGSTRING("read from disk")
	      return cacheRead;

	    on (READINTF.file_not_found)	-- file doesn't exist.
	      DEBUGSTRING("not found")
	      return cacheRead exception file_not_found;

#ifdef OBJECTCACHE
	    on (READINTF.file_not_readable)	-- file can't be read.
	      DEBUGSTRING("not readable")
	      return cacheRead exception file_not_readable;
#endif

	    end block;
	end block;
	      
----- write ------------------------------------------------------------------
      event cacheWriteQ
	block
	  declare
	    cacheWrite: WRITEINTF;
	    entry: cacheEntry;
	  begin
	    receive cacheWrite from cacheWriteQ;
	    DEBUGOBJECT(cacheWrite.NAMEFIELD)
	    block begin
	      remove entry from e in cache	-- remove cached entry...
		where (boolean#(e.fileName = cacheWrite.NAMEFIELD));
	      discard entry.object;		-- ...and destroy it.
	      DEBUGSTRING("removed from cache")
	    on (NotFound)
	      new entry;
	      entry.filename := cacheWrite.NAMEFIELD;
	    end block;

	    call write(entry.filename, cacheWrite.OBJECTFIELD);

#ifdef WRITEANDCOPY
	    entry.mtime := long#(stat(entry.filename));
	    entry.object := cacheWrite.OBJECTFIELD;
	    insert entry into cache;
	    DEBUGSTRING("written and cached")
#else
	    discard entry;
	    DEBUGSTRING("written")
#endif

	    return cacheWrite;

#ifdef OBJECTCACHE
	on (WRITEINTF.object_not_writeable)
	  discard entry;
	  return cacheWrite exception object_not_writeable;
	on (WRITEINTF.file_not_writeable)
	  discard entry;
	  return cacheWrite exception file_not_writeable;
#else
	on (WRITEINTF.cantStoreProgram)
	  discard entry;
	  return cacheWrite exception cantStoreProgram;
#endif
	end block;
-------------------------------------------------------------------------------
	      
      otherwise
	-- never happens
    end select;
  end while;

  /*NOTREACHED*/

end process

