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