You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@avalon.apache.org by Berin Loritsch <bl...@apache.org> on 2001/03/01 15:45:45 UTC
[Fwd: Pool Rework (was: Re: [Avalon][C2] ComponentPool problems under
Apache JMeter)]
-------- Original Message --------
Subject: Pool Rework (was: Re: [Avalon][C2] ComponentPool problems under Apache JMeter)
Date: Thu, 01 Mar 2001 09:44:44 -0500
From: Berin Loritsch <bl...@apache.org>
Reply-To: cocoon-dev@xml.apache.org
To: cocoon-dev@xml.apache.org, dims@yahoo.com
References: <20...@web118.yahoomail.com> <3A...@apache.org>
I am going to write out my plan for upgrading the Avalon pool package.
Concidering the fact that Avalon is a Server Framework, and most servers
live in a multithreaded environment, we need some design rework on the
pools.
The AbstractPool was not meant to be in a Multithreaded environment, so
it is suitable for a single threaded application. However its name should
be changed to reflect that fact and not be confused with the base class
that all pools should inherit from. The design of the abstract pool is
as optimized for a single threaded environment as you can get, but will
_*never*_ be reliable in any shape or form in a multithreaded environment.
Why? Because it is subject to a type of race condition that simply
synchronizing the get() and set() method won't solve:
It is built on an array of objects, and has an index pointing to the
current pool member:
[0][1][2][3]
^
When a get is called, the pointer is decremented
[0][1][2][3]
^
Obviously, when there are no more objects, the pool has to grow.
When a put is called, the pointer is incremented
[0][1][2][3]
^
For the problem domain and the efficiency that was trying to be attained,
this is a truly ingenious design. My kudos to Peter for coming up with it.
However, here is the type of race condition that it *cannot* prevent.
Two threads request a Poolable at the same time, but return them out of
order, also a third thread requests a Poolable after on of the objects
are returned:
Thread 1 get(object indexed 3):
[0][1][2][3]
^ <-
Thread 2 get(object indexed 2):
[0][1][2][3]
^ <-
Thread 1 put(object indexed 3):
[0][1][2][3]
-> ^
Thread 3 get(object indexed 2):
[0][1][2][3]
^ <-
Thread 2 put(object indexed 2):
[0][1][2][3]
-> ^
Thread 3 put(object indexed 2):
[0][1][2][3]
-> ^
So what has happened? Threads two and three are sharing an object! Since
the reason for a Pool is to pool high overhead objects that cannot be used
by more than one thread simultaneously, we cannot use any Pool based on this
algorithm in a multithreaded environment--as ingenious as it is.
The ThreadsafePool implementation extends this algorithm and makes the get()
and set() methods synchronized. This is not _truly_ a ThreadSafe object
because it cannot protect itself against the afformentioned Race Condition.
So how do we protect ourselves in a multithreaded environment? We have
two methods: Use a Stack, or use two Lists. Why? Because we need to have
a distinction between *active* objects and *inactive* objects. Under no
circumstances should a pool return an object that is already being used.
The Stack implementation is easy to implement, but does not keep track of
the active objects. The two list approach maintains a reference to the
objects in question so that resource limiting is much easier to implement.
NOTE: you can mix a Stack and a List.
It comes down to what is the most efficient object to use, but the gist
of the problem domain that we need to solve is that we cannot have a
Race Condition which has two threads sharing a reference to the same
object.
The initial state is this:
Inactive List Active List
------------- -----------
P1
P2
P3
P4
When multiple threads request an object, it _must_ be taken out of the
Inactive roster and placed in the Active roster (using the scenario above):
Thread 1 and 2 request an object
Inactive List Active List
------------- -----------
P3 P1 (Thread 1)
P4 P2 (Thread 2)
Thread 1 returns an object
Inactive List Active List
------------- -----------
P1 P2 (Thread 2)
P3
P4
Thread 3 requests an object
Inactive List Active List
------------- -----------
P3 P2 (Thread 2)
P4 P1 (Thread 3)
Threads 2 and 3 return their objects
Inactive List Active List
------------- -----------
P1
P2
P3
P4
The important thing to catch here is the sequence of events:
get()
-----
Inactive TemporalRef Active
| | |
| Remove(0) | |
|<----------+ |
| | Add(ref) |
| +-------------->|
| | |
put()
-----
Inactive TemporalRef Active
| | |
| | Remove(ref) |
| +-------------->|
| Add(ref) | |
|<----------+ |
| | |
The TemporalRef is the actual object that is being transfered
to and from the requesting thread. On a get(), we remove the
first item from the list (or stack.pop()), put it in our active
list and return that reference.
Now for the all important reason for using a List in the Active
references. On a put(), we remove the reference of that Object
from the list (Very important for it to be that particular object).
and add the object to the Inactive list (or stack.push()).
The pure stack approach will also work, but it is blind as to
whether the Pool actually originated the object or not.
---------------------------------------------------------------------
To unsubscribe, e-mail: cocoon-dev-unsubscribe@xml.apache.org
For additional commands, email: cocoon-dev-help@xml.apache.org
Re: Pool Rework
Posted by Berin Loritsch <bl...@apache.org>.
Peter Donald wrote:
>
> At 01:05 1/3/01 -0500, Berin Loritsch wrote:
> >I am working on a new Pool implementation using the algorithm previously
> >discussed (based on the JdbcConnectionPool) that would work properly.
> >The issue I am dealing with at this point in time is that we need the
> >ObjectFactory to handle the end of an Object's life. In Cocoon it would
> >call the destroy(), stop(), and other end of lifecycle events for an
> >Avalon Component. I propose this:
> >
> >ObjectFactory {
> > Object newInstance(); //fully prepared object
> > Class getCreatedClass(); //Class object
> > void decommission(Object); //takes care of end of life handling.
> >}
> >
> >This is important for ResourceLimiting implementations.
>
> yer that seems fine ;) Its at times like these you *almost* wish you were
> using C++ .... until of course you remember what it was like to use C++ ;)
Speaking of that....
I am going to have to completely rewrite my abstract pool because I hosed
up the get()/put() methods. Your formatting for the source code has got
my head beant in all sorts of directions, so I will reimplement using the
Cocoon standards which are much easier to read IMNSHO ;).
I much prefer to use "this." instead of "m_" because it is explicit, and
the compiler knows exactly what I do. I can't tell you the number of times
I had to help folks with bugs that were merely scoping issues. Both our
conventions address that issue (because according to your convention only
member variables have "m_"). It's just that my IDE will give me all the
methods and variables that are visible to me simply by typing in "this."
so there is less typing and less memory I have to worry about.
<DISCLAIMER>
I am not trying to start a new flame war on coding styles!!! That has
been hashed out many times on this list, and does not need to be hashed
out any more!
I am merely stating that I have a tougher time thinking in the coding
standards on the Avalon framework. I know Peter has a script that auto
formats the code, so personally I would rather commit correct code and
let him format it ;P
</DISCLAIMER>
I will get that done as soon as I possibly can...
Re: Pool Rework
Posted by Peter Donald <do...@apache.org>.
At 01:05 1/3/01 -0500, Berin Loritsch wrote:
>I am working on a new Pool implementation using the algorithm previously
>discussed (based on the JdbcConnectionPool) that would work properly.
>The issue I am dealing with at this point in time is that we need the
>ObjectFactory to handle the end of an Object's life. In Cocoon it would
>call the destroy(), stop(), and other end of lifecycle events for an
>Avalon Component. I propose this:
>
>ObjectFactory {
> Object newInstance(); //fully prepared object
> Class getCreatedClass(); //Class object
> void decommission(Object); //takes care of end of life handling.
>}
>
>This is important for ResourceLimiting implementations.
yer that seems fine ;) Its at times like these you *almost* wish you were
using C++ .... until of course you remember what it was like to use C++ ;)
Cheers,
Pete
*-----------------------------------------------------*
| "Faced with the choice between changing one's mind, |
| and proving that there is no need to do so - almost |
| everyone gets busy on the proof." |
| - John Kenneth Galbraith |
*-----------------------------------------------------*
Re: Pool Rework
Posted by Berin Loritsch <bl...@apache.org>.
Berin Loritsch wrote:
>
> Berin Loritsch wrote:
> >
> > Peter Donald wrote:
> > >
> > > Sounds good. Though all that threading is bound to make your hair curl ;)
> > > It got me going cross-eyed ;)
> >
I am working on a new Pool implementation using the algorithm previously
discussed (based on the JdbcConnectionPool) that would work properly.
The issue I am dealing with at this point in time is that we need the
ObjectFactory to handle the end of an Object's life. In Cocoon it would
call the destroy(), stop(), and other end of lifecycle events for an
Avalon Component. I propose this:
ObjectFactory {
Object newInstance(); //fully prepared object
Class getCreatedClass(); //Class object
void decommission(Object); //takes care of end of life handling.
}
This is important for ResourceLimiting implementations.
>
> Peter, Can the AbstractPool be a concrete class? If so, I would like to rename
> it to SingleThreadedPool, and have an AbstractPool that implements the design
> I had previously stated.
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: avalon-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: avalon-dev-help@jakarta.apache.org
Re: Pool Rework
Posted by Peter Donald <do...@apache.org>.
At 11:52 1/3/01 -0500, Berin Loritsch wrote:
>Berin Loritsch wrote:
>>
>> Peter Donald wrote:
>> >
>> > Sounds good. Though all that threading is bound to make your hair curl ;)
>> > It got me going cross-eyed ;)
>>
>
>Peter, Can the AbstractPool be a concrete class? If so, I would like to
rename
>it to SingleThreadedPool, and have an AbstractPool that implements the design
>I had previously stated.
That would be much better.
Cheers,
Pete
*-----------------------------------------------------*
| "Faced with the choice between changing one's mind, |
| and proving that there is no need to do so - almost |
| everyone gets busy on the proof." |
| - John Kenneth Galbraith |
*-----------------------------------------------------*
Re: Pool Rework
Posted by Berin Loritsch <bl...@apache.org>.
Berin Loritsch wrote:
>
> Peter Donald wrote:
> >
> > Sounds good. Though all that threading is bound to make your hair curl ;)
> > It got me going cross-eyed ;)
>
Peter, Can the AbstractPool be a concrete class? If so, I would like to rename
it to SingleThreadedPool, and have an AbstractPool that implements the design
I had previously stated.
Re: Pool Rework
Posted by Berin Loritsch <bl...@apache.org>.
Peter Donald wrote:
>
> Sounds good. Though all that threading is bound to make your hair curl ;)
> It got me going cross-eyed ;)
You didn't like my ASCII art? :(
So Peter, in your extensive testing it is obvious that the array performed
the best. However, by the last email, it is clear that an array won't work
in a multithreaded environment. My question to you is which Collection object
was the fastest that can satisfy our needs. One has to be a List or other
object that lets you remove a specific instance of an object. The other
has to let you pop off the next ready object (Stack, List, or other?).
I am appealing to you because you put alot of work into the current implementation
to find what performs the best.
BTW, let's keep Dims abreast of this thread, since he brought up the issue
in the first place.
Re: Pool Rework
Posted by Peter Donald <do...@apache.org>.
Sounds good. Though all that threading is bound to make your hair curl ;)
It got me going cross-eyed ;)
Cheers,
Pete
*-----------------------------------------------------*
| "Faced with the choice between changing one's mind, |
| and proving that there is no need to do so - almost |
| everyone gets busy on the proof." |
| - John Kenneth Galbraith |
*-----------------------------------------------------*