Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 5254f56

Browse filesBrowse files
committed
Better task manager implementation
The previous implementation used a set for serial running and a map of lists of tasks for managing the serial queues. In practice, there will be very few parallel tasks, using maps and creating/destroying lists is complex and unefficient. A better approach is to use only a list of tasks (only the ones we need to keep, having a non-null serial) and run through it sequentially for retrieving tasks. Moreover, it is more general, and paves the way for adding a task cancellation feature.
1 parent 2045e41 commit 5254f56
Copy full SHA for 5254f56

1 file changed

+126-88Lines changed: 126 additions & 88 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/api/BackgroundExecutor.java‎

Copy file name to clipboardExpand all lines: AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/api/BackgroundExecutor.java
+126-88Lines changed: 126 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,7 @@
1616
package org.androidannotations.api;
1717

1818
import java.util.ArrayList;
19-
import java.util.HashMap;
20-
import java.util.HashSet;
2119
import java.util.List;
22-
import java.util.Map;
23-
import java.util.Set;
2420
import java.util.concurrent.Executor;
2521
import java.util.concurrent.Executors;
2622
import java.util.concurrent.ScheduledExecutorService;
@@ -30,92 +26,86 @@ public class BackgroundExecutor {
3026

3127
private static Executor executor = Executors.newScheduledThreadPool(2 * Runtime.getRuntime().availableProcessors());
3228

33-
/*
34-
* serialRunning is used as a lock in synchronized blocks for both
35-
* serialRunning and serialQueues access
36-
*/
37-
38-
/* Set of queueIds having a currently running task */
39-
private static final Set<String> serialRunning = new HashSet<String>();
40-
41-
/* Tasks queues for each serial */
42-
private static final Map<String, List<Task>> serialQueues = new HashMap<String, List<Task>>();
29+
private static final List<Task> tasks = new ArrayList<Task>();
4330

4431
/**
45-
* Execute a task after (at least) the given delay <strong>and</strong>
46-
* after all tasks added with the same non-null <code>serial</code> (if any)
47-
* have completed execution.
32+
* Execute a runnable after the given delay.
4833
*
4934
* @param runnable
5035
* the task to execute
5136
* @param delay
5237
* the time from now to delay execution, in milliseconds
53-
* @param serial
54-
* the serial queue to use (<code>null</code> or <code>""</code>
55-
* for no serial execution)
5638
* @throws IllegalArgumentException
5739
* if <code>delay</code> is strictly positive and the current
5840
* executor does not support scheduling (if
5941
* {@link #setExecutor(Executor)} has been called with such an
6042
* executor)
6143
*/
62-
public static void execute(Runnable runnable, int delay, String serial) {
63-
/* "" means null (a default annotation String value cannot be null) */
64-
if (serial == null || serial.isEmpty()) {
65-
if (delay > 0) {
66-
/* no serial, but a delay: schedule the task */
67-
if (!(executor instanceof ScheduledExecutorService)) {
68-
throw new IllegalArgumentException("The executor set does not support scheduling");
69-
}
70-
((ScheduledExecutorService) executor).schedule(runnable, delay, TimeUnit.MILLISECONDS);
71-
} else {
72-
/* no serial, no delay: execute now */
73-
executor.execute(runnable);
44+
private static void directExecute(Runnable runnable, int delay) {
45+
if (delay > 0) {
46+
/* no serial, but a delay: schedule the task */
47+
if (!(executor instanceof ScheduledExecutorService)) {
48+
throw new IllegalArgumentException("The executor set does not support scheduling");
7449
}
50+
((ScheduledExecutorService) executor).schedule(runnable, delay, TimeUnit.MILLISECONDS);
7551
} else {
76-
/* serial is defined, the delay is managed by Task */
77-
Task task = new Task(runnable, delay, serial);
78-
79-
synchronized (serialRunning) {
80-
if (serialRunning.contains(serial)) {
81-
/* a task for this serial is already running, queue this one */
82-
List<Task> queue = serialQueues.get(serial);
83-
if (queue == null) {
84-
/* the queue does not exist yet */
85-
queue = new ArrayList<Task>();
86-
serialQueues.put(serial, queue);
87-
}
88-
/* queue the task for later execution */
89-
queue.add(task);
90-
} else {
91-
/* mark this serial as having a running task */
92-
serialRunning.add(serial);
93-
/* execute the task (a wrapper for runnable) now */
94-
execute(task, delay); /* do not pass serial here */
95-
}
96-
}
52+
/* no serial, no delay: execute now */
53+
executor.execute(runnable);
9754
}
9855
}
9956

10057
/**
101-
* Execute a task.
58+
* Execute a task after (at least) its delay <strong>and</strong> after all
59+
* tasks added with the same non-null <code>serial</code> (if any) have
60+
* completed execution.
10261
*
103-
* Equivalent to {@link #execute(Runnable, int, String) execute(runnable, 0,
104-
* null)}.
62+
* @param task
63+
* the task to execute
64+
* @throws IllegalArgumentException
65+
* if <code>task.delay</code> is strictly positive and the
66+
* current executor does not support scheduling (if
67+
* {@link #setExecutor(Executor)} has been called with such an
68+
* executor)
69+
*/
70+
public static synchronized void execute(Task task) {
71+
if (task.serial == null || !hasSerialRunning(task.serial)) {
72+
task.executionAsked = true;
73+
directExecute(task, task.delay);
74+
}
75+
if (task.serial != null) {
76+
/* keep task */
77+
tasks.add(task);
78+
}
79+
}
80+
81+
/**
82+
* Execute a task.
10583
*
10684
* @param runnable
10785
* the task to execute
86+
* @param delay
87+
* the time from now to delay execution, in milliseconds
88+
* @param serial
89+
* the serial queue (<code>null</code> or <code>""</code> for no
90+
* serial execution)
91+
* @throws IllegalArgumentException
92+
* if <code>delay</code> is strictly positive and the current
93+
* executor does not support scheduling (if
94+
* {@link #setExecutor(Executor)} has been called with such an
95+
* executor)
10896
*/
109-
public static void execute(Runnable runnable) {
110-
execute(runnable, 0, null);
97+
public static void execute(final Runnable runnable, int delay, String serial) {
98+
execute(new Task(delay, serial) {
99+
@Override
100+
public void execute() {
101+
runnable.run();
102+
}
103+
});
111104
}
112105

113106
/**
114107
* Execute a task after the given delay.
115108
*
116-
* Equivalent to {@link #execute(Runnable, int, String) execute(runnable,
117-
* delay, null)}.
118-
*
119109
* @param runnable
120110
* the task to execute
121111
* @param delay
@@ -127,7 +117,17 @@ public static void execute(Runnable runnable) {
127117
* executor)
128118
*/
129119
public static void execute(Runnable runnable, int delay) {
130-
execute(runnable, delay, null);
120+
directExecute(runnable, delay);
121+
}
122+
123+
/**
124+
* Execute a task.
125+
*
126+
* @param runnable
127+
* the task to execute
128+
*/
129+
public static void execute(Runnable runnable) {
130+
directExecute(runnable, 0);
131131
}
132132

133133
/**
@@ -160,50 +160,88 @@ public static void setExecutor(Executor executor) {
160160
BackgroundExecutor.executor = executor;
161161
}
162162

163-
private static class Task implements Runnable {
163+
/**
164+
* Indicates whether a task with the specified <code>serial</code> has been
165+
* submitted to the executor.
166+
*
167+
* @param serial
168+
* the serial queue
169+
* @return <code>true</code> if such a task has been submitted,
170+
* <code>false</code> otherwise
171+
*/
172+
private static boolean hasSerialRunning(String serial) {
173+
for (Task task : tasks) {
174+
if (task.executionAsked && serial.equals(task.serial)) {
175+
return true;
176+
}
177+
}
178+
return false;
179+
}
180+
181+
/**
182+
* Retrieve and remove the first task having the specified
183+
* <code>serial</code> (if any).
184+
*
185+
* @param serial
186+
* the serial queue
187+
* @return task if found, <code>null</code> otherwise
188+
*/
189+
private static Task take(String serial) {
190+
int len = tasks.size();
191+
for (int i = 0; i < len; i++) {
192+
if (serial.equals(tasks.get(i).serial)) {
193+
return tasks.remove(i);
194+
}
195+
}
196+
return null;
197+
}
198+
199+
public static abstract class Task implements Runnable {
164200

165-
Runnable runnable;
166-
long targetTime; /* in milliseconds since epoch */
167-
String serial;
201+
private int delay;
202+
private long targetTime; /* in milliseconds since epoch */
203+
private String serial;
204+
private boolean executionAsked;
168205

169-
Task(Runnable runnable, int delay, String serial) {
170-
this.runnable = runnable;
206+
public Task(int delay, String serial) {
171207
if (delay > 0) {
208+
this.delay = delay;
172209
targetTime = System.currentTimeMillis() + delay;
173210
}
174-
this.serial = serial;
211+
if (!"".equals(serial)) {
212+
this.serial = serial;
213+
}
175214
}
176215

177216
@Override
178217
public void run() {
179218
try {
180-
runnable.run();
219+
execute();
181220
} finally {
182221
/* handle next tasks */
183222
postExecute();
184223
}
185224
}
186225

226+
public abstract void execute();
227+
187228
private void postExecute() {
188-
synchronized (serialRunning) {
189-
List<Task> queue = serialQueues.get(serial);
190-
if (queue == null) {
191-
/* no task is queue for this serial, mark it as not running */
192-
serialRunning.remove(serial);
193-
} else {
194-
/* queue is not empty, retrieve the oldest queued task */
195-
Task nextTask = queue.remove(0);
196-
197-
if (queue.isEmpty()) {
198-
/* no more tasks in the queue */
199-
serialQueues.remove(serial);
229+
if (serial == null) {
230+
/* nothing to do */
231+
return;
232+
}
233+
synchronized (BackgroundExecutor.class) {
234+
/* execution complete */
235+
tasks.remove(this);
236+
237+
Task next = take(serial);
238+
if (next != null) {
239+
if (next.delay != 0) {
240+
/* compute remaining delay */
241+
next.delay = Math.max(0, (int) (targetTime - System.currentTimeMillis()));
200242
}
201-
202-
/* compute the remaining delay */
203-
int delay = Math.max(0, (int) (nextTask.targetTime - System.currentTimeMillis()));
204-
205-
/* execute the next task */
206-
execute(nextTask, delay); /* do not pass serial here */
243+
/* a task having the same serial was queued, execute it */
244+
BackgroundExecutor.execute(next);
207245
}
208246
}
209247
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.