Make sure invokeOperation is set once
[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 @Deprecated
54 public class SingletonTask {
55     protected static Logger logger = LoggerFactory
56             .getLogger(SingletonTask.class);
57
58     protected static class SingletonTaskContext {
59         protected boolean taskShouldRun = false;
60         protected boolean taskRunning = false;
61
62         protected SingletonTaskWorker waitingTask = null;
63     }
64
65     protected static class SingletonTaskWorker implements Runnable {
66         SingletonTask parent;
67         boolean canceled = false;
68         long nextschedule = 0;
69
70         public SingletonTaskWorker(SingletonTask parent) {
71             super();
72             this.parent = parent;
73         }
74
75         @Override
76         public void run() {
77             synchronized (parent.context) {
78                 if (canceled || !parent.context.taskShouldRun)
79                     return;
80
81                 parent.context.taskRunning = true;
82                 parent.context.taskShouldRun = false;
83             }
84
85             try {
86                 parent.task.run();
87             } catch (Exception e) {
88                 logger.error("Exception while executing task", e);
89             }
90
91             synchronized (parent.context) {
92                 parent.context.taskRunning = false;
93
94                 if (parent.context.taskShouldRun) {
95                     long now = System.nanoTime();
96                     if ((nextschedule <= 0 || (nextschedule - now) <= 0)) {
97                         parent.ses.execute(this);
98                     } else {
99                         parent.ses.schedule(this, nextschedule - now,
100                                 TimeUnit.NANOSECONDS);
101                     }
102                 }
103             }
104         }
105     }
106
107     protected SingletonTaskContext context = new SingletonTaskContext();
108     protected Runnable task;
109     protected ScheduledExecutorService ses;
110
111     /**
112      * Construct a new SingletonTask for the given runnable. The context is used
113      * to manage the state of the task execution and can be shared by more than
114      * one instance of the runnable.
115      *
116      * @param context
117      * @param Task
118      */
119     public SingletonTask(ScheduledExecutorService ses, Runnable task) {
120         super();
121         this.task = task;
122         this.ses = ses;
123     }
124
125     /**
126      * Schedule the task to run if there's not already a task scheduled If there
127      * is such a task waiting that has not already started, it cancel that task
128      * and reschedule it to run at the given time. If the task is already
129      * started, it will cause the task to be rescheduled once it completes to
130      * run after delay from the time of reschedule.
131      *
132      * @param delay
133      *            the delay in scheduling
134      * @param unit
135      *            the timeunit of the delay
136      */
137     public void reschedule(long delay, TimeUnit unit) {
138         boolean needQueue = true;
139         SingletonTaskWorker stw = null;
140
141         synchronized (context) {
142             if (context.taskRunning || context.taskShouldRun) {
143                 if (context.taskRunning) {
144                     // schedule to restart at the right time
145                     if (delay > 0) {
146                         long now = System.nanoTime();
147                         long then = now
148                                 + TimeUnit.NANOSECONDS.convert(delay, unit);
149                         context.waitingTask.nextschedule = then;
150                     } else {
151                         context.waitingTask.nextschedule = 0;
152                     }
153                     needQueue = false;
154                 } else {
155                     // cancel and requeue
156                     context.waitingTask.canceled = true;
157                     context.waitingTask = null;
158                 }
159             }
160
161             context.taskShouldRun = true;
162
163             if (needQueue) {
164                 stw = context.waitingTask = new SingletonTaskWorker(this);
165             }
166         }
167
168         if (needQueue) {
169             if (delay <= 0) {
170                 ses.execute(stw);
171             }
172             else {
173                 ses.schedule(stw, delay, unit);
174             }
175         }
176     }
177 }