Fix copyright header
[groupbasedpolicy.git] / groupbasedpolicy / src / main / java / org / opendaylight / groupbasedpolicy / util / SingletonTask.java
1 /*
2  * Copyright (c) 2011 Big Switch Networks, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.groupbasedpolicy.util;
10
11 import java.util.concurrent.ScheduledExecutorService;
12 import java.util.concurrent.TimeUnit;
13
14 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory;
16
17 /**
18  * This allows you to represent a task that should be queued for future execution
19  * but where you only want the task to complete once in response to some sequence
20  * of events.  For example, if you get a change notification and want to reload state,
21  * you only want to reload the state once, at the end, and don't want to queue
22  * an update for every notification that might come in.
23  *
24  * The semantics are as follows:
25  * * If the task hasn't begun yet, do not queue a new task
26  * * If the task has begun, set a bit to restart it after the current task finishes
27  */
28 public class SingletonTask {
29     protected static final Logger LOG =
30             LoggerFactory.getLogger(SingletonTask.class);
31
32     protected static class SingletonTaskContext  {
33         protected boolean taskShouldRun = false;
34         protected boolean taskRunning = false;
35
36         protected SingletonTaskWorker waitingTask = null;
37
38     }
39     protected static class SingletonTaskWorker implements Runnable  {
40         SingletonTask parent;
41         boolean canceled = false;
42         long nextschedule = 0;
43
44         public SingletonTaskWorker(SingletonTask parent) {
45             super();
46             this.parent = parent;
47         }
48
49         @Override
50         public void run() {
51             synchronized (parent.context) {
52                 if (canceled || !parent.context.taskShouldRun)
53                     return;
54
55                 parent.context.taskRunning = true;
56                 parent.context.taskShouldRun = false;
57             }
58
59             try {
60                 parent.task.run();
61             } catch (Exception e) {
62                 LOG.error("Exception while executing task", e);
63             }
64
65             synchronized (parent.context) {
66                 parent.context.taskRunning = false;
67
68                 if (parent.context.taskShouldRun) {
69                     long now = System.nanoTime();
70                     if ((nextschedule <= 0 || (nextschedule - now) <= 0)) {
71                         parent.ses.execute(this);
72                     } else {
73                         parent.ses.schedule(this,
74                                             nextschedule-now,
75                                             TimeUnit.NANOSECONDS);
76                     }
77                 }
78             }
79         }
80     }
81
82     protected SingletonTaskContext context = new SingletonTaskContext();
83     protected Runnable task;
84     protected ScheduledExecutorService ses;
85
86
87     /**
88      * Construct a new SingletonTask for the given runnable.  The context
89      * is used to manage the state of the task execution and can be shared
90      * by more than one instance of the runnable.
91      * @param ses
92      * @param task
93      */
94     public SingletonTask(ScheduledExecutorService ses,
95             Runnable task) {
96         super();
97         this.task = task;
98         this.ses = ses;
99     }
100
101     /**
102      * Schedule the task to run if there's not already a task scheduled
103      * If there is such a task waiting that has not already started, it
104      * cancel that task and reschedule it to run at the given time.  If the
105      * task is already started, it will cause the task to be rescheduled once
106      * it completes to run after delay from the time of reschedule.
107      *
108      * @param delay the delay in scheduling
109      * @param unit the timeunit of the delay
110      */
111     public void reschedule(long delay, TimeUnit unit) {
112         boolean needQueue = true;
113         SingletonTaskWorker stw = null;
114
115         synchronized (context) {
116             if (context.taskRunning || context.taskShouldRun) {
117                 if (context.taskRunning) {
118                     // schedule to restart at the right time
119                     if (delay > 0) {
120                         long now = System.nanoTime();
121                         long then =
122                             now + TimeUnit.NANOSECONDS.convert(delay, unit);
123                         context.waitingTask.nextschedule = then;
124                     } else {
125                         context.waitingTask.nextschedule = 0;
126                     }
127                     needQueue = false;
128                 } else {
129                     // cancel and requeue
130                     context.waitingTask.canceled = true;
131                     context.waitingTask = null;
132                 }
133             }
134
135             context.taskShouldRun = true;
136
137             if (needQueue) {
138                 stw = context.waitingTask = new SingletonTaskWorker(this);
139             }
140         }
141
142         if (needQueue) {
143             if (delay <= 0)
144                 ses.execute(stw);
145             else
146                 ses.schedule(stw, delay, unit);
147         }
148     }
149 }