;;;-*- Mode:LISP; Package:SYSTEM-INTERNALS; Base:10; Readtable:CL -*-
;;; ** (c) Copyright 1982 Massachusetts Institute of Technology **

;;;NOTE: The is quite a bit of confusion in terminlogy below wrt "FREE-LIST".
;;;  When the old code and documentation says FREE-LIST, it really means "Array which holds
;;;  all instances in existance.  Another component of the array has a flag which records
;;;  which instances are really free, etc".  The new FREE-LIST-CELL option indicates
;;;  that a new-style real (chained) free list is in use.

;;; New version of resource package, subsumes system-window facility
;;; Note that WITH-RESOURCE is obsolete because it takes its "arguments"
;;; in the wrong order.  It has been replaced by USING-RESOURCE.

;;; Old form of DEFRESOURCE:
;;;	(DEFRESOURCE [name | (name dont-make-initial-copy)] . creator-body)
;;; New form of DEFRESOURCE:
;;;	(DEFRESOURCE name parameters [docstring] keyword value keyword value ...)
;;;  Keywords are:
;;;	:CONSTRUCTOR form   (this is required)
;;;		Sees parameters as arguments.
;;;	:FINDER form
;;;		Sees resource-structure and parameters as arguments.
;;;	:MATCHER form
;;;		Sees OBJECT (in current package) and parameters as arguments.
;;;		Note that if the FREE-LIST-CELL option is in use, :MATCHER must be
;;;		supplied unless there are no parameters.
;;;	:CHECKER form
;;;		Sees OBJECT and IN-USE-P (in current package) and parameters as arguments.
;;;	:INITIALIZER form
;;;		Sees OBJECT and parameters as arguments.
;;;	  In the above options starting with :CONSTRUCTOR, form may also be a symbol
;;;	  which is a function to call.  It gets the resource data structure as its first
;;;	  argument then the specified args.
;;;	:DEINITIALIZER form
;;;		Sees OBJECT as argument.  This was initially added to preserve storage
;;;             integrity and avoid unnecessary data retention by the GC, by storing NILs
;;;             into boxed Qs when an object was deallocated.  SI:WIPE-STRUCTURE is a
;;;             general function for setting all slots of a structure to NIL.
;;;	:FREE-LIST-CELL form
;;;	   Giving this option specifies that objects are to be chained in a "real" free
;;;	   list instead of the funny array otherwise used.
;;;		Sees OBJECT as argument.   Returns locative pointer to a cell (presumably
;;;		withing OBJECT) which is to be used for free-list chaining.
;;;	   Note that the :MATCHER option should be used with the :FREE-LIST-CELL option
;;;	   unless parameters is always to be NIL.  This is due to the fact the parameters are not
;;;	   retained in the resource data structure for EQUAL testing as a default :MATCHER.
;;;      In the above two options, form may also be a symbol which is a function to call.
;;;	 However, the args are always as specified; the resource data structure is never passed.
;;;	:INITIAL-COPIES number  (default 0)
;;;		If this is specified, all parameters must be &optional and
;;;		have suitable defaults.  This is generally a good idea anyway.
;;;		Specifying NIL here is the same as zero.
;;;     :FREE-LIST-SIZE number  (default 20.)
;;;		If this is specified, the size of the free-list for this resource
;;;		will initially be that number.
;;;  If :FINDER is specified, we keep no list of free objects and use :FINDER
;;;  to find a free one by looking through the general environment.
;;;  Otherwise we keep a table of objects and whether they are free.
;;;  If :MATCHER is specified, we use it to check them against the parameters.
;;;  Otherwise the a-list also includes the parameter values, which are checked
;;;  with EQUAL (not EQ).
;;;  If :CHECKER is specified, then it gets to pass on each object to decide whether
;;;  or not to reuse it, whether or not it is already marked as in-use.
;;;
;;;  The finder, matcher, and checker are called without-interrupts.
;;;
;;;  Possible features that might be added: ability to keep a free list threaded
;;;  through the objects.  Code to be run when something is deallocated, e.g.
;;;  to deactivate a window.
;;;
;;;  Note: for windows, you typically want to use DEFWINDOW-RESOURCE,
;;;  which supplies the right options to DEFRESOURCE.
;;;
;;; DEFRESOURCE no longer uses the value and function cells of the resource's name.
;;; It puts on a DEFRESOURCE property of the following defstruct.  Note: only the
;;; functions right here are "allowed" to know what is in this structure.

