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

203     @Override
204     public boolean cancel(boolean mayInterruptIfRunning) {
205         boolean canceled = super.cancel(mayInterruptIfRunning);
206         if (canceled) {
207             scheduledExecutor().removeScheduled(this);
208         }
209         return canceled;
210     }
211
212     boolean cancelWithoutRemove(boolean mayInterruptIfRunning) {
213         return super.cancel(mayInterruptIfRunning);
214     }
215
216     @Override
217     protected StringBuilder toStringBuilder() {
218         StringBuilder buf = super.toStringBuilder();
219         buf.setCharAt(buf.length() - 1, ',');
220
221         return buf.append(" deadline: ")
222                   .append(deadlineNanos)
223                   .append(", period: ")
224                   .append(periodNanos)
225                   .append(')');
226     }
227
228     @Override
229     public int priorityQueueIndex(DefaultPriorityQueue<?> queue) {
230         return queueIndex;
231     }
232
233     @Override
234     public void priorityQueueIndex(DefaultPriorityQueue<?> queue, int i) {
235         queueIndex = i;
236     }
237 }
238