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 java.util.Queue;
20  import java.util.concurrent.Callable;
21  import java.util.concurrent.Delayed;
22  import java.util.concurrent.TimeUnit;
23  import java.util.concurrent.atomic.AtomicLong;
24  
25  @SuppressWarnings("ComparableImplementedButEqualsNotOverridden")
26  final class ScheduledFutureTask<V> extends PromiseTask<V> implements ScheduledFuture<V> {
27      private static final AtomicLong nextTaskId = new AtomicLong();
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          return nanoTime() + delay;
36      }
37  
38      private final long id = nextTaskId.getAndIncrement();
39      private long deadlineNanos;
40      /* 0 - no repeat, >0 - repeat at fixed rate, <0 - repeat with fixed delay */
41      private final long periodNanos;
42  
43      ScheduledFutureTask(
44              AbstractScheduledEventExecutor executor,
45              Runnable runnable, V result, long nanoTime) {
46  
47          this(executor, toCallable(runnable, result), nanoTime);
48      }
49  
50      ScheduledFutureTask(
51              AbstractScheduledEventExecutor executor,
52              Callable<V> callable, long nanoTime, long period) {
53  
54          super(executor, callable);
55          if (period == 0) {
56              throw new IllegalArgumentException("period: 0 (expected: != 0)");
57          }
58          deadlineNanos = nanoTime;
59          periodNanos = period;
60      }
61  
62      ScheduledFutureTask(
63              AbstractScheduledEventExecutor executor,
64              Callable<V> callable, long nanoTime) {
65  
66          super(executor, callable);
67          deadlineNanos = nanoTime;
68          periodNanos = 0;
69      }
70  
71      @Override
72      protected EventExecutor executor() {
73          return super.executor();
74      }
75  
76      public long deadlineNanos() {
77          return deadlineNanos;
78      }
79  
80      public long delayNanos() {
81          return Math.max(0, deadlineNanos() - nanoTime());
82      }
83  
84      public long delayNanos(long currentTimeNanos) {
85          return Math.max(0, deadlineNanos() - (currentTimeNanos - START_TIME));
86      }
87  
88      @Override
89      public long getDelay(TimeUnit unit) {
90          return unit.convert(delayNanos(), TimeUnit.NANOSECONDS);
91      }
92  
93      @Override
94      public int compareTo(Delayed o) {
95          if (this == o) {
96              return 0;
97          }
98  
99          ScheduledFutureTask<?> that = (ScheduledFutureTask<?>) o;
100         long d = deadlineNanos() - that.deadlineNanos();
101         if (d < 0) {
102             return -1;
103         } else if (d > 0) {
104             return 1;
105         } else if (id < that.id) {
106             return -1;
107         } else if (id == that.id) {
108             throw new Error();
109         } else {
110             return 1;
111         }
112     }
113 
114     @Override
115     public void run() {
116         assert executor().inEventLoop();
117         try {
118             if (periodNanos == 0) {
119                 if (setUncancellableInternal()) {
120                     V result = task.call();
121                     setSuccessInternal(result);
122                 }
123             } else {
124                 // check if is done as it may was cancelled
125                 if (!isCancelled()) {
126                     task.call();
127                     if (!executor().isShutdown()) {
128                         long p = periodNanos;
129                         if (p > 0) {
130                             deadlineNanos += p;
131                         } else {
132                             deadlineNanos = nanoTime() - p;
133                         }
134                         if (!isCancelled()) {
135                             // scheduledTaskQueue can never be null as we lazy init it before submit the task!
136                             Queue<ScheduledFutureTask<?>> scheduledTaskQueue =
137                                     ((AbstractScheduledEventExecutor) executor()).scheduledTaskQueue;
138                             assert scheduledTaskQueue != null;
139                             scheduledTaskQueue.add(this);
140                         }
141                     }
142                 }
143             }
144         } catch (Throwable cause) {
145             setFailureInternal(cause);
146         }
147     }
148 
149     @Override
150     public boolean cancel(boolean mayInterruptIfRunning) {
151         boolean canceled = super.cancel(mayInterruptIfRunning);
152         if (canceled) {
153             ((AbstractScheduledEventExecutor) executor()).removeScheduled(this);
154         }
155         return canceled;
156     }
157 
158     boolean cancelWithoutRemove(boolean mayInterruptIfRunning) {
159         return super.cancel(mayInterruptIfRunning);
160     }
161 
162     @Override
163     protected StringBuilder toStringBuilder() {
164         StringBuilder buf = super.toStringBuilder();
165         buf.setCharAt(buf.length() - 1, ',');
166 
167         return buf.append(" id: ")
168                   .append(id)
169                   .append(", deadline: ")
170                   .append(deadlineNanos)
171                   .append(", period: ")
172                   .append(periodNanos)
173                   .append(')');
174     }
175 }