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    *   https://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.concurrent.Callable;
23  import java.util.concurrent.Delayed;
24  import java.util.concurrent.TimeUnit;
25  
26  @SuppressWarnings("ComparableImplementedButEqualsNotOverridden")
27  final class ScheduledFutureTask<V> extends PromiseTask<V> implements ScheduledFuture<V>, PriorityQueueNode {
28      // set once when added to priority queue
29      private long id;
30  
31      private long deadlineNanos;
32      /* 0 - no repeat, >0 - repeat at fixed rate, <0 - repeat with fixed delay */
33      private final long periodNanos;
34  
35      private int queueIndex = INDEX_NOT_IN_QUEUE;
36  
37      ScheduledFutureTask(AbstractScheduledEventExecutor executor,
38              Runnable runnable, long nanoTime) {
39  
40          super(executor, runnable);
41          deadlineNanos = nanoTime;
42          periodNanos = 0;
43      }
44  
45      ScheduledFutureTask(AbstractScheduledEventExecutor executor,
46              Runnable runnable, long nanoTime, long period) {
47  
48          super(executor, runnable);
49          deadlineNanos = nanoTime;
50          periodNanos = validatePeriod(period);
51      }
52  
53      ScheduledFutureTask(AbstractScheduledEventExecutor executor,
54              Callable<V> callable, long nanoTime, long period) {
55  
56          super(executor, callable);
57          deadlineNanos = nanoTime;
58          periodNanos = validatePeriod(period);
59      }
60  
61      ScheduledFutureTask(AbstractScheduledEventExecutor executor,
62              Callable<V> callable, long nanoTime) {
63  
64          super(executor, callable);
65          deadlineNanos = nanoTime;
66          periodNanos = 0;
67      }
68  
69      private static long validatePeriod(long period) {
70          if (period == 0) {
71              throw new IllegalArgumentException("period: 0 (expected: != 0)");
72          }
73          return period;
74      }
75  
76      ScheduledFutureTask<V> setId(long id) {
77          if (this.id == 0L) {
78              this.id = id;
79          }
80          return this;
81      }
82  
83      long getId() {
84          return id;
85      }
86  
87      @Override
88      protected EventExecutor executor() {
89          return super.executor();
90      }
91  
92      public long deadlineNanos() {
93          return deadlineNanos;
94      }
95  
96      void setConsumed() {
97          // Optimization to avoid checking system clock again
98          // after deadline has passed and task has been dequeued
99          if (periodNanos == 0) {
100             assert scheduledExecutor().getCurrentTimeNanos() >= deadlineNanos;
101             deadlineNanos = 0L;
102         }
103     }
104 
105     public long delayNanos() {
106         if (deadlineNanos == 0L) {
107             return 0L;
108         }
109         return delayNanos(scheduledExecutor().getCurrentTimeNanos());
110     }
111 
112     static long deadlineToDelayNanos(long currentTimeNanos, long deadlineNanos) {
113         return deadlineNanos == 0L ? 0L : Math.max(0L, deadlineNanos - currentTimeNanos);
114     }
115 
116     public long delayNanos(long currentTimeNanos) {
117         return deadlineToDelayNanos(currentTimeNanos, deadlineNanos);
118     }
119 
120     @Override
121     public long getDelay(TimeUnit unit) {
122         return unit.convert(delayNanos(), TimeUnit.NANOSECONDS);
123     }
124 
125     @Override
126     public int compareTo(Delayed o) {
127         if (this == o) {
128             return 0;
129         }
130 
131         ScheduledFutureTask<?> that = (ScheduledFutureTask<?>) o;
132         long d = deadlineNanos() - that.deadlineNanos();
133         if (d < 0) {
134             return -1;
135         } else if (d > 0) {
136             return 1;
137         } else if (id < that.id) {
138             return -1;
139         } else {
140             assert id != that.id;
141             return 1;
142         }
143     }
144 
145     @Override
146     public void run() {
147         assert executor().inEventLoop();
148         try {
149             if (delayNanos() > 0L) {
150                 // Not yet expired, need to add or remove from queue
151                 if (isCancelled()) {
152                     scheduledExecutor().scheduledTaskQueue().removeTyped(this);
153                 } else {
154                     scheduledExecutor().scheduleFromEventLoop(this);
155                 }
156                 return;
157             }
158             if (periodNanos == 0) {
159                 if (setUncancellableInternal()) {
160                     V result = runTask();
161                     setSuccessInternal(result);
162                 }
163             } else {
164                 // check if is done as it may was cancelled
165                 if (!isCancelled()) {
166                     runTask();
167                     if (!executor().isShutdown()) {
168                         if (periodNanos > 0) {
169                             deadlineNanos += periodNanos;
170                         } else {
171                             deadlineNanos = scheduledExecutor().getCurrentTimeNanos() - periodNanos;
172                         }
173                         if (!isCancelled()) {
174                             scheduledExecutor().scheduleFromEventLoop(this);
175                         }
176                     }
177                 }
178             }
179         } catch (Throwable cause) {
180             setFailureInternal(cause);
181         }
182     }
183 
184     private AbstractScheduledEventExecutor scheduledExecutor() {
185         return (AbstractScheduledEventExecutor) executor();
186     }
187 
188     /**
189      * {@inheritDoc}
190      *
191      * @param mayInterruptIfRunning this value has no effect in this implementation.
192      */
193     @Override
194     public boolean cancel(boolean mayInterruptIfRunning) {
195         boolean canceled = super.cancel(mayInterruptIfRunning);
196         if (canceled) {
197             scheduledExecutor().removeScheduled(this);
198         }
199         return canceled;
200     }
201 
202     boolean cancelWithoutRemove(boolean mayInterruptIfRunning) {
203         return super.cancel(mayInterruptIfRunning);
204     }
205 
206     @Override
207     protected StringBuilder toStringBuilder() {
208         StringBuilder buf = super.toStringBuilder();
209         buf.setCharAt(buf.length() - 1, ',');
210 
211         return buf.append(" deadline: ")
212                   .append(deadlineNanos)
213                   .append(", period: ")
214                   .append(periodNanos)
215                   .append(')');
216     }
217 
218     @Override
219     public int priorityQueueIndex(DefaultPriorityQueue<?> queue) {
220         return queueIndex;
221     }
222 
223     @Override
224     public void priorityQueueIndex(DefaultPriorityQueue<?> queue, int i) {
225         queueIndex = i;
226     }
227 }