WeakArray.st
author claus
Thu, 09 Mar 1995 00:40:27 +0100
changeset 302 1f76060d58a4
parent 290 23994c0a7f7b
child 308 f04744ef7b5d
permissions -rw-r--r--
*** empty log message ***

"
 COPYRIGHT (c) 1991 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

Array variableSubclass:#WeakArray
       instanceVariableNames:'watcher dependents'
       classVariableNames:'RegistrationFailedSignal AlreadyInitialized'
       poolDictionaries:''
       category:'Collections-Arrayed'
!

WeakArray comment:'
COPYRIGHT (c) 1991 by Claus Gittinger
	      All Rights Reserved

$Header: /cvs/stx/stx/libbasic/WeakArray.st,v 1.10 1995-03-08 23:40:27 claus Exp $
'!

!WeakArray class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1991 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

version
"
$Header: /cvs/stx/stx/libbasic/WeakArray.st,v 1.10 1995-03-08 23:40:27 claus Exp $
"
!

documentation
"
    WeakArrays can be used to trace disposal of objects; in contrast to other
    objects, references by WeakArrays will NOT keep an object from dieing.
    Instead, whenever an object kept in a WeakArray dies, its entry is nilled,
    and the WeakArray informed by the storage manager. 

    You can use WeakArrays to track disposal of objects which keep external
    world resources. For example, FileStreams must close their underlying
    file when disposed (otherwise you could run out of OS filedescriptors).
    This can be done by keeping the FileStream objects in a weakArray, and
    keep a parallel array of filedescriptors. Whenever a fileStream gets
    free, search both arrays for an index where the stream is nil, but the
    filedescriptor is non-nil. Then close that file, and nil the filedescriptor
    entry. Notice, that there is a class (Registry) which does exactly this in
    a more programmer friendly way.

    Another application is caching of data: keep it in a weakArray, so the
    data in that cache will not be unreclaimable due to being cached.
    (for example, the ResourcePack class uses a WeakArray to cache recently
    used resource data for a while).

    The way in which weakArrays get informed by the runtime system is via
    an interrupt (DisposeInterrupt). The reason for not sending messages
    directly from the VM is to make it possible to run the finalization
    code at lower priority or from another class. 
    Also, as a side effect, it is possible to delay finalization by blocking 
    interrupts.

    This interrupt is cought in ObjectMemory and informs the weakArray,
    which in turn informs its watcher and pissibly the dependents. 
    Having two mechanisms here (i.e. watcher & dependent) is a historic leftover;
    I dont know, which of the two mechanisms will survive in the long run - 
    I started with the watcher, but now switch to dependencies since they seem 
    to offer more flexibility (although watcher notification ).
    Be prepared, that the watcher mechanism may vanish in the future 
    (i.e. use dependents for your applications).

    NOTICE: WeakArray handling adds some overhead to the VM (each weakarray
    is scanned after each GC). It is uncertain, if the current mechanism works well
    with (say) ten-thousands of weakArrays.

    instanceVariables:

	watcher                         if non-nil, gets informed via #informDispose
					that the weakArray has lost pointers.

	dependents                      get informed via #change 
					that the weakArray has lost pointers.


    classVariables:

	RegistrationFailedSignal        raised if a weakArray cannot be
					registered by the VM. This only happens,
					if the VM has to resize its shadow tables
					and is running out of malloc-memory.
"
! !

!WeakArray class methodsFor:'initialization'!

initialize
    "setup the private signal"

    RegistrationFailedSignal isNil ifTrue:[
	ErrorSignal isNil ifTrue:[super initialize].

	RegistrationFailedSignal := ErrorSignal newSignalMayProceed:true.
	RegistrationFailedSignal nameClass:self message:#registrationFailedSignal.
	RegistrationFailedSignal notifierString:'weakArray registration failed'.
    ]
! !

!WeakArray class methodsFor:'instance creation'!

new:size
    "return a new weakArray with size slots"

    "This is a kludge: I would like to set WEAK-flag in the classes
     initialize method, but (currently) the order in which the class-initialize
     methods are called is not defined ...
     ... therefore it could happen, that a WeakArray is used by other
     classes initialize method BEFORE this method is evaluated. 
     To avoid this, the WEAK bit in the class is set here, when the very first 
     WeakArray is created."

    AlreadyInitialized isNil ifTrue:[
	self flags:(Behavior flagWeakPointers).
	AlreadyInitialized := true
    ].

    ^ (self basicNew:size) registerAsWeakArray
! !

!WeakArray methodsFor:'GC registration'!

registerAsWeakArray
    "register the receiver in the VM - 
     i.e. tell the VM to nil disposed entries in the receiver
     and notify the disposeInterruptHandler whenever that happened."

    |ok|
%{
    OBJ __addShadowObject();

    ok = __addShadowObject(self, 0);
    if (ok == false) {
	/* 
	 * this happens when too many shadow objects are
	 * already there, collect garbage to get rid of
	 * obsolete ones, and try again.
	 * since a full collect is expensive, we try
	 * a scavenge first, doing a full collect only if 
	 * that does not help.
	 *
	 * THIS MAY OR MAY NOT BE A GOOD IDEA: although it reduces
	 * the number of shadow objects that have to be
	 * processed at GC time, it may create a long delay here,
	 * at shadow object creation time.
	 * Dont know which is better ...
	 */
	nonTenuringScavenge(__context);
	ok = __addShadowObject(self, 0);
	if (ok == false) {
	    /* 
	     * try more ... 
	     */
	    scavenge(__context);
	    ok = __addShadowObject(self, 0);
	    if (ok == false) {
		/* 
		 * hard stuff - need full collect
		 */
#ifdef OLD
		__garbageCollect(__context);

#else
		markAndSweep(__context);
#endif
		ok = __addShadowObject(self, 0);
		if (ok == false) {
		    /*
		     * mhmh - it seems that there are really many shadow 
		     * objects around - force creation
		     */
		    ok = __addShadowObject(self, 1);
		    if (ok == false) {
			/* no chance - something must be wrong */
		    }
		}
	    }
	}
    }
%}.
    ok ifFalse:[
	"
	 the VM was not able to register the new weakArray
	 This can only happen, if the VM has to resize its tables,
	 and a malloc request failed. Usually, this smells like big
	 trouble being on the way (soon running out of memory in
	 other places as well).
	 Configure your OS for more swap space.
	"
	^ RegistrationFailedSignal raiseRequestWith:self
    ]
! !

!WeakArray methodsFor:'notification'!

lostPointer
    "I lost a pointer; tell watcher and dependents.
     This is sent from the finalization in ObjectMemory."

    dependents notNil ifTrue:[
	self changed.
    ].
    watcher notNil ifTrue:[
	watcher informDispose
    ]
! !

!WeakArray methodsFor:'copying'!

postCopy
    "copying alone does not really help - we have to tell
     the VM, that there is a new WeakArray around ...
     Q: who copies weakArrays ?"

    dependents := nil.
    self registerAsWeakArray.
! !

!WeakArray methodsFor:'accessing'!

dependents 
    "return the dependents of the receiver"

    ^ dependents
!

dependents:aCollection
    "set the dependents of the receiver"

    dependents := aCollection
!

watcher
    "return the watcher of the receiver.
     The watcher-stuff is a leftover from an old implementation 
     and will vanish soon"

    ^ watcher
!

watcher:anObject
    "set the watcher of the receiver.
     The watcher-stuff is a leftover from an old implementation 
     and will vanish soon"

    watcher := anObject
! !