mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-04 03:11:43 +00:00
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:
@@ -0,0 +1,28 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "frc/smartdashboard/ListenerExecutor.h"
|
||||
|
||||
using namespace frc::detail;
|
||||
|
||||
void ListenerExecutor::Execute(std::function<void()> task) {
|
||||
std::scoped_lock lock(m_lock);
|
||||
m_tasks.emplace_back(task);
|
||||
}
|
||||
|
||||
void ListenerExecutor::RunListenerTasks() {
|
||||
// Locally copy tasks from internal list; minimizes blocking time
|
||||
{
|
||||
std::scoped_lock lock(m_lock);
|
||||
std::swap(m_tasks, m_runningTasks);
|
||||
}
|
||||
|
||||
for (auto&& task : m_runningTasks) {
|
||||
task();
|
||||
}
|
||||
m_runningTasks.clear();
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
|
||||
/* Copyright (c) 2017-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. */
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <ntcore_cpp.h>
|
||||
#include <wpi/SmallString.h>
|
||||
|
||||
#include "frc/smartdashboard/SmartDashboard.h"
|
||||
|
||||
using namespace frc;
|
||||
|
||||
void SendableBuilderImpl::SetTable(std::shared_ptr<nt::NetworkTable> table) {
|
||||
@@ -88,7 +90,8 @@ void SendableBuilderImpl::AddBooleanProperty(const wpi::Twine& key,
|
||||
return entry.AddListener(
|
||||
[=](const nt::EntryNotification& event) {
|
||||
if (!event.value->IsBoolean()) return;
|
||||
setter(event.value->GetBoolean());
|
||||
SmartDashboard::PostListenerTask(
|
||||
[=] { setter(event.value->GetBoolean()); });
|
||||
},
|
||||
NT_NOTIFY_IMMEDIATE | NT_NOTIFY_NEW | NT_NOTIFY_UPDATE);
|
||||
};
|
||||
@@ -111,7 +114,8 @@ void SendableBuilderImpl::AddDoubleProperty(
|
||||
return entry.AddListener(
|
||||
[=](const nt::EntryNotification& event) {
|
||||
if (!event.value->IsDouble()) return;
|
||||
setter(event.value->GetDouble());
|
||||
SmartDashboard::PostListenerTask(
|
||||
[=] { setter(event.value->GetDouble()); });
|
||||
},
|
||||
NT_NOTIFY_IMMEDIATE | NT_NOTIFY_NEW | NT_NOTIFY_UPDATE);
|
||||
};
|
||||
@@ -134,7 +138,8 @@ void SendableBuilderImpl::AddStringProperty(
|
||||
return entry.AddListener(
|
||||
[=](const nt::EntryNotification& event) {
|
||||
if (!event.value->IsString()) return;
|
||||
setter(event.value->GetString());
|
||||
SmartDashboard::PostListenerTask(
|
||||
[=] { setter(event.value->GetString()); });
|
||||
},
|
||||
NT_NOTIFY_IMMEDIATE | NT_NOTIFY_NEW | NT_NOTIFY_UPDATE);
|
||||
};
|
||||
@@ -157,7 +162,8 @@ void SendableBuilderImpl::AddBooleanArrayProperty(
|
||||
return entry.AddListener(
|
||||
[=](const nt::EntryNotification& event) {
|
||||
if (!event.value->IsBooleanArray()) return;
|
||||
setter(event.value->GetBooleanArray());
|
||||
SmartDashboard::PostListenerTask(
|
||||
[=] { setter(event.value->GetBooleanArray()); });
|
||||
},
|
||||
NT_NOTIFY_IMMEDIATE | NT_NOTIFY_NEW | NT_NOTIFY_UPDATE);
|
||||
};
|
||||
@@ -180,7 +186,8 @@ void SendableBuilderImpl::AddDoubleArrayProperty(
|
||||
return entry.AddListener(
|
||||
[=](const nt::EntryNotification& event) {
|
||||
if (!event.value->IsDoubleArray()) return;
|
||||
setter(event.value->GetDoubleArray());
|
||||
SmartDashboard::PostListenerTask(
|
||||
[=] { setter(event.value->GetDoubleArray()); });
|
||||
},
|
||||
NT_NOTIFY_IMMEDIATE | NT_NOTIFY_NEW | NT_NOTIFY_UPDATE);
|
||||
};
|
||||
@@ -203,7 +210,8 @@ void SendableBuilderImpl::AddStringArrayProperty(
|
||||
return entry.AddListener(
|
||||
[=](const nt::EntryNotification& event) {
|
||||
if (!event.value->IsStringArray()) return;
|
||||
setter(event.value->GetStringArray());
|
||||
SmartDashboard::PostListenerTask(
|
||||
[=] { setter(event.value->GetStringArray()); });
|
||||
},
|
||||
NT_NOTIFY_IMMEDIATE | NT_NOTIFY_NEW | NT_NOTIFY_UPDATE);
|
||||
};
|
||||
@@ -226,7 +234,8 @@ void SendableBuilderImpl::AddRawProperty(
|
||||
return entry.AddListener(
|
||||
[=](const nt::EntryNotification& event) {
|
||||
if (!event.value->IsRaw()) return;
|
||||
setter(event.value->GetRaw());
|
||||
SmartDashboard::PostListenerTask(
|
||||
[=] { setter(event.value->GetRaw()); });
|
||||
},
|
||||
NT_NOTIFY_IMMEDIATE | NT_NOTIFY_NEW | NT_NOTIFY_UPDATE);
|
||||
};
|
||||
@@ -247,7 +256,9 @@ void SendableBuilderImpl::AddValueProperty(
|
||||
m_properties.back().createListener =
|
||||
[=](nt::NetworkTableEntry entry) -> NT_EntryListener {
|
||||
return entry.AddListener(
|
||||
[=](const nt::EntryNotification& event) { setter(event.value); },
|
||||
[=](const nt::EntryNotification& event) {
|
||||
SmartDashboard::PostListenerTask([=] { setter(event.value); });
|
||||
},
|
||||
NT_NOTIFY_IMMEDIATE | NT_NOTIFY_NEW | NT_NOTIFY_UPDATE);
|
||||
};
|
||||
}
|
||||
@@ -271,7 +282,8 @@ void SendableBuilderImpl::AddSmallStringProperty(
|
||||
return entry.AddListener(
|
||||
[=](const nt::EntryNotification& event) {
|
||||
if (!event.value->IsString()) return;
|
||||
setter(event.value->GetString());
|
||||
SmartDashboard::PostListenerTask(
|
||||
[=] { setter(event.value->GetString()); });
|
||||
},
|
||||
NT_NOTIFY_IMMEDIATE | NT_NOTIFY_NEW | NT_NOTIFY_UPDATE);
|
||||
};
|
||||
@@ -296,7 +308,8 @@ void SendableBuilderImpl::AddSmallBooleanArrayProperty(
|
||||
return entry.AddListener(
|
||||
[=](const nt::EntryNotification& event) {
|
||||
if (!event.value->IsBooleanArray()) return;
|
||||
setter(event.value->GetBooleanArray());
|
||||
SmartDashboard::PostListenerTask(
|
||||
[=] { setter(event.value->GetBooleanArray()); });
|
||||
},
|
||||
NT_NOTIFY_IMMEDIATE | NT_NOTIFY_NEW | NT_NOTIFY_UPDATE);
|
||||
};
|
||||
@@ -322,7 +335,8 @@ void SendableBuilderImpl::AddSmallDoubleArrayProperty(
|
||||
return entry.AddListener(
|
||||
[=](const nt::EntryNotification& event) {
|
||||
if (!event.value->IsDoubleArray()) return;
|
||||
setter(event.value->GetDoubleArray());
|
||||
SmartDashboard::PostListenerTask(
|
||||
[=] { setter(event.value->GetDoubleArray()); });
|
||||
},
|
||||
NT_NOTIFY_IMMEDIATE | NT_NOTIFY_NEW | NT_NOTIFY_UPDATE);
|
||||
};
|
||||
@@ -349,7 +363,8 @@ void SendableBuilderImpl::AddSmallStringArrayProperty(
|
||||
return entry.AddListener(
|
||||
[=](const nt::EntryNotification& event) {
|
||||
if (!event.value->IsStringArray()) return;
|
||||
setter(event.value->GetStringArray());
|
||||
SmartDashboard::PostListenerTask(
|
||||
[=] { setter(event.value->GetStringArray()); });
|
||||
},
|
||||
NT_NOTIFY_IMMEDIATE | NT_NOTIFY_NEW | NT_NOTIFY_UPDATE);
|
||||
};
|
||||
@@ -374,7 +389,8 @@ void SendableBuilderImpl::AddSmallRawProperty(
|
||||
return entry.AddListener(
|
||||
[=](const nt::EntryNotification& event) {
|
||||
if (!event.value->IsRaw()) return;
|
||||
setter(event.value->GetRaw());
|
||||
SmartDashboard::PostListenerTask(
|
||||
[=] { setter(event.value->GetRaw()); });
|
||||
},
|
||||
NT_NOTIFY_IMMEDIATE | NT_NOTIFY_NEW | NT_NOTIFY_UPDATE);
|
||||
};
|
||||
|
||||
@@ -254,10 +254,17 @@ std::shared_ptr<nt::Value> SmartDashboard::GetValue(wpi::StringRef keyName) {
|
||||
return Singleton::GetInstance().table->GetEntry(keyName).GetValue();
|
||||
}
|
||||
|
||||
detail::ListenerExecutor SmartDashboard::listenerExecutor;
|
||||
|
||||
void SmartDashboard::PostListenerTask(std::function<void()> task) {
|
||||
listenerExecutor.Execute(task);
|
||||
}
|
||||
|
||||
void SmartDashboard::UpdateValues() {
|
||||
auto& inst = Singleton::GetInstance();
|
||||
std::scoped_lock lock(inst.tablesToDataMutex);
|
||||
for (auto& i : inst.tablesToData) {
|
||||
i.getValue().builder.UpdateTable();
|
||||
}
|
||||
listenerExecutor.RunListenerTasks();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/mutex.h>
|
||||
|
||||
namespace frc::detail {
|
||||
/**
|
||||
* An executor for running listener tasks posted by Sendable listeners
|
||||
* synchronously from the main application thread.
|
||||
*
|
||||
* @see Sendable
|
||||
*/
|
||||
class ListenerExecutor {
|
||||
public:
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
void Execute(std::function<void()> task);
|
||||
|
||||
/**
|
||||
* Runs all posted tasks. Called periodically from main thread.
|
||||
*/
|
||||
void RunListenerTasks();
|
||||
|
||||
private:
|
||||
std::vector<std::function<void()>> m_tasks;
|
||||
std::vector<std::function<void()>> m_runningTasks;
|
||||
wpi::mutex m_lock;
|
||||
};
|
||||
} // namespace frc::detail
|
||||
@@ -1,5 +1,5 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2011-2018 FIRST. All Rights Reserved. */
|
||||
/* Copyright (c) 2011-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. */
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <networktables/NetworkTableValue.h>
|
||||
|
||||
#include "frc/ErrorBase.h"
|
||||
#include "frc/smartdashboard/ListenerExecutor.h"
|
||||
#include "frc/smartdashboard/SendableBase.h"
|
||||
|
||||
namespace frc {
|
||||
@@ -401,6 +402,15 @@ class SmartDashboard : public ErrorBase, public SendableBase {
|
||||
*/
|
||||
static std::shared_ptr<nt::Value> GetValue(wpi::StringRef keyName);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
static void PostListenerTask(std::function<void()> task);
|
||||
|
||||
/**
|
||||
* Puts all sendable data to the dashboard.
|
||||
*/
|
||||
@@ -408,6 +418,8 @@ class SmartDashboard : public ErrorBase, public SendableBase {
|
||||
|
||||
private:
|
||||
virtual ~SmartDashboard() = default;
|
||||
|
||||
static detail::ListenerExecutor listenerExecutor;
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user