ScheduledThreadPoolExecutor.remove()

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

ScheduledThreadPoolExecutor.remove()

Gregg Wonderly-3
I'm confused on how to use remove(Runnable).  The code says that the argument
must be a ScheduledFutureTask before it will be removed.  However, Runnable is
not returned by any of the schedule() methods.  It would be nice to not have to
cast to do the remove((Runnable)schedFuture), nor cast the return of schedule to
Runnable.

Also, can ScheduledFutureTask have a toString() that returns toString() on the
embedded Runnable so that it is easier to track the contents and map that to
expected contents?

Gregg Wonderly
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: ScheduledThreadPoolExecutor.remove()

Joe Bowbeer
On 4/22/06, Gregg Wonderly <[hidden email]> wrote:
> I'm confused on how to use remove(Runnable). [...]

I understand your predicament.

A few details, in case they might lead to a workaround:

remove(Runnable) is a low level operation on the Runnable queue
inherited from ThreadPoolExecutor.

I'm curious to know why cancelling the scheduledFuture won't suffice
in your case.  To prevent cancelled tasks from building up in the
Runnable queue, you can call purge() periodically.


On 4/22/06, Gregg Wonderly <[hidden email]> wrote:

> I'm confused on how to use remove(Runnable).  The code says that the argument
> must be a ScheduledFutureTask before it will be removed.  However, Runnable is
> not returned by any of the schedule() methods.  It would be nice to not have to
> cast to do the remove((Runnable)schedFuture), nor cast the return of schedule to
> Runnable.
>
> Also, can ScheduledFutureTask have a toString() that returns toString() on the
> embedded Runnable so that it is easier to track the contents and map that to
> expected contents?
>
> Gregg Wonderly
>

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: ScheduledThreadPoolExecutor.remove()

Holger Hoffstätte-2
In reply to this post by Gregg Wonderly-3
Gregg Wonderly wrote:
> I'm confused on how to use remove(Runnable).  The code says that the
> argument must be a ScheduledFutureTask before it will be removed.

Things are worse than you might suspect because even if you succeed (for
all the wrong reasons) you'll reap a ClassCastException. You can take a
loot at the archives for a thread in last December called "Confusing API:
ScheduledThreadPoolExecutor vs. remove()" which will explain all the
issues; in short: the docs are a bit misleading and there's a hidden JDK
bug that was fixed in Mustang b71.
If you really need a custom ScheduledFutureExecutor I can send you a test
class that exposes the bug and demonstrates how to get this right.

Holger

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: ScheduledThreadPoolExecutor.remove()

Gregg Wonderly-2
In reply to this post by Joe Bowbeer


Joe Bowbeer wrote:
> I'm curious to know why cancelling the scheduledFuture won't suffice
> in your case.  To prevent cancelled tasks from building up in the
> Runnable queue, you can call purge() periodically.

In my application, I am cancelling 100% of these timers in the normal case.  I
just want the task out of the queue with an easy and guaranteed removal.  There
are perhaps 70 of these created per second in this application.

Gregg Wonderly
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: ScheduledThreadPoolExecutor.remove()

Doug Lea
Gregg Wonderly wrote:

>
>
> Joe Bowbeer wrote:
>> I'm curious to know why cancelling the scheduledFuture won't suffice
>> in your case.  To prevent cancelled tasks from building up in the
>> Runnable queue, you can call purge() periodically.
>
> In my application, I am cancelling 100% of these timers in the normal
> case.  I just want the task out of the queue with an easy and guaranteed
> removal.  There are perhaps 70 of these created per second in this
> application.
>

Joe's advice is still best. The worker threads will often be
contending with and racing with purge() to extract tasks (in which case the
worker threads generally win, in which case, the cancelled tasks won't run
anyway.) You want to balance contention with resource management, so
in your case, calling purge every minute or so sounds like a reasonable
way to go about it.

Someday, we'll have to consider classes that are designed for use
in network protocols and the like in which many timeout tasks are
scheduled, but very few ever run. ScheduledThreadPoolExecutor isn't
really optimized for this case.

-Doug

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: ScheduledThreadPoolExecutor.remove()

Joe Bowbeer
In reply to this post by Gregg Wonderly-2
70 per second doesn't sound excessive.  I expect simple cancellation
will suffice without causing a jam.

If garbage pile-up is a problem, I'm thinking you might be able to
handle this effectively in the rejected execution handler: something
similar to discard-oldest, but instead of discarding a live task,
purge the queue of any lingering cancelled tasks at this time.

