blob: 0e03144c2019b02176c4afdb58ecf06d64266dd1 [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001/**
2* 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**/
17
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
30 * but where you only want the task to complete once in response to some sequence
31 * 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.
34 *
35 * 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);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080041
42 protected static class SingletonTaskContext {
43 protected boolean taskShouldRun = false;
44 protected boolean taskRunning = false;
45
46 protected SingletonTaskWorker waitingTask = null;
47 }
48
49 protected static class SingletonTaskWorker implements Runnable {
50 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
60 @LogMessageDoc(level="ERROR",
61 message="Exception while executing task",
62 recommendation=LogMessageDoc.GENERIC_ACTION)
63 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 {
86 parent.ses.schedule(this,
87 nextschedule-now,
88 TimeUnit.NANOSECONDS);
89 }
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.
104 * @param context
105 * @param Task
106 */
107 public SingletonTask(ScheduledExecutorService ses,
108 Runnable task) {
109 super();
110 this.task = task;
111 this.ses = ses;
112 }
113
114 /**
115 * Schedule the task to run if there's not already a task scheduled
116 * If there is such a task waiting that has not already started, it
117 * cancel that task and reschedule it to run at the given time. If the
118 * task is already started, it will cause the task to be rescheduled once
119 * it completes to run after delay from the time of reschedule.
120 *
121 * @param delay the delay in scheduling
122 * @param unit the timeunit of the delay
123 */
124 public void reschedule(long delay, TimeUnit unit) {
125 boolean needQueue = true;
126 SingletonTaskWorker stw = null;
127
128 synchronized (context) {
129 if (context.taskRunning || context.taskShouldRun) {
130 if (context.taskRunning) {
131 // schedule to restart at the right time
132 if (delay > 0) {
133 long now = System.nanoTime();
134 long then =
135 now + TimeUnit.NANOSECONDS.convert(delay, unit);
136 context.waitingTask.nextschedule = then;
137 } else {
138 context.waitingTask.nextschedule = 0;
139 }
140 needQueue = false;
141 } else {
142 // cancel and requeue
143 context.waitingTask.canceled = true;
144 context.waitingTask = null;
145 }
146 }
147
148 context.taskShouldRun = true;
149
150 if (needQueue) {
151 stw = context.waitingTask = new SingletonTaskWorker(this);
152 }
153 }
154
155 if (needQueue) {
156 if (delay <= 0)
157 ses.execute(stw);
158 else
159 ses.schedule(stw, delay, unit);
160 }
161 }
162}