AbstractScheduledEventExecutor.schedule(...) must accept delay <= 0.

Motivation:

As the javadoc of ScheduledExecutorService state:

Zero and negative delays (but not periods) are also allowed in schedule methods,and are treated as requests for immediate execution.

Modifications:

- Correctly handle delay <= 0.
- Add unit tests.

Result:

Fixes [#6627].
This commit is contained in:
Norman Maurer 2017-04-18 07:12:58 +02:00
parent c62564cf4c
commit 59a8292e36
2 changed files with 152 additions and 5 deletions

View File

@ -124,12 +124,11 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
}
@Override
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
ObjectUtil.checkNotNull(command, "command");
ObjectUtil.checkNotNull(unit, "unit");
if (delay < 0) {
throw new IllegalArgumentException(
String.format("delay: %d (expected: >= 0)", delay));
delay = 0;
}
return schedule(new ScheduledFutureTask<Void>(
this, command, null, ScheduledFutureTask.deadlineNanos(unit.toNanos(delay))));
@ -140,8 +139,7 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
ObjectUtil.checkNotNull(callable, "callable");
ObjectUtil.checkNotNull(unit, "unit");
if (delay < 0) {
throw new IllegalArgumentException(
String.format("delay: %d (expected: >= 0)", delay));
delay = 0;
}
return schedule(new ScheduledFutureTask<V>(
this, callable, ScheduledFutureTask.deadlineNanos(unit.toNanos(delay))));

View File

@ -0,0 +1,149 @@
/*
* Copyright 2017 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.util.concurrent;
import org.junit.Test;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
public class AbstractScheduledEventExecutorTest {
private static final Runnable TEST_RUNNABLE = new Runnable() {
@Override
public void run() {
}
};
private static final Callable<?> TEST_CALLABLE = Executors.callable(TEST_RUNNABLE);
@Test
public void testScheduleRunnableZero() {
TestScheduledEventExecutor executor = new TestScheduledEventExecutor();
ScheduledFuture<?> future = executor.schedule(TEST_RUNNABLE, 0, TimeUnit.NANOSECONDS);
assertEquals(0, future.getDelay(TimeUnit.NANOSECONDS));
assertNotNull(executor.pollScheduledTask());
assertNull(executor.pollScheduledTask());
}
@Test
public void testScheduleRunnableNegative() {
TestScheduledEventExecutor executor = new TestScheduledEventExecutor();
ScheduledFuture<?> future = executor.schedule(TEST_RUNNABLE, -1, TimeUnit.NANOSECONDS);
assertEquals(0, future.getDelay(TimeUnit.NANOSECONDS));
assertNotNull(executor.pollScheduledTask());
assertNull(executor.pollScheduledTask());
}
@Test
public void testScheduleCallableZero() {
TestScheduledEventExecutor executor = new TestScheduledEventExecutor();
ScheduledFuture<?> future = executor.schedule(TEST_CALLABLE, 0, TimeUnit.NANOSECONDS);
assertEquals(0, future.getDelay(TimeUnit.NANOSECONDS));
assertNotNull(executor.pollScheduledTask());
assertNull(executor.pollScheduledTask());
}
@Test
public void testScheduleCallableNegative() {
TestScheduledEventExecutor executor = new TestScheduledEventExecutor();
ScheduledFuture<?> future = executor.schedule(TEST_CALLABLE, -1, TimeUnit.NANOSECONDS);
assertEquals(0, future.getDelay(TimeUnit.NANOSECONDS));
assertNotNull(executor.pollScheduledTask());
assertNull(executor.pollScheduledTask());
}
@Test(expected = IllegalArgumentException.class)
public void testScheduleAtFixedRateRunnableZero() {
TestScheduledEventExecutor executor = new TestScheduledEventExecutor();
executor.scheduleAtFixedRate(TEST_RUNNABLE, 0, 0, TimeUnit.DAYS);
}
@Test(expected = IllegalArgumentException.class)
public void testScheduleAtFixedRateRunnableNegative() {
TestScheduledEventExecutor executor = new TestScheduledEventExecutor();
executor.scheduleAtFixedRate(TEST_RUNNABLE, 0, -1, TimeUnit.DAYS);
}
@Test(expected = IllegalArgumentException.class)
public void testScheduleWithFixedDelayZero() {
TestScheduledEventExecutor executor = new TestScheduledEventExecutor();
executor.scheduleWithFixedDelay(TEST_RUNNABLE, 0, -1, TimeUnit.DAYS);
}
@Test(expected = IllegalArgumentException.class)
public void testScheduleWithFixedDelayNegative() {
TestScheduledEventExecutor executor = new TestScheduledEventExecutor();
executor.scheduleWithFixedDelay(TEST_RUNNABLE, 0, -1, TimeUnit.DAYS);
}
private static final class TestScheduledEventExecutor extends AbstractScheduledEventExecutor {
@Override
public boolean isShuttingDown() {
return false;
}
@Override
public boolean inEventLoop(Thread thread) {
return true;
}
@Override
public void shutdown() {
// NOOP
}
@Override
public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
throw new UnsupportedOperationException();
}
@Override
public Future<?> terminationFuture() {
throw new UnsupportedOperationException();
}
@Override
public boolean isShutdown() {
return false;
}
@Override
public boolean isTerminated() {
return false;
}
@Override
public boolean awaitTermination(long timeout, TimeUnit unit) {
return false;
}
@Override
public void execute(Runnable command) {
throw new UnsupportedOperationException();
}
@Override
public EventExecutorGroup parent() {
return null;
}
}
}