I ran the postal test for a little while against Harmeet's server with this
latest change.  It appeared to fix the known problem, right up until the
point when his system died.  Don't know what the cause was.  IF this code is
OK, it improves upon the DefaultAvalonScheduler for James' needs.

Performing a resetTrigger is a significantly more costly operation than
either the Watchdog implementation or the Avalon scheduler implementation,
demonstrating some of the challenges involved in maintaining a
single-threaded multi-event timing mechanism.

The high duty cycle is this code (I've unwrapped one method):

   synchronized void reset(String id) {
       Entry entry = (Entry)idMap.remove(id);
       timerMap.remove(entry.scheduledTime);
       entry = new Entry(entry.offset,entry.id,entry.trg);

       // move by on millisec, if there is a stored entry
       while ( timerMap.containsKey(entry.scheduledTime) ) {
           entry = new Entry(entry.offset+1,entry.id,entry.trg);
       }
       timerMap.put(entry.scheduledTime,entry);
       idMap.put(entry.id,entry);
   }

Resetting the timer involves removing from both a TreeMap and a HashMap,
creating a new Event object, iteratively SEARCHING the timerMap to find the
next open time after the one we want, creating a new Event each iteration,
and then adding the final Event to both the TreeMap and the HashMap.

The code wakes up periodically to check the next entry in the TreeMap.  One
limitation in this implementation is that once the scheduler starts
sleeping, it cannot effectively handle a time for less than the current wake
time, due to the operational loop:

   synchronized void execFirstTask() throws InterruptedException {
       // sleep if there is nothing.
       while ( timerMap.isEmpty() )
           Thread.currentThread().sleep(1000);

       Long first = (Long)timerMap.firstKey();
       long future = first.longValue()-System.currentTimeMillis();
       if ( future > 0 )
           Thread.currentThread().sleep(future);
       else {
           Entry entry = (Entry)timerMap.remove(first);
           idMap.remove(entry.id);
           entry.trg.targetTriggered(entry.id);
       }
   }

One way to fix that is to modify the code so that if the new trigger time is
less than the current wake time, that the thread is interrupted so that it
can go back to sleep for the correct time.

Another change is that the constrant re-creation of Entry objects should be
replaced by a mutator method that changes the internal values.

This basically showcases the tradeoffs between a multi-threaded solution and
a single-threaded solution.  There is still significant and unavoidable
overhead due to all of the collection manipulation, creation of a
non-mutable Long object to be a key value, and synchronization.  The
tradeoff is that it runs with one thread.  However, if having more threads
is an issue in some implementation, this is one way to do it.

As I mentioned earlier, this code could easily sit underneath the Watchdog
interface, and I would actually prefer that solution for a variety of
reasons.  The chang in our handlers is trivial, it would be easier to Unit
test, and more reusable.  Given that we have our own implementation, there
is no reason to hardwire it to Avalon Frameworks.  A useful watchdog, with
multi-threaded and single-threaded implementations could be submitted to
Commons, and easily bridged to the Avalon Frameworks interfaces if that
group also wants to use it.  In our case, we could also use it in code that
is not married to Avalon Frameworks, e.g., multi-threaded mailets.

        --- Noel

-----Original Message-----
From: Harmeet Bedi [mailto:[EMAIL PROTECTED]]
Sent: Sunday, October 13, 2002 22:41
To: James Developers List
Subject: Re: [PATCH] Removing Scheduler dependency, refactoring service
code


Here is a patch that seems to hold up fine.
It involves a TimeScheduler implemenation.

Please try it out.

Ran with danny test, 20 threads and no upper bound. Seems to be stable and
not leak memory.

Please try it out.
thanks,
Harmeet


--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to