View Javadoc
1   /*
2    * Copyright 2013 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
17  package io.netty.util.concurrent;
18  
19  import io.netty.util.internal.DefaultPriorityQueue;
20  import io.netty.util.internal.PriorityQueueNode;
21  
22  import java.util.Queue;
23  import java.util.concurrent.Callable;
24  import java.util.concurrent.Delayed;
25  import java.util.concurrent.TimeUnit;
26  import java.util.concurrent.atomic.AtomicLong;
27  
28  @SuppressWarnings("ComparableImplementedButEqualsNotOverridden")
29  final class ScheduledFutureTask<V> extends PromiseTask<V> implements ScheduledFuture<V>, PriorityQueueNode {
30      private static final AtomicLong nextTaskId = new AtomicLong();
31      private static final long START_TIME = System.nanoTime();
32  
33      static long nanoTime() {
34          return System.nanoTime() - START_TIME;
35      }
36  
37      static long deadlineNanos(long delay) {
38          long deadlineNanos = nanoTime() + delay;
39          // Guard against overflow
40          return deadlineNanos < 0 ? Long.MAX_VALUE : deadlineNanos;
41      }
42  
43      private final long id = nextTaskId.getAndIncrement();
44      private long deadlineNanos;
45      /* 0 - no repeat, >0 - repeat at fixed rate, <0 - repeat with fixed delay */
46      private final long periodNanos;
47  
48      private int queueIndex = INDEX_NOT_IN_QUEUE;
49  
50      ScheduledFutureTask(
51              AbstractScheduledEventExecutor executor,
52              Runnable runnable, V result, long nanoTime) {
53  
54          this(executor, toCallable(runnable, result), nanoTime);
55      }
56  
57      ScheduledFutureTask(
58              AbstractScheduledEventExecutor executor,
59              Callable<V> callable, long nanoTime, long period) {
60  
61          super(executor, callable);
62          if (period == 0) {
63              throw new IllegalArgumentException("period: 0 (expected: != 0)");
64          }
65          deadlineNanos = nanoTime;
66          periodNanos = period;
67      }
68  
69      ScheduledFutureTask(
70              AbstractScheduledEventExecutor executor,
71              Callable<V> callable, long nanoTime) {
72  
73          super(executor, callable);
74          deadlineNanos = nanoTime;
75          periodNanos = 0;
76      }
77  
78      @Override
79      protected EventExecutor executor() {
80          return super.executor();
81      }
82  
83      public long deadlineNanos() {
84          return deadlineNanos;
85      }
86  
87      public long delayNanos() {
88          return Math.max(0, deadlineNanos() - nanoTime());
89      }
90  
91      public long delayNanos(long currentTimeNanos) {
92          return Math.max(0, deadlineNanos() - (currentTimeNanos - START_TIME));
93      }
94  
95      @Override
96      public long getDelay(TimeUnit unit) {
97          return unit.convert(delayNanos(), TimeUnit.NANOSECONDS);
98      }
99  
100     @Override
101     public int compareTo(Delayed o) {
102         if (this == o) {
103             return 0;
104         }
105 
106         ScheduledFutureTask<?> that = (ScheduledFutureTask<?>) o;
107         long d = deadlineNanos() - that.deadlineNanos();
108         if (d < 0) {
109             return -1;
110         } else if (d > 0) {
111             return 1;
112         } else if (id < that.id) {
113             return -1;
114         } else if (id == that.id) {
115             throw new Error();
116         } else {
117             return 1;
118         }
119     }
120 
121     @Override
122     public void run() {
123         assert executor().inEventLoop();
124         try {
125             if (periodNanos == 0) {
126                 if (setUncancellableInternal()) {
127                     V result = task.call();
128                     setSuccessInternal(result);
129                 }
130             } else {
131                 // check if is done as it may was cancelled
132                 if (!isCancelled()) {
133                     task.call();
134                     if (!executor().isShutdown()) {
135                         long p = periodNanos;
136                         if (p > 0) {
137                             deadlineNanos += p;
138                         } else {
139                             deadlineNanos = nanoTime() - p;
140                         }
141                         if (!isCancelled()) {
142                             // scheduledTaskQueue can never be null as we lazy init it before submit the task!
143                             Queue<ScheduledFutureTask<?>> scheduledTaskQueue =
144                                     ((AbstractScheduledEventExecutor) executor()).scheduledTaskQueue;
145                             assert scheduledTaskQueue != null;
146                             scheduledTaskQueue.add(this);
147                         }
148                     }
149                 }
150             }
151         } catch (Throwable cause) {
152             setFailureInternal(cause);
153         }
154     }
155 
156     /**
157      * {@inheritDoc}
158      *
159      * @param mayInterruptIfRunning this value has no effect in this implementation.
160      */
161     @Override
162     public boolean cancel(boolean mayInterruptIfRunning) {
163         boolean canceled = super.cancel(mayInterruptIfRunning);
164         if (canceled) {
165             ((AbstractScheduledEventExecutor) executor()).removeScheduled(this);
166         }
167         return canceled;
168     }
169 
170     boolean cancelWithoutRemove(boolean mayInterruptIfRunning) {
171         return super.cancel(mayInterruptIfRunning);
172     }
173 
174     @Override
175     protected StringBuilder toStringBuilder() {
176         StringBuilder buf = super.toStringBuilder();
177         buf.setCharAt(buf.length() - 1, ',');
178 
179         return buf.append(" id: ")
180                   .append(id)
181                   .append(", deadline: ")
182                   .append(deadlineNanos)
183                   .append(", period: ")
184                   .append(periodNanos)
185                   .append(')');
186     }
187 
188     @Override
189     public int priorityQueueIndex(DefaultPriorityQueue<?> queue) {
190         return queueIndex;
191     }
192 
193     @Override
194     public void priorityQueueIndex(DefaultPriorityQueue<?> queue, int i) {
195         queueIndex = i;
196     }
197 }