5d363f128544056a3639f661634bab429896c3a9
[controller.git] / opendaylight / adsal / sal / api / src / main / java / org / opendaylight / controller / sal / utils / SingletonTask.java
1 /*
2  * Copyright (c) 2011,2013 Big Switch Networks, Inc.
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the
5  * "License"); you may not use this file except in compliance with the
6  * License. You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/legal/epl-v10.html
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,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13  * implied. See the License for the specific language governing
14  * permissions and limitations under the License.
15  *
16  * This file incorporates work covered by the following copyright and
17  * permission notice:
18  *
19  *    Originally created by David Erickson, Stanford University
20  *
21  *    Licensed under the Apache License, Version 2.0 (the "License");
22  *    you may not use this file except in compliance with the
23  *    License. You may obtain a copy of the License at
24  *
25  *         http://www.apache.org/licenses/LICENSE-2.0
26  *
27  *    Unless required by applicable law or agreed to in writing,
28  *    software distributed under the License is distributed on an "AS
29  *    IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
30  *    express or implied. See the License for the specific language
31  *    governing permissions and limitations under the License.
32  */
33
34 package org.opendaylight.controller.sal.utils;
35
36 import java.util.concurrent.ScheduledExecutorService;
37 import java.util.concurrent.TimeUnit;
38
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 /**
43  * This allows you to represent a task that should be queued for future
44  * execution but where you only want the task to complete once in response to
45  * some sequence of events. For example, if you get a change notification and
46  * want to reload state, you only want to reload the state once, at the end, and
47  * don't want to queue an update for every notification that might come in.
48  *
49  * The semantics are as follows: * If the task hasn't begun yet, do not queue a
50  * new task * If the task has begun, set a bit to restart it after the current
51  * task finishes
52  */
53 public class SingletonTask {
54     protected static Logger logger = LoggerFactory
55             .getLogger(SingletonTask.class);
56
57     protected static class SingletonTaskContext {
58         protected boolean taskShouldRun = false;
59         protected boolean taskRunning = false;
60
61         protected SingletonTaskWorker waitingTask = null;
62     }
63
64     protected static class SingletonTaskWorker implements Runnable {
65         SingletonTask parent;
66         boolean canceled = false;
67         long nextschedule = 0;
68
69         public SingletonTaskWorker(SingletonTask parent) {
70             super();
71             this.parent = parent;
72         }
73
74         @Override
75         public void run() {
76             synchronized (parent.context) {
77                 if (canceled || !parent.context.taskShouldRun)
78                     return;
79
80                 parent.context.taskRunning = true;
81                 parent.context.taskShouldRun = false;
82             }
83
84             try {
85                 parent.task.run();
86             } catch (Exception e) {
87                 logger.error("Exception while executing task", e);
88             }
89
90             synchronized (parent.context) {
91                 parent.context.taskRunning = false;
92
93                 if (parent.context.taskShouldRun) {
94                     long now = System.nanoTime();
95                     if ((nextschedule <= 0 || (nextschedule - now) <= 0)) {
96                         parent.ses.execute(this);
97                     } else {
98                         parent.ses.schedule(this, nextschedule - now,
99                                 TimeUnit.NANOSECONDS);
100                     }
101                 }
102             }
103         }
104     }
105
106     protected SingletonTaskContext context = new SingletonTaskContext();
107     protected Runnable task;
108     protected ScheduledExecutorService ses;
109
110     /**
111      * Construct a new SingletonTask for the given runnable. The context is used
112      * to manage the state of the task execution and can be shared by more than
113      * one instance of the runnable.
114      *
115      * @param context
116      * @param Task
117      */
118     public SingletonTask(ScheduledExecutorService ses, Runnable task) {
119         super();
120         this.task = task;
121         this.ses = ses;
122     }
123
124     /**
125      * Schedule the task to run if there's not already a task scheduled If there
126      * is such a task waiting that has not already started, it cancel that task
127      * and reschedule it to run at the given time. If the task is already
128      * started, it will cause the task to be rescheduled once it completes to
129      * run after delay from the time of reschedule.
130      *
131      * @param delay
132      *            the delay in scheduling
133      * @param unit
134      *            the timeunit of the delay
135      */
136     public void reschedule(long delay, TimeUnit unit) {
137         boolean needQueue = true;
138         SingletonTaskWorker stw = null;
139
140         synchronized (context) {
141             if (context.taskRunning || context.taskShouldRun) {
142                 if (context.taskRunning) {
143                     // schedule to restart at the right time
144                     if (delay > 0) {
145                         long now = System.nanoTime();
146                         long then = now
147                                 + TimeUnit.NANOSECONDS.convert(delay, unit);
148                         context.waitingTask.nextschedule = then;
149                     } else {
150                         context.waitingTask.nextschedule = 0;
151                     }
152                     needQueue = false;
153                 } else {
154                     // cancel and requeue
155                     context.waitingTask.canceled = true;
156                     context.waitingTask = null;
157                 }
158             }
159
160             context.taskShouldRun = true;
161
162             if (needQueue) {
163                 stw = context.waitingTask = new SingletonTaskWorker(this);
164             }
165         }
166
167         if (needQueue) {
168             if (delay <= 0) {
169                 ses.execute(stw);
170             }
171             else {
172                 ses.schedule(stw, delay, unit);
173             }
174         }
175     }
176 }