blob: a7cdc0e0bcac98ae5027ad730d316325d1076f7e [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001/**
Ray Milkey269ffb92014-04-03 14:43:30 -07002 * Copyright 2011, Big Switch Networks, Inc.
3 * Originally created by David Erickson, Stanford University
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may
6 * not use this file except in compliance with the License. You may obtain
7 * a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations
15 * under the License.
16 **/
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080017
18package net.floodlightcontroller.core.util;
19
20import java.util.concurrent.ScheduledExecutorService;
21import java.util.concurrent.TimeUnit;
22
23import net.floodlightcontroller.core.annotations.LogMessageDoc;
24
25import org.slf4j.Logger;
26import org.slf4j.LoggerFactory;
27
28/**
29 * This allows you to represent a task that should be queued for future execution
Ray Milkey269ffb92014-04-03 14:43:30 -070030 * but where you only want the task to complete once in response to some sequence
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080031 * of events. For example, if you get a change notification and want to reload state,
32 * you only want to reload the state once, at the end, and don't want to queue
33 * an update for every notification that might come in.
Ray Milkey269ffb92014-04-03 14:43:30 -070034 * <p/>
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080035 * The semantics are as follows:
36 * * If the task hasn't begun yet, do not queue a new task
37 * * If the task has begun, set a bit to restart it after the current task finishes
38 */
39public class SingletonTask {
Yuta HIGUCHI6ac8d182013-10-22 15:24:56 -070040 protected final static Logger logger = LoggerFactory.getLogger(SingletonTask.class);
Ray Milkey269ffb92014-04-03 14:43:30 -070041
42 protected static class SingletonTaskContext {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080043 protected boolean taskShouldRun = false;
44 protected boolean taskRunning = false;
45
46 protected SingletonTaskWorker waitingTask = null;
47 }
48
Ray Milkey269ffb92014-04-03 14:43:30 -070049 protected static class SingletonTaskWorker implements Runnable {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080050 SingletonTask parent;
51 boolean canceled = false;
52 long nextschedule = 0;
53
54 public SingletonTaskWorker(SingletonTask parent) {
55 super();
56 this.parent = parent;
57 }
58
59 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -070060 @LogMessageDoc(level = "ERROR",
61 message = "Exception while executing task",
62 recommendation = LogMessageDoc.GENERIC_ACTION)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080063 public void run() {
64 synchronized (parent.context) {
65 if (canceled || !parent.context.taskShouldRun)
66 return;
67
68 parent.context.taskRunning = true;
69 parent.context.taskShouldRun = false;
70 }
71
72 try {
73 parent.task.run();
74 } catch (Exception e) {
75 logger.error("Exception while executing task", e);
76 }
77
78 synchronized (parent.context) {
79 parent.context.taskRunning = false;
80
81 if (parent.context.taskShouldRun) {
82 long now = System.nanoTime();
83 if ((nextschedule <= 0 || (nextschedule - now) <= 0)) {
84 parent.ses.execute(this);
85 } else {
Ray Milkey269ffb92014-04-03 14:43:30 -070086 parent.ses.schedule(this,
87 nextschedule - now,
88 TimeUnit.NANOSECONDS);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080089 }
90 }
91 }
92 }
93 }
94
95 protected SingletonTaskContext context = new SingletonTaskContext();
96 protected Runnable task;
97 protected ScheduledExecutorService ses;
98
99
100 /**
101 * Construct a new SingletonTask for the given runnable. The context
102 * is used to manage the state of the task execution and can be shared
103 * by more than one instance of the runnable.
Ray Milkey269ffb92014-04-03 14:43:30 -0700104 *
Sho SHIMIZUa1199fa2014-06-10 18:11:12 -0700105 * @param ses
106 * @param task
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800107 */
108 public SingletonTask(ScheduledExecutorService ses,
Ray Milkey269ffb92014-04-03 14:43:30 -0700109 Runnable task) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800110 super();
111 this.task = task;
112 this.ses = ses;
113 }
114
115 /**
116 * Schedule the task to run if there's not already a task scheduled
117 * If there is such a task waiting that has not already started, it
118 * cancel that task and reschedule it to run at the given time. If the
119 * task is already started, it will cause the task to be rescheduled once
120 * it completes to run after delay from the time of reschedule.
Ray Milkey269ffb92014-04-03 14:43:30 -0700121 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800122 * @param delay the delay in scheduling
Ray Milkey269ffb92014-04-03 14:43:30 -0700123 * @param unit the timeunit of the delay
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800124 */
125 public void reschedule(long delay, TimeUnit unit) {
126 boolean needQueue = true;
127 SingletonTaskWorker stw = null;
128
129 synchronized (context) {
130 if (context.taskRunning || context.taskShouldRun) {
131 if (context.taskRunning) {
132 // schedule to restart at the right time
133 if (delay > 0) {
134 long now = System.nanoTime();
Ray Milkey269ffb92014-04-03 14:43:30 -0700135 long then =
136 now + TimeUnit.NANOSECONDS.convert(delay, unit);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800137 context.waitingTask.nextschedule = then;
138 } else {
139 context.waitingTask.nextschedule = 0;
140 }
141 needQueue = false;
142 } else {
143 // cancel and requeue
144 context.waitingTask.canceled = true;
145 context.waitingTask = null;
146 }
147 }
148
149 context.taskShouldRun = true;
150
151 if (needQueue) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700152 stw = context.waitingTask = new SingletonTaskWorker(this);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800153 }
154 }
155
156 if (needQueue) {
157 if (delay <= 0)
158 ses.execute(stw);
159 else
160 ses.schedule(stw, delay, unit);
161 }
162 }
Sho SHIMIZUa1199fa2014-06-10 18:11:12 -0700163}