(ZL:DEFSTRUCT (RESOURCE (:TYPE :NAMED-ARRAY-LEADER) (:ALTERANT NIL)
			:CONC-NAME)
  NAME				;Symbol which names it
  (N-OBJECTS 0)			;Total number of objects ever created
  PARAMETIZER			;Function which defaults the parameters and returns list
  CONSTRUCTOR			;Constructor function
  FINDER			;Optional finder function
  MATCHER			;Optional matcher function
  CHECKER			;Optional checker function
  INITIALIZER			;Optional initializer function
  DEINITIALIZER			;Optional deinitializer function
  FREE-LIST-CELL		;Optional function, returns cell for free list chaining
  FREE-LIST)			;Available for use as real free list if FREE-LIST-CELL in use.

;;; The free list is the (n x 3) array itself, with the following fields:
(DEFSUBST RESOURCE-OBJECT (RESOURCE I) (CL:AREF RESOURCE I 0))
(DEFSUBST RESOURCE-IN-USE-P (RESOURCE I) (CL:AREF RESOURCE I 1))
(DEFSUBST RESOURCE-PARAMETERS (RESOURCE I) (CL:AREF RESOURCE I 2))

(defun resource-name-p (s)
  (and (symbolp s) (not (null (get s 'defresource)))))
(deftype resource-name ()
  "a symbol which is the name of a resource"
  `(satisfies resource-name-p))

(DEFSELECT ((:PROPERTY RESOURCE NAMED-STRUCTURE-INVOKE))
  (:DESCRIBE (RESOURCE &AUX (N-OBJECTS (RESOURCE-N-OBJECTS RESOURCE)))
    (DESCRIBE-DEFSTRUCT RESOURCE)
    (cond ((ZEROP N-OBJECTS)
	   (FORMAT T "~&There are currently no objects.~%"))
	  (t
	   (FORMAT T "~&There ~[~;is~:;are~] currently ~:*~D object~:P:~@
		Object~40TParameters~60TIn Use"
		   N-OBJECTS)
	   (unless (or (resource-finder resource) (resource-free-list-cell resource))
	     (dotimes (i n-objects)
	       (FORMAT T "~%~S~40T~S~60T~:[No~;Yes~]"
		       (RESOURCE-OBJECT RESOURCE I)
		       (RESOURCE-PARAMETERS RESOURCE I)
		       (RESOURCE-IN-USE-P RESOURCE I))))
	   (terpri)))))

(DEFMACRO DEFRESOURCE (NAME PARAMETERS &REST OPTIONS)
  "Define a resource named NAME, with parameters PARAMETERS for constructing objects.
OPTIONS can specify how to create objects and how to tell when old objects
can be reused."
  (DECLARE (ARGLIST NAME PARAMETERS \[DOCUMENTATION-STRING\]
		    &KEY :CONSTRUCTOR :FINDER :MATCHER :CHECKER
			 :INITIALIZER :DEINITIALIZER
			 :FREE-LIST-CELL :INITIAL-COPIES :FREE-LIST-SIZE))
  (DECLARE (ZWEI:INDENTATION 2 1))
  (LET ((CONSTRUCTOR-FORM NIL) (FINDER-FORM NIL) (MATCHER-FORM NIL) (CHECKER-FORM NIL)
	(CONSTRUCTOR-FUNCTION NIL) (FINDER-FUNCTION NIL) (MATCHER-FUNCTION NIL)
	(PARAMETIZER-FUNCTION NIL) (CHECKER-FUNCTION NIL) (INITIAL-COPIES 0)
	(INITIALIZER-FORM NIL) (INITIALIZER-FUNCTION NIL)
	(DEINITIALIZER-FORM NIL) (DEINITIALIZER-FUNCTION NIL) 
	(FREE-LIST-CELL-FORM NIL) (FREE-LIST-CELL-FUNCTION NIL) (FREE-LIST-SIZE 20.) (PARAMS NIL)
	(DOCUMENTATION NIL))
    (UNLESS (CL:LISTP PARAMETERS)
      (FERROR "~S invalid parameter list" PARAMETERS))
    (SETQ PARAMS (LOOP FOR P IN PARAMETERS
		       UNLESS (MEMQ P LAMBDA-LIST-KEYWORDS)
		     COLLECT (IF (SYMBOLP P) P (CAR P))))
    ;; if first option is a string, use it as documentation instead
    (WHEN (STRINGP (CAR OPTIONS))
      (SETQ DOCUMENTATION (POP OPTIONS)))
    (LOOP FOR (KEYWORD VALUE) ON OPTIONS BY 'CDDR
       DO (CASE KEYWORD
	    (:CONSTRUCTOR (SETQ CONSTRUCTOR-FORM VALUE))
	    (:FINDER (SETQ FINDER-FORM VALUE))
	    (:MATCHER (SETQ MATCHER-FORM VALUE))
	    (:CHECKER (SETQ CHECKER-FORM VALUE))
	    (:INITIALIZER (SETQ INITIALIZER-FORM VALUE))
	    (:DEINITIALIZER (SETQ DEINITIALIZER-FORM VALUE))
	    (:FREE-LIST-CELL (SETQ FREE-LIST-CELL-FORM VALUE))
	    (:INITIAL-COPIES
	     (SETQ INITIAL-COPIES
		   (COND ((NULL VALUE) 0)
			 ((NUMBERP VALUE) VALUE)
			 (T (FERROR "~S ~S - number required" :INITIAL-VALUES VALUE)))))
	    (:FREE-LIST-SIZE
	     (SETQ FREE-LIST-SIZE
		   (COND ((NULL VALUE) 20.)
			 ((NUMBERP VALUE) VALUE)
			 (T (FERROR "~S ~S - number required" :FREE-LIST-SIZE FREE-LIST-SIZE)))))
	       (OTHERWISE (FERROR "~S illegal option in ~S" KEYWORD 'DEFRESOURCE))))
    (OR CONSTRUCTOR-FORM (FERROR "~S requires the ~S option" 'DEFRESOURCE :CONSTRUCTOR))
    ;; Pick function names.  Note that NIL is SYMBOLP.
    (SETQ CONSTRUCTOR-FUNCTION (IF (SYMBOLP CONSTRUCTOR-FORM) CONSTRUCTOR-FORM
				 `(:PROPERTY ,NAME RESOURCE-CONSTRUCTOR)))
    (SETQ FINDER-FUNCTION (IF (SYMBOLP FINDER-FORM) FINDER-FORM
			    `(:PROPERTY ,NAME RESOURCE-FINDER)))
    (SETQ MATCHER-FUNCTION (IF (SYMBOLP MATCHER-FORM) MATCHER-FORM
			     `(:PROPERTY ,NAME RESOURCE-MATCHER)))
    (SETQ CHECKER-FUNCTION (IF (SYMBOLP CHECKER-FORM) CHECKER-FORM
			     `(:PROPERTY ,NAME RESOURCE-CHECKER)))
    (SETQ INITIALIZER-FUNCTION (IF (SYMBOLP INITIALIZER-FORM) INITIALIZER-FORM
				 `(:PROPERTY ,NAME RESOURCE-INITIALIZER)))
    (SETQ DEINITIALIZER-FUNCTION (IF (SYMBOLP DEINITIALIZER-FORM) DEINITIALIZER-FORM
				 `(:PROPERTY ,NAME RESOURCE-DEINITIALIZER)))
    (SETQ FREE-LIST-CELL-FUNCTION (IF (SYMBOLP FREE-LIST-CELL-FORM) FREE-LIST-CELL-FORM
				    `(:PROPERTY ,NAME RESOURCE-FREE-LIST-CELL)))
    (SETQ PARAMETIZER-FUNCTION (IF (AND PARAMETERS (NOT MATCHER-FORM) (NOT FINDER-FORM))
				   `(:PROPERTY ,NAME RESOURCE-PARAMETIZER)))
    `(LOCAL-DECLARE ((SYS:FUNCTION-PARENT ,NAME DEFRESOURCE))
       ,(IF (NOT (SYMBOLP CONSTRUCTOR-FORM))
	    `(DEFUN ,CONSTRUCTOR-FUNCTION (IGNORE ,@PARAMETERS)
	       ,@PARAMS
	       ,CONSTRUCTOR-FORM))
       ,(IF (NOT (SYMBOLP FINDER-FORM))
	    `(DEFUN ,FINDER-FUNCTION (IGNORE ,@PARAMETERS)
	       ,@PARAMS
	       ,FINDER-FORM))
       ,(IF (NOT (SYMBOLP MATCHER-FORM))
	    `(DEFUN ,MATCHER-FUNCTION (IGNORE ,(INTERN "OBJECT") ,@PARAMETERS)
	       ,@PARAMS
	       ,MATCHER-FORM))
       ,(IF (NOT (SYMBOLP CHECKER-FORM))
	    `(DEFUN ,CHECKER-FUNCTION (IGNORE ,(INTERN "OBJECT") ,(INTERN "IN-USE-P")
				       ,@PARAMETERS)
	       ,@PARAMS ,(INTERN "OBJECT") ,(INTERN "IN-USE-P")
	       ,CHECKER-FORM))
       ,(IF (NOT (SYMBOLP INITIALIZER-FORM))
	    `(DEFUN ,INITIALIZER-FUNCTION (IGNORE ,(INTERN "OBJECT") ,@PARAMETERS)
	       ,@PARAMS ,(INTERN "OBJECT")
	       ,INITIALIZER-FORM))
       ,(IF (NOT (SYMBOLP DEINITIALIZER-FORM))
	    `(DEFUN ,DEINITIALIZER-FUNCTION (,(INTERN "OBJECT"))
	       ,DEINITIALIZER-FORM))
       ,(IF (NOT (SYMBOLP FREE-LIST-CELL-FORM))
	    `(DEFUN ,FREE-LIST-CELL-FUNCTION (,(INTERN "OBJECT"))
	       ,FREE-LIST-CELL-FORM))
       ,(IF PARAMETIZER-FUNCTION
	    `(DEFUN ,PARAMETIZER-FUNCTION ,PARAMETERS
	       (LIST ,@PARAMS)))
       (INITIALIZE-RESOURCE ',NAME ',CONSTRUCTOR-FUNCTION ',FINDER-FUNCTION
			    ',MATCHER-FUNCTION ',CHECKER-FUNCTION
			    ',PARAMETIZER-FUNCTION ',INITIAL-COPIES ',FREE-LIST-SIZE
			    ',INITIALIZER-FUNCTION ',DEINITIALIZER-FUNCTION
			    ',FREE-LIST-CELL-FUNCTION)
       ,(IF DOCUMENTATION
	  `(SET-DOCUMENTATION ',NAME 'RESOURCE ,DOCUMENTATION)))))

(DEFPROP DEFRESOURCE "Resource" DEFINITION-TYPE-NAME)

(DEFVAR *ALL-RESOURCES* NIL
  "List of all symbols that are names of DEFRESOURCEs.")

(DEFUN INITIALIZE-RESOURCE (NAME CONSTRUCTOR-FUNCTION FINDER-FUNCTION MATCHER-FUNCTION
			    CHECKER-FUNCTION PARAMETIZER-FUNCTION INITIAL-COPIES
			    ;; Keep this &OPTIONAL for the time being so old QFASLs work.
			    &OPTIONAL (FREE-LIST-SIZE 20.) INITIALIZER-FUNCTION
			    DEINITIALIZER-FUNCTION FREE-LIST-CELL-FUNCTION)
  (OR (SYMBOLP CONSTRUCTOR-FUNCTION)
      (SETQ CONSTRUCTOR-FUNCTION (GET (SECOND CONSTRUCTOR-FUNCTION)
				      (THIRD CONSTRUCTOR-FUNCTION))))
  (OR (SYMBOLP FINDER-FUNCTION)
      (SETQ FINDER-FUNCTION (GET (SECOND FINDER-FUNCTION) (THIRD FINDER-FUNCTION))))
  (OR (SYMBOLP MATCHER-FUNCTION)
      (SETQ MATCHER-FUNCTION (GET (SECOND MATCHER-FUNCTION) (THIRD MATCHER-FUNCTION))))
  (OR (SYMBOLP CHECKER-FUNCTION)
      (SETQ CHECKER-FUNCTION (GET (SECOND CHECKER-FUNCTION) (THIRD CHECKER-FUNCTION))))
  (OR (SYMBOLP INITIALIZER-FUNCTION)
      (SETQ INITIALIZER-FUNCTION (GET (SECOND INITIALIZER-FUNCTION)
				      (THIRD INITIALIZER-FUNCTION))))
  (OR (SYMBOLP DEINITIALIZER-FUNCTION)
      (SETQ DEINITIALIZER-FUNCTION (GET (SECOND DEINITIALIZER-FUNCTION)
					(THIRD DEINITIALIZER-FUNCTION))))
  (OR (SYMBOLP FREE-LIST-CELL-FUNCTION)
      (SETQ FREE-LIST-CELL-FUNCTION (GET (SECOND FREE-LIST-CELL-FUNCTION)
					 (THIRD FREE-LIST-CELL-FUNCTION))))
  (OR (SYMBOLP PARAMETIZER-FUNCTION)
      (SETQ PARAMETIZER-FUNCTION (GET (SECOND PARAMETIZER-FUNCTION)
				      (THIRD PARAMETIZER-FUNCTION))))
  (AND (RECORD-SOURCE-FILE-NAME NAME 'DEFRESOURCE)
       (LET ((OLD-RESOURCE (GET NAME 'DEFRESOURCE)) RESOURCE)
	 ;; Be careful that there's enough room for all objects in the old resource
	 ;; when replacing it.
	 (AND OLD-RESOURCE (NOT FINDER-FUNCTION) (NOT FREE-LIST-CELL-FUNCTION)
	      (SETQ FREE-LIST-SIZE (MAX (RESOURCE-N-OBJECTS OLD-RESOURCE)
					FREE-LIST-SIZE)))
	 (AND (OR FINDER-FUNCTION FREE-LIST-CELL-FUNCTION) (SETQ FREE-LIST-SIZE 0))
	 (SETQ RESOURCE (MAKE-RESOURCE :NAME NAME
				       :MAKE-ARRAY (:LENGTH (LIST FREE-LIST-SIZE 3)
						    :AREA PERMANENT-STORAGE-AREA)
				       :PARAMETIZER PARAMETIZER-FUNCTION
				       :CONSTRUCTOR CONSTRUCTOR-FUNCTION
				       :FINDER FINDER-FUNCTION
				       :MATCHER MATCHER-FUNCTION
				       :CHECKER CHECKER-FUNCTION
				       :INITIALIZER INITIALIZER-FUNCTION
				       :DEINITIALIZER DEINITIALIZER-FUNCTION
				       :FREE-LIST-CELL FREE-LIST-CELL-FUNCTION))
	 ;; Save any old objects when reloading a DEFRESOURCE
	 (IF OLD-RESOURCE
	     (COND ((and FREE-LIST-CELL-FUNCTION
			 (resource-free-list-cell old-resource))
		    (SETF (RESOURCE-N-OBJECTS RESOURCE)
			  (RESOURCE-N-OBJECTS OLD-RESOURCE))
		    (SETF (RESOURCE-FREE-LIST RESOURCE)
			  (RESOURCE-FREE-LIST OLD-RESOURCE)))
		   ((NOT FINDER-FUNCTION)
		    (COPY-ARRAY-CONTENTS OLD-RESOURCE RESOURCE)
		    (SETF (RESOURCE-N-OBJECTS RESOURCE)
			  (RESOURCE-N-OBJECTS OLD-RESOURCE)))))
	 (SETF (GET NAME 'DEFRESOURCE) RESOURCE)
	 (LOOP FOR OBJECT IN (LOOP REPEAT INITIAL-COPIES COLLECT (ALLOCATE-RESOURCE NAME))
	    DO (DEALLOCATE-RESOURCE NAME OBJECT))))
  (PUSHNEW NAME *ALL-RESOURCES* :TEST #'EQ)
  NAME)

;Don't record this in qfasl files because it always does a RECORD-SOURCE-FILE-NAME.
(DEFPROP INITIALIZE-RESOURCE T QFASL-DONT-RECORD)

(DEFUN CLEAR-RESOURCE (RESOURCE-NAME &AUX RESOURCE)
  "Throw away all objects allocated from the resource RESOURCE-NAME.
This is useful if you discover they were all constructed wrong,
and you fix the constructor, to make sure newly constructed objects will be used."
  (CHECK-TYPE RESOURCE-NAME RESOURCE-NAME "the name of a resource")
  (SETQ RESOURCE (GET RESOURCE-NAME 'DEFRESOURCE))
  (WITHOUT-INTERRUPTS
    ;; Clear the actual cells so the old objects can be garbage collected immediately.
    (cond ((or (resource-finder resource) (resource-free-list-cell resource))
	   (setf (resource-free-list resource) nil)
	   (setf (resource-n-objects resource) 0))
	  (t
	   (DOTIMES (I (RESOURCE-N-OBJECTS RESOURCE))
	     (WHEN (RESOURCE-IN-USE-P RESOURCE I)
	       (FORMAT *ERROR-OUTPUT* "~%[Warning: ~S still in use]"
		       (RESOURCE-OBJECT RESOURCE I))
	       (SETF (RESOURCE-OBJECT RESOURCE I) NIL)))
	   (SETF (RESOURCE-N-OBJECTS RESOURCE) 0)))))

(DEFUN MAP-RESOURCE (FUNCTION RESOURCE-NAME &REST EXTRA-ARGS &AUX RESOURCE)
  "Call FUNCTION on each object created in resource RESOURCE-NAME.
FUNCTION gets three args at each call: the object, whether the resource
believes it is in use, and RESOURCE-NAME."
  (CHECK-TYPE RESOURCE-NAME resource-name "the name of a resource")
  (SETQ RESOURCE (GET RESOURCE-NAME 'DEFRESOURCE))
  ;; Windows are the user's problem....
  (unless (or (resource-finder resource) (resource-free-list-cell resource))
    (LOOP FOR I FROM 0 BELOW (RESOURCE-N-OBJECTS RESOURCE)
	  FOR OBJECT = (RESOURCE-OBJECT RESOURCE I)
       WHEN OBJECT
         DO (APPLY FUNCTION OBJECT (RESOURCE-IN-USE-P RESOURCE I) RESOURCE-NAME EXTRA-ARGS))))
  
(DEFUN ALLOCATE-RESOURCE (RESOURCE-NAME &REST PARAMETERS
			  &AUX RESOURCE (PARAMS PARAMETERS)  ;Note PARAMS is UNSAFE!
			  TEM INDEX (OLD INHIBIT-SCHEDULING-FLAG) INITIALIZER)
  "Allocate an object from resource RESOURCE-NAME according to PARAMETERS.
An old object is reused if possible; otherwise a new one is created.
The significance of the PARAMETERS is determined by the individual resource."
 ;the following CHECK-TYPE is amazingly slow, assume anything with a DEFRESOURCE must be OK.
 ;  (CHECK-TYPE RESOURCE-NAME RESOURCE-NAME "the name of a resource")
  (cond ((null (SETQ RESOURCE (GET RESOURCE-NAME 'DEFRESOURCE)))
	 (CHECK-TYPE RESOURCE-NAME RESOURCE-NAME "the name of a resource")
      ;The second try, it is a resource for sure	 
	 (SETQ RESOURCE (GET RESOURCE-NAME 'DEFRESOURCE))))
  (AND (SETQ TEM (RESOURCE-PARAMETIZER RESOURCE))
       (< (LENGTH PARAMS) (LDB %%ARG-DESC-MAX-ARGS (%ARGS-INFO TEM)))
       (SETQ PARAMS (APPLY TEM PARAMS)))
  (WITHOUT-INTERRUPTS
    (COND ((SETQ TEM (RESOURCE-FINDER RESOURCE))
	   (SETQ TEM (APPLY TEM RESOURCE PARAMS)))
	  ((RESOURCE-FREE-LIST-CELL RESOURCE)
	   (DO ((CHECKER (RESOURCE-CHECKER RESOURCE))
		(MATCHER (RESOURCE-MATCHER RESOURCE))
		(CELL (LOCF (RESOURCE-FREE-LIST RESOURCE))
		      (FUNCALL (RESOURCE-FREE-LIST-CELL RESOURCE) (CONTENTS CELL))))
	       ((NULL (CONTENTS CELL))
		;; make new object.  PARAMS not copied since it is assumed they will not be stored.
		(SETQ INHIBIT-SCHEDULING-FLAG OLD)
		(SETQ TEM (APPLY (RESOURCE-CONSTRUCTOR RESOURCE) RESOURCE PARAMS))
		(SETQ INHIBIT-SCHEDULING-FLAG T))
	     (LET ((OBJ (CONTENTS CELL)))
	       (WHEN (AND (or (null CHECKER)
			      (APPLY CHECKER RESOURCE OBJ NIL PARAMS))  ;IN-USE-P NIL
			  (IF MATCHER
			      (APPLY MATCHER RESOURCE OBJ PARAMS)
			      (NULL PARAMS)))	;PARAMS not retained.
		 (SETF (CONTENTS CELL) (CONTENTS (FUNCALL (RESOURCE-FREE-LIST-CELL RESOURCE) OBJ)))
		 (RETURN (SETQ TEM OBJ))))))
	  ((LOOP WITH CHECKER = (RESOURCE-CHECKER RESOURCE)
		 WITH MATCHER = (RESOURCE-MATCHER RESOURCE)
		 WITH N-OBJECTS = (RESOURCE-N-OBJECTS RESOURCE)
		 FOR N FROM (1- N-OBJECTS) DOWNTO 0
		 AS IN-USE-P = (RESOURCE-IN-USE-P RESOURCE N)
		 AS OBJ = (RESOURCE-OBJECT RESOURCE N)
	      WHEN (AND (IF CHECKER
			    (APPLY CHECKER RESOURCE OBJ IN-USE-P PARAMS)
			  (NOT IN-USE-P))
			(IF MATCHER (APPLY MATCHER RESOURCE OBJ PARAMS)
			  (OR (NULL PARAMS)
			      (EQUAL (RESOURCE-PARAMETERS RESOURCE N) PARAMS))))
	        DO (SETF (RESOURCE-IN-USE-P RESOURCE N) T)
		   (RETURN (SETQ TEM OBJ))))
	  (T (SETQ INHIBIT-SCHEDULING-FLAG OLD)
	     (SETQ PARAMS (COPY-LIST PARAMS))
	     (SETQ TEM (APPLY (RESOURCE-CONSTRUCTOR RESOURCE) RESOURCE PARAMS))
	     (SETQ INHIBIT-SCHEDULING-FLAG T)
	     (SETF (RESOURCE-N-OBJECTS RESOURCE)
		   (1+ (SETQ INDEX (RESOURCE-N-OBJECTS RESOURCE))))
	     (WHEN ( INDEX (ARRAY-DIMENSION RESOURCE 0))
	       (SETF (GET (RESOURCE-NAME RESOURCE) 'DEFRESOURCE)
		     (SETQ RESOURCE (ARRAY-GROW RESOURCE
						(+ INDEX (MAX 20. (TRUNCATE INDEX 2)))
						3))))
	     (SETF (RESOURCE-OBJECT RESOURCE INDEX) TEM)
	     (SETF (RESOURCE-IN-USE-P RESOURCE INDEX) T)
	     (SETF (RESOURCE-PARAMETERS RESOURCE INDEX)			;Avoid lossage with
		   (IF (EQ PARAMS PARAMETERS) (COPY-LIST PARAMS)	;as little consing
		     PARAMS)))))					;as possible.
  ;; TEM now is the object
  (WHEN (SETQ INITIALIZER (RESOURCE-INITIALIZER RESOURCE))
    (APPLY INITIALIZER RESOURCE TEM PARAMS))
  TEM)

(DEFUN DEALLOCATE-RESOURCE (RESOURCE-NAME OBJECT &AUX RESOURCE)
  "Return OBJECT to the free pool of resource RESOURCE-NAME.
OBJECT should have been returned by a previous call to ALLOCATE-RESOURCE."
 ;Amazingly slow, see ALLOCATE-RESOURCE..
 ;  (CHECK-TYPE RESOURCE-NAME RESOURCE-NAME "the name of a resource")
  (cond ((null (SETQ RESOURCE (GET RESOURCE-NAME 'DEFRESOURCE)))
	 (CHECK-TYPE RESOURCE-NAME RESOURCE-NAME "the name of a resource")
	 (SETQ RESOURCE (GET RESOURCE-NAME 'DEFRESOURCE))))
  (COND ((RESOURCE-DEINITIALIZER RESOURCE)
	 (FUNCALL (RESOURCE-DEINITIALIZER RESOURCE) OBJECT)))
  (COND ((RESOURCE-FREE-LIST-CELL RESOURCE)
	 (WITHOUT-INTERRUPTS
	   (RPLACA (FUNCALL (RESOURCE-FREE-LIST-CELL RESOURCE) OBJECT)
		   (RESOURCE-FREE-LIST RESOURCE))
	   (SETF (RESOURCE-FREE-LIST RESOURCE) OBJECT)))
	((NOT (RESOURCE-FINDER RESOURCE))
	 (LOOP WITH N-OBJECTS = (RESOURCE-N-OBJECTS RESOURCE)
	       FOR N FROM (1- N-OBJECTS) DOWNTO 0
	    WHEN (EQ (RESOURCE-OBJECT RESOURCE N) OBJECT)
	      ;; Note that this doesn't need any locking.
	      DO (RETURN (SETF (RESOURCE-IN-USE-P RESOURCE N) NIL))
	    FINALLY (FERROR "~S is not an object from the ~S resource"
			    OBJECT RESOURCE-NAME)))))

(DEFUN DEALLOCATE-WHOLE-RESOURCE (RESOURCE-NAME &AUX RESOURCE)
  "Return all objects allocated from resource RESOURCE-NAME to the free pool."
  (CHECK-TYPE RESOURCE-NAME RESOURCE-NAME "the name of a resource")
  (SETQ RESOURCE (GET RESOURCE-NAME 'DEFRESOURCE))
  (COND ((NOT (OR (RESOURCE-FINDER RESOURCE) (RESOURCE-FREE-LIST-CELL RESOURCE)))
	 (LOOP WITH N-OBJECTS = (RESOURCE-N-OBJECTS RESOURCE)
	       FOR N FROM 0 BELOW N-OBJECTS
	    DO (SETF (RESOURCE-IN-USE-P RESOURCE N) NIL)))))

(DEFMACRO USING-RESOURCE (&ENVIRONMENT ENV (VAR RESOURCE-NAME &REST PARAMETERS) &BODY BODY)
  "Execute BODY with VAR bound to an object allocated from resource RESOURCE-NAME.
PARAMETERS are used in selecting or creating the object,
according to the definition of the resource."
  (CHECK-TYPE RESOURCE-NAME SYMBOL)
  (MULTIPLE-VALUE-BIND (BODY DECLARATIONS)
      (EXTRACT-DECLARATIONS BODY NIL NIL ENV)
    `(LET ((,VAR NIL))
       (DECLARE . ,DECLARATIONS)
       (UNWIND-PROTECT
	   (PROGN
	     (SETQ ,VAR (ALLOCATE-RESOURCE ',RESOURCE-NAME . ,PARAMETERS))
	     . ,BODY)
	 (AND ,VAR (DEALLOCATE-RESOURCE ',RESOURCE-NAME ,VAR))))))

;For compatibility with old programs
;(DEFF WITH-RESOURCE 'USING-RESOURCE)

