Skip to content

Commit ab2610b

Browse files
authored
Merge pull request #85 from rhauch/issue-84
Issue 84 - Simplify engine and executor
2 parents 1f09032 + fdb99b7 commit ab2610b

File tree

11 files changed

+1209
-380
lines changed

11 files changed

+1209
-380
lines changed

build.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
strongback.version=1.1.7
1+
strongback.version=1.2.0-Beta1
22
#
33
# The build will download a specific version of the WPILib given by the following URL
44
# and install it into the 'libs/wpilib' folder. To use a different version of WPILib,

build.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@
8888

8989
<doctitle><![CDATA[<h1>Strongback Java Library</h1>]]></doctitle>
9090
<bottom><![CDATA[<i>Copyright &#169; ${current.year} Strongback and individual contributors. All Rights Reserved.</i>]]></bottom>
91-
<tag name="todo" scope="all" description="To do:"/>
9291
<group title="Strongback" packages="org.strongback"/>
9392
<group title="Components" packages="org.strongback.components*:org.strongback.hardware*"/>
9493
<group title="Drives" packages="org.strongback.drive"/>

strongback-tests/src/org/strongback/ExecutableTimerTest.java

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,15 @@
2020

2121
import java.util.concurrent.TimeUnit;
2222

23-
import junit.framework.AssertionFailedError;
24-
2523
import org.junit.AfterClass;
2624
import org.junit.BeforeClass;
2725
import org.junit.Test;
2826
import org.strongback.Logger.Level;
29-
import org.strongback.Strongback.Configurator.TimerMode;
27+
28+
import junit.framework.AssertionFailedError;
3029

3130
/**
3231
* This test attempts to measure the timing of the {@link Strongback#executor() Strongback Executor} instance at various periods
33-
* using the system clock and various {@link TimerMode}s. On OS X 10.10.3, the {@link TimerMode#BUSY} is clearly the most
34-
* accurate down to 1 millisecond, whereas {@link TimerMode#PARK} and {@link TimerMode#SLEEP} are relatively inaccurate and
35-
* imprecise for intervals even as high as 20ms.
3632
* <p>
3733
* The code committed into Git are small execution rates and sample sizes to minimize the time required to run the tests. As
3834
* such, the results are probably not terribly meaningful. To obtain more meaningful results on your own platform, simply adjust
@@ -44,28 +40,22 @@ public class ExecutableTimerTest {
4440

4541
@BeforeClass
4642
public static void beforeAll() {
47-
Strongback.configure().useSystemLogger(Level.ERROR).recordNoData().recordNoEvents().initialize();
48-
Strongback.start();
43+
Strongback.configure().setLogLevel(Level.ERROR).recordNoData().recordNoEvents().initialize();
4944
}
5045

5146
@AfterClass
5247
public static void afterAll() {
53-
Strongback.shutdown();
54-
}
55-
56-
@Test
57-
public void shouldMeasureAndPrintTimingHistogramUsingBusyMode() throws InterruptedException {
58-
runTimer(TimerMode.BUSY, 4, 2000);
48+
Strongback.stop();
5949
}
6050

6151
@Test
62-
public void shouldMeasureAndPrintTimingHistogramUsingParkMode() throws InterruptedException {
63-
runTimer(TimerMode.PARK, 5, 2000);
52+
public void shouldMeasureAndPrintTimingHistogramFor4MillisecondPeriod() throws InterruptedException {
53+
runTimer(4, 2000);
6454
}
6555

6656
@Test
67-
public void shouldMeasureAndPrintTimingHistogramUsingSleepMode() throws InterruptedException {
68-
runTimer(TimerMode.SLEEP, 6, 2000);
57+
public void shouldMeasureAndPrintTimingHistogramFor10MillisecondPeriod() throws InterruptedException {
58+
runTimer(10, 2000);
6959
}
7060

7161
/**
@@ -75,14 +65,14 @@ public void shouldMeasureAndPrintTimingHistogramUsingSleepMode() throws Interrup
7565
* @param millisecondExecutionPeriod the execution period in milliseconds
7666
* @param sampleTimeInMilliseconds the sample time in milliseconds
7767
*/
78-
protected void runTimer(TimerMode mode, int millisecondExecutionPeriod, int sampleTimeInMilliseconds) {
68+
protected void runTimer(int millisecondExecutionPeriod, int sampleTimeInMilliseconds) {
7969
try {
70+
Strongback.stop();
8071
Strongback.configure()
81-
.useExecutionTimerMode(mode)
82-
.useExecutionPeriod(millisecondExecutionPeriod, TimeUnit.MILLISECONDS)
83-
.initialize();
72+
.useExecutionPeriod(millisecondExecutionPeriod, TimeUnit.MILLISECONDS);
73+
Strongback.start();
8474
assertThat(ExecutableTimer.measureTimingAndPrint(Strongback.executor(),
85-
mode.toString() + " for " + millisecondExecutionPeriod + " ms",
75+
" for " + millisecondExecutionPeriod + " ms",
8676
sampleTimeInMilliseconds / millisecondExecutionPeriod)
8777
.await(10, TimeUnit.SECONDS)
8878
.isComplete());
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Strongback
3+
* Copyright 2015, Strongback and individual contributors by the @authors tag.
4+
* See the COPYRIGHT.txt in the distribution for a full listing of individual
5+
* contributors.
6+
*
7+
* Licensed under the MIT License; you may not use this file except in
8+
* compliance with the License. You may obtain a copy of the License at
9+
* http://opensource.org/licenses/MIT
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 implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.strongback;
18+
19+
import static org.fest.assertions.Assertions.assertThat;
20+
21+
import org.junit.After;
22+
import org.junit.Before;
23+
import org.junit.Test;
24+
import org.strongback.Logger.Level;
25+
import org.strongback.components.Clock;
26+
27+
/**
28+
* Tests that check the functionality of the {@link Strongback.Engine}.
29+
*/
30+
public class StrongbackTest {
31+
32+
private static final SystemLogger LOGGER = new SystemLogger();
33+
34+
private Clock clock;
35+
private Strongback.Engine engine;
36+
37+
@Before
38+
public void beforeEach() {
39+
LOGGER.enable(Level.INFO);
40+
clock = Clock.system();
41+
engine = new Strongback.Engine(clock, LOGGER);
42+
}
43+
44+
@After
45+
public void afterEach() {
46+
try {
47+
if (engine != null && engine.isRunning()) {
48+
engine.stop();
49+
}
50+
} finally {
51+
engine = null;
52+
}
53+
}
54+
55+
@Test
56+
public void shouldNotBeRunningWhenCreated() {
57+
assertThat(engine.isRunning()).isFalse();
58+
}
59+
60+
@Test
61+
public void shouldStartWithDefaultConfiguration() {
62+
engine.logConfiguration();
63+
assertThat(engine.isRunning()).isFalse();
64+
assertThat(engine.start()).isTrue();
65+
assertThat(engine.isRunning()).isTrue();
66+
}
67+
68+
@Test
69+
public void shouldAllowChangingExecutionPeriodWhenNotRunning() {
70+
assertThat(engine.isRunning()).isFalse();
71+
assertThat(engine.getExecutionPeriod()).isEqualTo(20);
72+
engine.setExecutionPeriod(5);
73+
assertThat(engine.getExecutionPeriod()).isEqualTo(5);
74+
assertThat(engine.start()).isTrue();
75+
engine.logConfiguration();
76+
}
77+
78+
@Test
79+
public void shouldNotAllowChangingExecutionPeriodWhenRunning() {
80+
assertThat(engine.isRunning()).isFalse();
81+
assertThat(engine.start()).isTrue();
82+
assertThat(engine.getExecutionPeriod()).isEqualTo(20);
83+
LOGGER.enable(Level.OFF);
84+
assertThat(engine.setExecutionPeriod(5)).isFalse();
85+
LOGGER.enable(Level.INFO);
86+
assertThat(engine.getExecutionPeriod()).isEqualTo(20);
87+
}
88+
89+
}

strongback/src/org/strongback/Executables.java

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,41 +16,89 @@
1616

1717
package org.strongback;
1818

19-
import java.util.Iterator;
19+
import java.util.List;
2020
import java.util.concurrent.CopyOnWriteArrayList;
2121

2222
import org.strongback.annotation.ThreadSafe;
2323

2424
/**
2525
* A simple threadsafe list of {@link Executable} instances.
26+
*
2627
* @author Randall Hauch
2728
*/
2829
@ThreadSafe
29-
final class Executables implements Executor, Iterable<Executable> {
30+
final class Executables implements Executor {
3031

31-
private final CopyOnWriteArrayList<Executable> executables = new CopyOnWriteArrayList<>();
32+
private final CopyOnWriteArrayList<Executable> highPriority = new CopyOnWriteArrayList<>();
33+
private final CopyOnWriteArrayList<Executable> mediumPriority = new CopyOnWriteArrayList<>();
34+
private final CopyOnWriteArrayList<Executable> lowPriority = new CopyOnWriteArrayList<>();
3235

3336
Executables() {
3437
}
3538

3639
@Override
37-
public boolean register(Executable r) {
38-
return executables.addIfAbsent(r);
40+
public boolean register(Executable r, Priority priority) {
41+
if (priority != null) {
42+
unregister(r);
43+
switch (priority) {
44+
case HIGH:
45+
mediumPriority.remove(r);
46+
lowPriority.remove(r);
47+
return highPriority.addIfAbsent(r);
48+
case MEDIUM:
49+
highPriority.remove(r);
50+
lowPriority.remove(r);
51+
return mediumPriority.addIfAbsent(r);
52+
case LOW:
53+
highPriority.remove(r);
54+
mediumPriority.remove(r);
55+
return lowPriority.addIfAbsent(r);
56+
}
57+
}
58+
return false;
3959
}
4060

4161
@Override
4262
public boolean unregister(Executable r) {
43-
return executables.remove(r);
63+
if (r != null) {
64+
// Remove from all
65+
boolean removed = highPriority.remove(r);
66+
removed = mediumPriority.remove(r) || removed;
67+
removed = lowPriority.remove(r) || removed;
68+
return removed;
69+
}
70+
return false;
4471
}
4572

4673
@Override
4774
public void unregisterAll() {
48-
executables.clear();
75+
highPriority.clear();
76+
mediumPriority.clear();
77+
lowPriority.clear();
4978
}
5079

51-
@Override
52-
public Iterator<Executable> iterator() {
53-
return executables.iterator();
80+
public List<Executable> lowPriorityExecutables() {
81+
return lowPriority;
82+
}
83+
84+
public List<Executable> mediumPriorityExecutables() {
85+
return mediumPriority;
86+
}
87+
88+
public List<Executable> highPriorityExecutables() {
89+
return highPriority;
90+
}
91+
92+
protected Executable[] lowPriorityExecutablesAsArrays() {
93+
return lowPriority.toArray(new Executable[0]); // will be reallocated with correct size
94+
}
95+
96+
protected Executable[] mediumPriorityExecutablesAsArrays() {
97+
return mediumPriority.toArray(new Executable[0]); // will be reallocated with correct size
98+
}
99+
100+
protected Executable[] highPriorityExecutablesAsArrays() {
101+
return highPriority.toArray(new Executable[0]); // will be reallocated with correct size
54102
}
55103

56104
}

strongback/src/org/strongback/Executor.java

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,45 @@
2424
@ThreadSafe
2525
public interface Executor {
2626

27+
public static enum Priority {
28+
HIGH, MEDIUM, LOW;
29+
}
30+
31+
/**
32+
* Register a high priority {@link Executable} task so that it is called repeatedly on Strongback's executor thread.
33+
*
34+
* @param task the executable task
35+
* @return {@code true} if the executable task was registered for the first time as a high priority, or {@code false} if
36+
* {@code task} was null or was already registered with this executor at high priority
37+
* @deprecated Use {@link #register(Executable, Priority)} instead
38+
*/
39+
@Deprecated
40+
default boolean register(Executable task) {
41+
return register(task, Priority.HIGH);
42+
}
43+
2744
/**
28-
* Register an {@link Executable} task to be called repeatedly.
45+
* Register an {@link Executable} task with the given priority so that it is called repeatedly on Strongback's executor
46+
* thread. If the given task is already registered with a different priority, this method reassigns it to the desired
47+
* priority; if the given task is already registered with the desired priority, this method does nothing.
48+
* <p>
49+
* This executor runs high priority tasks every cycle, medium priority tasks slightly every other cycles, and low priority
50+
* tasks every 4 cycles. All {@link Executable} tasks are called on the first cycle.
2951
*
30-
* @param r the executable task
31-
* @return true if the executable task was registered, or false if it was null or was already registered with this executor
52+
* @param task the executable task
53+
* @param priority the priority of the executable; may not be null
54+
* @return {@code true} if the executable task was registered for the first time at the given priority, or {@code false} if
55+
* {@code task} was null or was already registered with this executor at the given priority
3256
*/
33-
public boolean register(Executable r);
57+
public boolean register(Executable task, Priority priority);
3458

3559
/**
3660
* Unregister an {@link Executable} task to no longer be called.
3761
*
38-
* @param r the executable task
62+
* @param task the executable task
3963
* @return true if the executable task was unregistered, or false if it was null or not registered with this executor
4064
*/
41-
public boolean unregister(Executable r);
65+
public boolean unregister(Executable task);
4266

4367
/**
4468
* Unregister all {@link Executable} tasks.

0 commit comments

Comments
 (0)