4

Situation

During a development I implemented the design pattern called object-pool-pattern. Basically this means that our class has a static public method and a static-protected attribute. This attribute carries all instances, which can be retrieved by the static public method if a certain object key is provided. I use a method called get_instance to retrieve the instance into a local reference variable and then invoke other methods on it. In our case, a wrapping function module calls get_instance, and later on invokes some methods on that instance. In a particular case the lifetime of a certain instance ended. So, it has to be removed from the object pool. Until now I do it inside an instance-method, where the object deletes itself from the pool (a table).

Assumption

The outer reference in the function module should have become invalid, because it does reference a not existing object. The garbage collector should now carry about this orphaned reference, If I assume correctly. (In general this goes the other way around: A not referenced object is generally killed by the GC.) In this case I still assume that the local reference in my function module should also become collected. Because the object it was referring to, was deleted from the object-pool. Nevertheless, until now, I still call Free lr_myreference. A co-worker said, it might be better to implement a static public method, which would NOT act on the reference but simply killing the entry of the pool, prior to calling Free lr_myreference.

Questions

In general I am thinking about: Who is responsible for deleting the object from the pool? What about other local references referring to the entry in the table (the "original reference")?

vwegert
  • 18,371
  • 3
  • 37
  • 55
icbytes
  • 1,831
  • 1
  • 17
  • 27

2 Answers2

4

I use the very same pattern you described rather frequently, preferably with some hash table kept as a static attribute of a factory/manager/pool class.

Personally, I try to avoid objects that need to be finalized explicitly wherever possible. From my experience, as the application grows more complex, someone will always tend to forget the finalization. This in turn might lead to any number of undesirable side effects that are notoriously hard to debug. I try to follow the basic assumption that the garbage collector will know best when to remove an object, and that once the object is gone, that's all there is. As long as anyone (a function group, another class, doesn't matter) keeps a reference to an object, it is still in use and won't be gc'ed. Aside from the Control Framework, where you need to explicitly deconstruct object instances frequently, this approach works quite well.

The apparent downside of this approach is that the object pool will tend to grow unless implemented properly. My instance hash tables don't contain hard references to the instances; instead, I use CL_ABAP_WEAK_REFERENCE to keep track of the managed objects. This allows for the garbage collector to remove all managed instances that are not in use elsewhere. Of course, this also means that you need to track instances that you still need via other direct references (for example using a lock object that encapsulates the ENQUEUE/DEQUEUE calls and at the same time serves as a lock token - whoever is the current owner of that object is also responsible for releasing the lock again).

Caution: There's a fairly common structural mistake that has to be avoided when using weak references extensively. I've written an SCN article some time ago that contains a (counter)example. Bottom line: If a managing object (in your example, the pool class) is referenced via a weak reference, make sure that the managed instances have a hard reference to the manager - otherwise you might unexpectedly end up with several sets of managed instances that represent the same thing.

vwegert
  • 18,371
  • 3
  • 37
  • 55
  • Thx, vwegert. Good to know the stuff with weak reference. By the way, if the object is based on a sap-standard-object (let's say a quality notification), would You let the ctor create it implicitely (until now most of my object-pool-implementing classes use a protected/private ctor, which is only invoked inside "get_instance". But now I would like to know, whether an object, which relies on some standard business object should create it implicitely in the ctor, afterwards add the instance to the pool and then return it's reference to the client-caller. Can we answer this in general at all ? – icbytes Dec 09 '15 at 13:47
2

In essence you are temporarily transferring ownership of (or at least lending) the object from the pool to the caller of get_instance and so the pool cannot kill the object or remove it from the pool until it knows that the new owner has finished using the object. If it does then the current owner may be left with an invalid object.

So in order for the pool to do its job correctly you need to return the object to the pool (or tell it that you have finished using it) so the pool can know which objects are not currently in use and can destroy them safely.

I would have a return_instance object which would do this.

If you have multiple clients calling get_instance and you are giving them the same instance then you'll need to track by reference count, otherwise you can just keep the references in two lists, one for available objects and another for currently loaned out objects, and just switch between the lists when get_instance and return_instance are called.

The clients should not be responsible for destroying the objects, otherwise what's the point of having a pool? Just have a factory instead...

I asked a slightly related question a while ago, which influenced my thinking

Community
  • 1
  • 1
Sam Holder
  • 32,535
  • 13
  • 101
  • 181
  • I am understanding, that the client lends the instance, but IT WORKS on one and the same. Meaning, If my local reference variable will change any attribute-value, the object in the pool-table also will change it's value, because the client referrs to this object in the pool-table. So why is there any need to create "return_instance" at all ? – icbytes Dec 08 '15 at 16:48
  • you need to return the instance so that the pool knows when it can safely call `Dispose()` (or whatever cleanup method). The client can't call the clean up method, as the pool will still have a reference and may want to reuse the object. If the client is disposing then you don't have a pool, you have a factory. – Sam Holder Dec 09 '15 at 09:32
  • Thx for clarification – icbytes Dec 09 '15 at 10:19