Make Sendable setters synchronous (#1799)

Instead of being called asynchronously by NetworkTables, they are now called by updateValues() synchronously with the main loop, just like the getters.
This commit is contained in:
Oblarg
2019-08-03 18:08:06 -04:00
committed by Peter Johnson
parent c67a488a09
commit fbe67c90c8
8 changed files with 194 additions and 23 deletions

View File

@@ -0,0 +1,50 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
package edu.wpi.first.wpilibj.smartdashboard;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.Executor;
/**
* An executor for running listener tasks posted by {@link edu.wpi.first.wpilibj.Sendable} listeners
* synchronously from the main application thread.
*/
class ListenerExecutor implements Executor {
private final Collection<Runnable> m_tasks = new ArrayList<>();
private final Object m_lock = new Object();
/**
* Posts a task to the executor to be run synchronously from the main thread.
*
* @param task The task to run synchronously from the main thread.
*/
@Override
public void execute(Runnable task) {
synchronized (m_lock) {
m_tasks.add(task);
}
}
/**
* Runs all posted tasks. Called periodically from main thread.
*/
public void runListenerTasks() {
// Locally copy tasks from internal list; minimizes blocking time
Collection<Runnable> tasks = new ArrayList<>();
synchronized (m_lock) {
tasks.addAll(m_tasks);
m_tasks.clear();
}
// Run all tasks
for (Runnable task : tasks) {
task.run();
}
}
}

View File

@@ -222,7 +222,7 @@ public class SendableBuilderImpl implements SendableBuilder {
if (setter != null) {
property.m_createListener = entry -> entry.addListener(event -> {
if (event.value.isBoolean()) {
setter.accept(event.value.getBoolean());
SmartDashboard.postListenerTask(() -> setter.accept(event.value.getBoolean()));
}
}, EntryListenerFlags.kImmediate | EntryListenerFlags.kNew | EntryListenerFlags.kUpdate);
}
@@ -245,7 +245,7 @@ public class SendableBuilderImpl implements SendableBuilder {
if (setter != null) {
property.m_createListener = entry -> entry.addListener(event -> {
if (event.value.isDouble()) {
setter.accept(event.value.getDouble());
SmartDashboard.postListenerTask(() -> setter.accept(event.value.getDouble()));
}
}, EntryListenerFlags.kImmediate | EntryListenerFlags.kNew | EntryListenerFlags.kUpdate);
}
@@ -268,7 +268,7 @@ public class SendableBuilderImpl implements SendableBuilder {
if (setter != null) {
property.m_createListener = entry -> entry.addListener(event -> {
if (event.value.isString()) {
setter.accept(event.value.getString());
SmartDashboard.postListenerTask(() -> setter.accept(event.value.getString()));
}
}, EntryListenerFlags.kImmediate | EntryListenerFlags.kNew | EntryListenerFlags.kUpdate);
}
@@ -292,7 +292,7 @@ public class SendableBuilderImpl implements SendableBuilder {
if (setter != null) {
property.m_createListener = entry -> entry.addListener(event -> {
if (event.value.isBooleanArray()) {
setter.accept(event.value.getBooleanArray());
SmartDashboard.postListenerTask(() -> setter.accept(event.value.getBooleanArray()));
}
}, EntryListenerFlags.kImmediate | EntryListenerFlags.kNew | EntryListenerFlags.kUpdate);
}
@@ -316,7 +316,7 @@ public class SendableBuilderImpl implements SendableBuilder {
if (setter != null) {
property.m_createListener = entry -> entry.addListener(event -> {
if (event.value.isDoubleArray()) {
setter.accept(event.value.getDoubleArray());
SmartDashboard.postListenerTask(() -> setter.accept(event.value.getDoubleArray()));
}
}, EntryListenerFlags.kImmediate | EntryListenerFlags.kNew | EntryListenerFlags.kUpdate);
}
@@ -340,7 +340,7 @@ public class SendableBuilderImpl implements SendableBuilder {
if (setter != null) {
property.m_createListener = entry -> entry.addListener(event -> {
if (event.value.isStringArray()) {
setter.accept(event.value.getStringArray());
SmartDashboard.postListenerTask(() -> setter.accept(event.value.getStringArray()));
}
}, EntryListenerFlags.kImmediate | EntryListenerFlags.kNew | EntryListenerFlags.kUpdate);
}
@@ -363,7 +363,7 @@ public class SendableBuilderImpl implements SendableBuilder {
if (setter != null) {
property.m_createListener = entry -> entry.addListener(event -> {
if (event.value.isRaw()) {
setter.accept(event.value.getRaw());
SmartDashboard.postListenerTask(() -> setter.accept(event.value.getRaw()));
}
}, EntryListenerFlags.kImmediate | EntryListenerFlags.kNew | EntryListenerFlags.kUpdate);
}
@@ -386,7 +386,7 @@ public class SendableBuilderImpl implements SendableBuilder {
}
if (setter != null) {
property.m_createListener = entry -> entry.addListener(event -> {
setter.accept(event.value);
SmartDashboard.postListenerTask(() -> setter.accept(event.value));
}, EntryListenerFlags.kImmediate | EntryListenerFlags.kNew | EntryListenerFlags.kUpdate);
}
m_properties.add(property);

View File

@@ -50,6 +50,11 @@ public class SmartDashboard {
@SuppressWarnings("PMD.UseConcurrentHashMap")
private static final Map<String, Data> tablesToData = new HashMap<>();
/**
* The executor for listener tasks; calls listener tasks synchronously from main thread.
*/
private static final ListenerExecutor listenerExecutor = new ListenerExecutor();
static {
HAL.report(tResourceType.kResourceType_SmartDashboard, 0);
}
@@ -521,6 +526,16 @@ public class SmartDashboard {
return getEntry(key).getRaw(defaultValue);
}
/**
* Posts a task from a listener to the ListenerExecutor, so that it can be run synchronously
* from the main loop on the next call to {@link SmartDashboard#updateValues()}.
*
* @param task The task to run synchronously from the main thread.
*/
public static void postListenerTask(Runnable task) {
listenerExecutor.execute(task);
}
/**
* Puts all sendable data to the dashboard.
*/
@@ -528,5 +543,7 @@ public class SmartDashboard {
for (Data data : tablesToData.values()) {
data.m_builder.updateTable();
}
// Execute posted listener tasks
listenerExecutor.runListenerTasks();
}
}