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      @Override
84      protected EventExecutor executor() {
85          return super.executor();
86      }
87  
88      public long deadlineNanos() {
89          return deadlineNanos;
90      }
91  
92      void setConsumed() {
93          // Optimization to avoid checking system clock again
94          // after deadline has passed and task has been dequeued
95          if (periodNanos == 0) {
96              assert scheduledExecutor().getCurrentTimeNanos() >= deadlineNanos;
97              deadlineNanos = 0L;
98          }
99      }
100 
101     public long delayNanos() {
102         if (deadlineNanos == 0L) {
103             return 0L;
104         }
105         return delayNanos(scheduledExecutor().getCurrentTimeNanos());
106     }
107 
108     static long deadlineToDelayNanos(long currentTimeNanos, long deadlineNanos) {
109         return deadlineNanos == 0L ? 0L : Math.max(0L, deadlineNanos - currentTimeNanos);
110     }
111 
112     public long delayNanos(long currentTimeNanos) {
113         return deadlineToDelayNanos(currentTimeNanos, deadlineNanos);
114     }
115 
116     @Override
117     public long getDelay(TimeUnit unit) {
118         return unit.convert(delayNanos(), TimeUnit.NANOSECONDS);
119     }
120 
121     @Override
122     public int compareTo(Delayed o) {
123         if (this == o) {
124             return 0;
125         }
126 
127         ScheduledFutureTask<?> that = (ScheduledFutureTask<?>) o;
128         long d = deadlineNanos() - that.deadlineNanos();
129         if (d < 0) {
130             return -1;
131         } else if (d > 0) {
132             return 1;
133         } else if (id < that.id) {
134             return -1;
135         } else {
136             assert id != that.id;
137             return 1;
138         }
139     }
140 
141     @Override
142     public void run() {
143         assert executor().inEventLoop();
144         try {
145             if (delayNanos() > 0L) {
146                 // Not yet expired, need to add or remove from queue
147                 if (isCancelled()) {
148                     scheduledExecutor().scheduledTaskQueue().removeTyped(this);
149                 } else {
150                     scheduledExecutor().scheduleFromEventLoop(this);
151                 }
152                 return;
153             }
154             if (periodNanos == 0) {
155                 if (setUncancellableInternal()) {
156                     V result = runTask();
157                     setSuccessInternal(result);
158                 }
159             } else {
160                 // check if is done as it may was cancelled
161                 if (!isCancelled()) {
162                     runTask();
163                     if (!executor().isShutdown()) {
164                         if (periodNanos > 0) {
165                             deadlineNanos += periodNanos;
166                         } else {
167                             deadlineNanos = scheduledExecutor().getCurrentTimeNanos() - periodNanos;
168                         }
169                         if (!isCancelled()) {
170                             scheduledExecutor().scheduledTaskQueue().add(this);
171                         }
172                     }
173                 }
174             }
175         } catch (Throwable cause) {
176             setFailureInternal(cause);
177         }
178     }
179 
180     private AbstractScheduledEventExecutor scheduledExecutor() {
181         return (AbstractScheduledEventExecutor) executor();
182     }
183 
184     /**
185      * {@inheritDoc}
186      *
187      * @param mayInterruptIfRunning this value has no effect in this implementation.
188      */
189     @Override
190     public boolean cancel(boolean mayInterruptIfRunning) {
191         boolean canceled = super.cancel(mayInterruptIfRunning);
192         if (canceled) {
193             scheduledExecutor().removeScheduled(this);
194         }
195         return canceled;
196     }
197 
198     boolean cancelWithoutRemove(boolean mayInterruptIfRunning) {
199         return super.cancel(mayInterruptIfRunning);
200     }
201 
202     @Override
203     protected StringBuilder toStringBuilder() {
204         StringBuilder buf = super.toStringBuilder();
205         buf.setCharAt(buf.length() - 1, ',');
206 
207         return buf.append(" deadline: ")
208                   .append(deadlineNanos)
209                   .append(", period: ")
210                   .append(periodNanos)
211                   .append(')');
212     }
213 
214     @Override
215     public int priorityQueueIndex(DefaultPriorityQueue<?> queue) {
216         return queueIndex;
217     }
218 
219     @Override
220     public void priorityQueueIndex(DefaultPriorityQueue<?> queue, int i) {
221         queueIndex = i;
222     }
223 }