2020-12-26 14:12:05 -08:00
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
2017-08-18 21:35:53 -07:00
|
|
|
|
2018-07-20 00:03:45 -07:00
|
|
|
#include "hal/Notifier.h"
|
2017-08-18 21:35:53 -07:00
|
|
|
|
2019-11-10 20:54:25 -08:00
|
|
|
#include <atomic>
|
2017-08-18 21:35:53 -07:00
|
|
|
#include <chrono>
|
2019-11-09 11:41:58 -08:00
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstring>
|
2019-11-09 11:41:58 -08:00
|
|
|
#include <string>
|
2017-08-18 21:35:53 -07:00
|
|
|
|
2020-09-27 13:27:53 -07:00
|
|
|
#include <wpi/SmallVector.h>
|
2023-09-17 20:00:16 -07:00
|
|
|
#include <wpi/StringExtras.h>
|
2018-04-29 23:33:19 -07:00
|
|
|
#include <wpi/condition_variable.h>
|
|
|
|
|
#include <wpi/mutex.h>
|
2017-08-27 00:11:52 -07:00
|
|
|
|
2018-05-13 22:02:47 -07:00
|
|
|
#include "HALInitializer.h"
|
2019-11-10 20:54:25 -08:00
|
|
|
#include "NotifierInternal.h"
|
2020-10-03 12:21:03 -04:00
|
|
|
#include "hal/Errors.h"
|
|
|
|
|
#include "hal/HALBase.h"
|
2018-07-20 00:03:45 -07:00
|
|
|
#include "hal/cpp/fpga_clock.h"
|
|
|
|
|
#include "hal/handles/UnlimitedHandleResource.h"
|
2020-06-27 22:11:24 -07:00
|
|
|
#include "hal/simulation/NotifierData.h"
|
2017-08-18 21:35:53 -07:00
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
struct Notifier {
|
2019-11-09 11:41:58 -08:00
|
|
|
std::string name;
|
2020-10-15 20:18:15 -07:00
|
|
|
uint64_t waitTime = UINT64_MAX;
|
2017-11-19 17:58:40 -08:00
|
|
|
bool active = true;
|
2020-10-15 20:18:15 -07:00
|
|
|
bool waitTimeValid = false; // True if waitTime is set and in the future
|
|
|
|
|
bool waitingForAlarm = false; // True if in HAL_WaitForNotifierAlarm()
|
|
|
|
|
uint64_t waitCount = 0; // Counts calls to HAL_WaitForNotifierAlarm()
|
2017-11-19 17:58:40 -08:00
|
|
|
wpi::mutex mutex;
|
|
|
|
|
wpi::condition_variable cond;
|
2017-08-18 21:35:53 -07:00
|
|
|
};
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
using namespace hal;
|
|
|
|
|
|
2020-09-27 13:27:53 -07:00
|
|
|
static wpi::mutex notifiersWaiterMutex;
|
|
|
|
|
static wpi::condition_variable notifiersWaiterCond;
|
|
|
|
|
|
2017-11-19 17:58:40 -08:00
|
|
|
class NotifierHandleContainer
|
|
|
|
|
: public UnlimitedHandleResource<HAL_NotifierHandle, Notifier,
|
|
|
|
|
HAL_HandleEnum::Notifier> {
|
|
|
|
|
public:
|
|
|
|
|
~NotifierHandleContainer() {
|
|
|
|
|
ForEach([](HAL_NotifierHandle handle, Notifier* notifier) {
|
|
|
|
|
{
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(notifier->mutex);
|
2017-11-19 17:58:40 -08:00
|
|
|
notifier->active = false;
|
2020-10-15 20:18:15 -07:00
|
|
|
notifier->waitTimeValid = false;
|
2017-11-19 17:58:40 -08:00
|
|
|
}
|
|
|
|
|
notifier->cond.notify_all(); // wake up any waiting threads
|
|
|
|
|
});
|
2020-09-27 13:27:53 -07:00
|
|
|
notifiersWaiterCond.notify_all();
|
2017-11-19 17:58:40 -08:00
|
|
|
}
|
|
|
|
|
};
|
2017-08-18 21:35:53 -07:00
|
|
|
|
2017-12-10 19:38:53 -08:00
|
|
|
static NotifierHandleContainer* notifierHandles;
|
2019-11-10 20:54:25 -08:00
|
|
|
static std::atomic<bool> notifiersPaused{false};
|
2017-12-10 19:38:53 -08:00
|
|
|
|
|
|
|
|
namespace hal {
|
|
|
|
|
namespace init {
|
|
|
|
|
void InitializeNotifier() {
|
|
|
|
|
static NotifierHandleContainer nH;
|
|
|
|
|
notifierHandles = &nH;
|
|
|
|
|
}
|
|
|
|
|
} // namespace init
|
2019-11-10 20:54:25 -08:00
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
void PauseNotifiers() {
|
|
|
|
|
notifiersPaused = true;
|
|
|
|
|
}
|
2019-11-10 20:54:25 -08:00
|
|
|
|
|
|
|
|
void ResumeNotifiers() {
|
|
|
|
|
notifiersPaused = false;
|
|
|
|
|
WakeupNotifiers();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WakeupNotifiers() {
|
|
|
|
|
notifierHandles->ForEach([](HAL_NotifierHandle handle, Notifier* notifier) {
|
|
|
|
|
notifier->cond.notify_all();
|
|
|
|
|
});
|
|
|
|
|
}
|
2020-09-27 13:27:53 -07:00
|
|
|
|
2020-10-15 20:18:15 -07:00
|
|
|
void WaitNotifiers() {
|
|
|
|
|
std::unique_lock ulock(notifiersWaiterMutex);
|
|
|
|
|
wpi::SmallVector<HAL_NotifierHandle, 8> waiters;
|
|
|
|
|
|
|
|
|
|
// Wait for all Notifiers to hit HAL_WaitForNotifierAlarm()
|
|
|
|
|
notifierHandles->ForEach([&](HAL_NotifierHandle handle, Notifier* notifier) {
|
|
|
|
|
std::scoped_lock lock(notifier->mutex);
|
|
|
|
|
if (notifier->active && !notifier->waitingForAlarm) {
|
|
|
|
|
waiters.emplace_back(handle);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
for (;;) {
|
|
|
|
|
int count = 0;
|
|
|
|
|
int end = waiters.size();
|
|
|
|
|
while (count < end) {
|
|
|
|
|
auto& it = waiters[count];
|
|
|
|
|
if (auto notifier = notifierHandles->Get(it)) {
|
|
|
|
|
std::scoped_lock lock(notifier->mutex);
|
|
|
|
|
if (notifier->active && !notifier->waitingForAlarm) {
|
|
|
|
|
++count;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// No longer need to wait for it, put at end so it can be erased
|
|
|
|
|
std::swap(it, waiters[--end]);
|
|
|
|
|
}
|
2020-12-28 12:58:06 -08:00
|
|
|
if (count == 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-10-15 20:18:15 -07:00
|
|
|
waiters.resize(count);
|
|
|
|
|
notifiersWaiterCond.wait_for(ulock, std::chrono::duration<double>(1));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-27 13:27:53 -07:00
|
|
|
void WakeupWaitNotifiers() {
|
|
|
|
|
std::unique_lock ulock(notifiersWaiterMutex);
|
|
|
|
|
int32_t status = 0;
|
|
|
|
|
uint64_t curTime = HAL_GetFPGATime(&status);
|
|
|
|
|
wpi::SmallVector<std::pair<HAL_NotifierHandle, uint64_t>, 8> waiters;
|
2020-10-15 20:18:15 -07:00
|
|
|
|
|
|
|
|
// Wake up Notifiers that have expired timeouts
|
2020-09-27 13:27:53 -07:00
|
|
|
notifierHandles->ForEach([&](HAL_NotifierHandle handle, Notifier* notifier) {
|
|
|
|
|
std::scoped_lock lock(notifier->mutex);
|
2020-10-15 20:18:15 -07:00
|
|
|
|
|
|
|
|
// Only wait for the Notifier if it has a valid timeout that's expired
|
|
|
|
|
if (notifier->active && notifier->waitTimeValid &&
|
|
|
|
|
curTime >= notifier->waitTime) {
|
|
|
|
|
waiters.emplace_back(handle, notifier->waitCount);
|
2020-09-27 13:27:53 -07:00
|
|
|
notifier->cond.notify_all();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
for (;;) {
|
|
|
|
|
int count = 0;
|
|
|
|
|
int end = waiters.size();
|
|
|
|
|
while (count < end) {
|
|
|
|
|
auto& it = waiters[count];
|
|
|
|
|
if (auto notifier = notifierHandles->Get(it.first)) {
|
|
|
|
|
std::scoped_lock lock(notifier->mutex);
|
2020-10-15 20:18:15 -07:00
|
|
|
|
|
|
|
|
// waitCount is used here instead of waitingForAlarm because we want to
|
|
|
|
|
// wait until HAL_WaitForNotifierAlarm() is exited, then reentered
|
|
|
|
|
if (notifier->active && notifier->waitCount == it.second) {
|
2020-09-27 13:27:53 -07:00
|
|
|
++count;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-10-15 20:18:15 -07:00
|
|
|
// No longer need to wait for it, put at end so it can be erased
|
2020-09-27 13:27:53 -07:00
|
|
|
it.swap(waiters[--end]);
|
|
|
|
|
}
|
2020-12-28 12:58:06 -08:00
|
|
|
if (count == 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-09-27 13:27:53 -07:00
|
|
|
waiters.resize(count);
|
|
|
|
|
notifiersWaiterCond.wait_for(ulock, std::chrono::duration<double>(1));
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-12-10 19:38:53 -08:00
|
|
|
} // namespace hal
|
2017-08-18 21:35:53 -07:00
|
|
|
|
2017-11-19 17:58:40 -08:00
|
|
|
extern "C" {
|
2017-08-18 21:35:53 -07:00
|
|
|
|
2017-11-19 17:58:40 -08:00
|
|
|
HAL_NotifierHandle HAL_InitializeNotifier(int32_t* status) {
|
2018-05-13 22:02:47 -07:00
|
|
|
hal::init::CheckInit();
|
2017-08-18 21:35:53 -07:00
|
|
|
std::shared_ptr<Notifier> notifier = std::make_shared<Notifier>();
|
2017-12-10 19:38:53 -08:00
|
|
|
HAL_NotifierHandle handle = notifierHandles->Allocate(notifier);
|
2017-08-18 21:35:53 -07:00
|
|
|
if (handle == HAL_kInvalidHandle) {
|
|
|
|
|
*status = HAL_HANDLE_ERROR;
|
|
|
|
|
return HAL_kInvalidHandle;
|
|
|
|
|
}
|
|
|
|
|
return handle;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-28 22:05:26 -08:00
|
|
|
HAL_Bool HAL_SetNotifierThreadPriority(HAL_Bool realTime, int32_t priority,
|
|
|
|
|
int32_t* status) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-09 11:41:58 -08:00
|
|
|
void HAL_SetNotifierName(HAL_NotifierHandle notifierHandle, const char* name,
|
|
|
|
|
int32_t* status) {
|
|
|
|
|
auto notifier = notifierHandles->Get(notifierHandle);
|
2020-12-28 12:58:06 -08:00
|
|
|
if (!notifier) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-11-09 11:41:58 -08:00
|
|
|
std::scoped_lock lock(notifier->mutex);
|
|
|
|
|
notifier->name = name;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-19 17:58:40 -08:00
|
|
|
void HAL_StopNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
|
2017-12-10 19:38:53 -08:00
|
|
|
auto notifier = notifierHandles->Get(notifierHandle);
|
2020-12-28 12:58:06 -08:00
|
|
|
if (!notifier) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-11-19 17:58:40 -08:00
|
|
|
|
|
|
|
|
{
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(notifier->mutex);
|
2017-11-19 17:58:40 -08:00
|
|
|
notifier->active = false;
|
2020-10-15 20:18:15 -07:00
|
|
|
notifier->waitTimeValid = false;
|
2017-11-19 17:58:40 -08:00
|
|
|
}
|
|
|
|
|
notifier->cond.notify_all();
|
2017-08-18 21:35:53 -07:00
|
|
|
}
|
2017-11-19 17:58:40 -08:00
|
|
|
|
|
|
|
|
void HAL_CleanNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
|
2017-12-10 19:38:53 -08:00
|
|
|
auto notifier = notifierHandles->Free(notifierHandle);
|
2020-12-28 12:58:06 -08:00
|
|
|
if (!notifier) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-11-19 17:58:40 -08:00
|
|
|
|
|
|
|
|
// Just in case HAL_StopNotifier() wasn't called...
|
|
|
|
|
{
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(notifier->mutex);
|
2017-11-19 17:58:40 -08:00
|
|
|
notifier->active = false;
|
2020-10-15 20:18:15 -07:00
|
|
|
notifier->waitTimeValid = false;
|
2017-11-19 17:58:40 -08:00
|
|
|
}
|
|
|
|
|
notifier->cond.notify_all();
|
2017-08-18 21:35:53 -07:00
|
|
|
}
|
2017-11-19 17:58:40 -08:00
|
|
|
|
2017-08-18 21:35:53 -07:00
|
|
|
void HAL_UpdateNotifierAlarm(HAL_NotifierHandle notifierHandle,
|
|
|
|
|
uint64_t triggerTime, int32_t* status) {
|
2017-12-10 19:38:53 -08:00
|
|
|
auto notifier = notifierHandles->Get(notifierHandle);
|
2020-12-28 12:58:06 -08:00
|
|
|
if (!notifier) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-11-19 17:58:40 -08:00
|
|
|
|
|
|
|
|
{
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(notifier->mutex);
|
2017-11-19 17:58:40 -08:00
|
|
|
notifier->waitTime = triggerTime;
|
2020-10-15 20:18:15 -07:00
|
|
|
notifier->waitTimeValid = (triggerTime != UINT64_MAX);
|
2017-11-19 17:58:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We wake up any waiters to change how long they're sleeping for
|
|
|
|
|
notifier->cond.notify_all();
|
2017-08-18 21:35:53 -07:00
|
|
|
}
|
2017-11-19 17:58:40 -08:00
|
|
|
|
|
|
|
|
void HAL_CancelNotifierAlarm(HAL_NotifierHandle notifierHandle,
|
|
|
|
|
int32_t* status) {
|
2017-12-10 19:38:53 -08:00
|
|
|
auto notifier = notifierHandles->Get(notifierHandle);
|
2020-12-28 12:58:06 -08:00
|
|
|
if (!notifier) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-11-19 17:58:40 -08:00
|
|
|
|
|
|
|
|
{
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(notifier->mutex);
|
2020-10-15 20:18:15 -07:00
|
|
|
notifier->waitTimeValid = false;
|
2017-11-19 17:58:40 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t HAL_WaitForNotifierAlarm(HAL_NotifierHandle notifierHandle,
|
|
|
|
|
int32_t* status) {
|
2017-12-10 19:38:53 -08:00
|
|
|
auto notifier = notifierHandles->Get(notifierHandle);
|
2020-12-28 12:58:06 -08:00
|
|
|
if (!notifier) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2017-11-19 17:58:40 -08:00
|
|
|
|
2020-09-27 13:27:53 -07:00
|
|
|
std::unique_lock ulock(notifiersWaiterMutex);
|
2019-07-07 19:17:14 -07:00
|
|
|
std::unique_lock lock(notifier->mutex);
|
2020-10-15 20:18:15 -07:00
|
|
|
notifier->waitingForAlarm = true;
|
|
|
|
|
++notifier->waitCount;
|
2020-09-27 13:27:53 -07:00
|
|
|
ulock.unlock();
|
|
|
|
|
notifiersWaiterCond.notify_all();
|
2017-11-19 17:58:40 -08:00
|
|
|
while (notifier->active) {
|
2020-07-20 23:30:14 -07:00
|
|
|
uint64_t curTime = HAL_GetFPGATime(status);
|
2020-10-15 20:18:15 -07:00
|
|
|
if (notifier->waitTimeValid && curTime >= notifier->waitTime) {
|
|
|
|
|
notifier->waitTimeValid = false;
|
|
|
|
|
notifier->waitingForAlarm = false;
|
2020-07-20 23:30:14 -07:00
|
|
|
return curTime;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-15 20:18:15 -07:00
|
|
|
double waitDuration;
|
|
|
|
|
if (!notifier->waitTimeValid || notifiersPaused) {
|
2017-11-19 17:58:40 -08:00
|
|
|
// If not running, wait 1000 seconds
|
2020-10-15 20:18:15 -07:00
|
|
|
waitDuration = 1000.0;
|
2017-11-19 17:58:40 -08:00
|
|
|
} else {
|
2020-10-15 20:18:15 -07:00
|
|
|
waitDuration = (notifier->waitTime - curTime) * 1e-6;
|
2017-11-19 17:58:40 -08:00
|
|
|
}
|
|
|
|
|
|
2020-10-15 20:18:15 -07:00
|
|
|
notifier->cond.wait_for(lock, std::chrono::duration<double>(waitDuration));
|
2017-11-19 17:58:40 -08:00
|
|
|
}
|
2020-10-15 20:18:15 -07:00
|
|
|
notifier->waitingForAlarm = false;
|
2017-11-19 17:58:40 -08:00
|
|
|
return 0;
|
2017-08-18 21:35:53 -07:00
|
|
|
}
|
|
|
|
|
|
2019-11-09 11:41:58 -08:00
|
|
|
uint64_t HALSIM_GetNextNotifierTimeout(void) {
|
|
|
|
|
uint64_t timeout = UINT64_MAX;
|
|
|
|
|
notifierHandles->ForEach([&](HAL_NotifierHandle, Notifier* notifier) {
|
|
|
|
|
std::scoped_lock lock(notifier->mutex);
|
2020-10-15 20:18:15 -07:00
|
|
|
if (notifier->active && notifier->waitTimeValid &&
|
2020-12-28 12:58:06 -08:00
|
|
|
timeout > notifier->waitTime) {
|
2019-11-09 11:41:58 -08:00
|
|
|
timeout = notifier->waitTime;
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
2019-11-09 11:41:58 -08:00
|
|
|
});
|
|
|
|
|
return timeout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t HALSIM_GetNumNotifiers(void) {
|
|
|
|
|
int32_t count = 0;
|
|
|
|
|
notifierHandles->ForEach([&](HAL_NotifierHandle, Notifier* notifier) {
|
|
|
|
|
std::scoped_lock lock(notifier->mutex);
|
2020-12-28 12:58:06 -08:00
|
|
|
if (notifier->active) {
|
|
|
|
|
++count;
|
|
|
|
|
}
|
2019-11-09 11:41:58 -08:00
|
|
|
});
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t HALSIM_GetNotifierInfo(struct HALSIM_NotifierInfo* arr, int32_t size) {
|
|
|
|
|
int32_t num = 0;
|
|
|
|
|
notifierHandles->ForEach([&](HAL_NotifierHandle handle, Notifier* notifier) {
|
|
|
|
|
std::scoped_lock lock(notifier->mutex);
|
2020-12-28 12:58:06 -08:00
|
|
|
if (!notifier->active) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-11-09 11:41:58 -08:00
|
|
|
if (num < size) {
|
|
|
|
|
arr[num].handle = handle;
|
|
|
|
|
if (notifier->name.empty()) {
|
2023-09-17 20:00:16 -07:00
|
|
|
wpi::format_to_n_c_str(arr[num].name, sizeof(arr[num].name),
|
|
|
|
|
"Notifier{}",
|
|
|
|
|
static_cast<int>(getHandleIndex(handle)));
|
2019-11-09 11:41:58 -08:00
|
|
|
} else {
|
|
|
|
|
std::strncpy(arr[num].name, notifier->name.c_str(),
|
2021-12-19 16:46:12 -08:00
|
|
|
sizeof(arr[num].name) - 1);
|
2019-11-09 11:41:58 -08:00
|
|
|
arr[num].name[sizeof(arr[num].name) - 1] = '\0';
|
|
|
|
|
}
|
|
|
|
|
arr[num].timeout = notifier->waitTime;
|
2020-10-15 20:18:15 -07:00
|
|
|
arr[num].waitTimeValid = notifier->waitTimeValid;
|
2019-11-09 11:41:58 -08:00
|
|
|
}
|
|
|
|
|
++num;
|
|
|
|
|
});
|
|
|
|
|
return num;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-18 21:35:53 -07:00
|
|
|
} // extern "C"
|