Purging a bunch of tasks from the queue once in a while should be more
efficient than removing each one individually.


On 4/23/06, Gregg Wonderly <[hidden email]> wrote:

>
>
> Joe Bowbeer wrote:
> > I'm curious to know why cancelling the scheduledFuture won't suffice
> > in your case.  To prevent cancelled tasks from building up in the
> > Runnable queue, you can call purge() periodically.
>
> In my application, I am cancelling 100% of these timers in the normal case.  I
> just want the task out of the queue with an easy and guaranteed removal.  There
> are perhaps 70 of these created per second in this application.
>
> Gregg Wonderly
>

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: ScheduledThreadPoolExecutor.remove()

Dawid Kurzyniec
In reply to this post by Doug Lea
Doug Lea wrote:

> Gregg Wonderly wrote:
>>
>>
>> Joe Bowbeer wrote:
>>> I'm curious to know why cancelling the scheduledFuture won't suffice
>>> in your case.  To prevent cancelled tasks from building up in the
>>> Runnable queue, you can call purge() periodically.
>>
>> In my application, I am cancelling 100% of these timers in the normal
>> case.  I just want the task out of the queue with an easy and
>> guaranteed removal.  There are perhaps 70 of these created per second
>> in this application.
>>

> You want to balance contention with resource management, so
> in your case, calling purge every minute or so sounds like a reasonable
> way to go about it.
>
> Someday, we'll have to consider classes that are designed for use
> in network protocols and the like in which many timeout tasks are
> scheduled, but very few ever run. ScheduledThreadPoolExecutor isn't
> really optimized for this case.

I had a very similar problem to Gregg. The solution that worked out for
me was - as you say - to cancel and then to purge periodically, where
"periodically" is not at a fixed time interval though, but based on a
simple heuristics. After a task is canceled, the "cancellation count" is
incremented. Then, purge() is called if (1) the cancellation count
exceeds a threshold (e.g. 10000) or if the ratio of the cancellation
count to the queue length exceeds a threshold (e.g. 0.75). This seems to
work reasonably, since it assures that the linear purge is called
sparingly if queues are long and cancellations rare, but at the same
time, if 100% or close of all tasks are being canceled, they are purged
very quickly. I wonder if anybody has any comments on this approach?

Regards,
Dawid

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: ScheduledThreadPoolExecutor.remove()

Gregg Wonderly-2
In reply to this post by Joe Bowbeer


Joe Bowbeer wrote:
> 70 per second doesn't sound excessive.  I expect simple cancellation
> will suffice without causing a jam.

What it comes down to is proof of correctness for my application.  It has to
work, no matter what code paths are involved.  Adding a scheduled thread to
periodically do a purge might seems sensible at first glance.  However, for me,
guessing a time and having the system be dependent on the load and that timing
is perilous.  Involving a third party for purging just doesn't excite me.  I was
doing this before with java.util.Timer (once we had purge).

The queue and ScheduledThreadPoolExecutor is already a multi-threaded, contended
resource, so I'm a little surprised at the adversion to remove().  I suppose
that a lock to remove 20 entries is cheaper (cache issues and locking) than 20
locks.

> If garbage pile-up is a problem, I'm thinking you might be able to
> handle this effectively in the rejected execution handler: something
> similar to discard-oldest, but instead of discarding a live task,
> purge the queue of any lingering cancelled tasks at this time.
>
> Purging a bunch of tasks from the queue once in a while should be more
> efficient than removing each one individually.

Logically, this all make sense, practically it complicates the application
tremendously and makes it necessary for different parts of the code to know
about what the others are doing.  I don't need a limit on the queue.  I just
need to be able to schedule these timeout events, and cancel them when the work
completes normally.

For now, I have

ScheduledThreadPoolExecutor schedExec;
ScheduledFuture toCancel;

public void setTimeout( long timeout ) {
        synchronized( schedExec ) {
                toCancel = schedExec.schedule(
                        this, timeout, TimeUnits.SECONDS );
        }
}

and

volatile boolean didRun;
public void cancel() {
        boolean how = false;

        synchronized( schedExec ) {
                how = toCancel.cancel();
                schedExec.remove( (Runnable)toCancel );
        }

        if( !how && !didRun ) {
                throw new IllegalStateException(
                        "Did not run nor cancel: "+this );
        }
}

public void run() {
        didRun = true;
        ... code to handle timeout ...
}

Gregg Wonderly
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest