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