[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Is there a resource manager in Allegro?

   Date: Fri, 08 Sep 89 11:07:48 PDT
   From: Darrell <shane%mishka@rand.org>

   I would like to create a pool of objects which would release and reclaim an 
   object, reusing reclaimed objects whenever possible.

Although Jim Veitch replied that there was no such code, actually
there is, but it requires some care and thought to use correctly.  The
code is undocumented and officially unsupported, but you could use it
if you really want.  Anyway, the functionality is simple enough for
anyone who wanted to recreate.  Here is the entire text of what is
already in Allegro.  The symbols are unexported.

(in-package :excl)

;; The resource var identifies the resource,
;; and in the present implementation also holds a list of free instances.
;; The make argument is a form which will be evaluated to make a new instance
;; of the resource.
;; The reset argument is the name of a function or macro of one argument
;; which will reset an instance of the resource to its initial state.

(defmacro def-dynamic-resource (resource-var &key make reset)
     (defvar ,resource-var)		;only simple defvar for bootstrap
     (setq ,resource-var nil)
     (eval-when (compile load eval)
       (setf (get ',resource-var 'dynamic-resource) ',(list make reset)))))

;; This macro dynamically allocates an instance of a resource during execution
;; of the body and returns the instance to the free list upon exit.
;; It protects carefully against multiprocessing hazards.
;; The variable argument, which typically is a special but need not be, is
;; bound to the resource instance.  The resource-var identifies the resource.

(defmacro with-dynamic-resource ((variable resource-var) &body body)
  (let* ((resource (get resource-var 'dynamic-resource))
	 (make (first resource))
	 (reset (second resource)))
    `(let ((,variable
	    ;; This form must evaluate in-line or be protected from scheduling.
	     (let ((obj (car ,resource-var)))
	       (if* obj
		  then (setq ,resource-var (cdr ,resource-var))
		       ;; Only this much must actually be protected.
		       ,@(and reset `((,reset obj)))
		  else ;;Presume new objects don't need reset.
       (unwind-protect (progn ,@body)
	 (let ((cell (cons ,variable nil)));May cause GC.
	   ;; The next forms must be protected from scheduler interrupts.
	    (setf (cdr cell) ,resource-var)
	    (setq ,resource-var cell)))))))

Here's an explaination of why you might *not* want to do this sort
of thing.

Allegro's generation scavenging GC is quite fast and especiually
efficient with objects are become garbage "soon" after they are
allocated.  Any resource mechanism (at least that doesn't interface
with the GC) forces the GC nonetheless to consider objects that are
not in use except for being on some resource list.  But the real
problem comes when resource objects live long enough to become
tenured.  Thereafter, whenever a "non-immediate" Lisp objects in new
space is stored in a slot of a resourced object, a rootset entry must
be created.  This can have a serious impact on efficiency.  (Refer to
the Allegro User Guide section on the GC for details.)  So the bottom
line is that you should verify there *really* is an efficiency problem
before you try to solve the problem.

Resourced objects still make sense if they will hold exclusively (or
*almost* exclusively) immediate objects, i.e., FIXNUMs and CHARs, or
if they are arrays of specialized types that hold "unboxed" data (see
Section 3.2: Data Types).  These might be useful for large data arrays
that are needed repeatedly; for instance, Allegro uses resources for
buffer of string output streams.  These are actually *more* efficient
than newly-allocated arrays because once they get tenured the gc does
not need to copy them at every scavenge.