/*----------------------------------------------------------------------------*/ /* Copyright (c) 2016-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 "hal/Notifier.h" #include #include #include #include #include #include #include #include #include "HALInitializer.h" #include "NotifierInternal.h" #include "hal/HAL.h" #include "hal/cpp/fpga_clock.h" #include "hal/handles/UnlimitedHandleResource.h" #include "mockdata/NotifierData.h" namespace { struct Notifier { std::string name; uint64_t waitTime; bool active = true; bool running = false; wpi::mutex mutex; wpi::condition_variable cond; }; } // namespace using namespace hal; class NotifierHandleContainer : public UnlimitedHandleResource { public: ~NotifierHandleContainer() { ForEach([](HAL_NotifierHandle handle, Notifier* notifier) { { std::scoped_lock lock(notifier->mutex); notifier->active = false; notifier->running = false; } notifier->cond.notify_all(); // wake up any waiting threads }); } }; static NotifierHandleContainer* notifierHandles; static std::atomic notifiersPaused{false}; namespace hal { namespace init { void InitializeNotifier() { static NotifierHandleContainer nH; notifierHandles = &nH; } } // namespace init void PauseNotifiers() { notifiersPaused = true; } void ResumeNotifiers() { notifiersPaused = false; WakeupNotifiers(); } void WakeupNotifiers() { notifierHandles->ForEach([](HAL_NotifierHandle handle, Notifier* notifier) { notifier->cond.notify_all(); }); } } // namespace hal extern "C" { HAL_NotifierHandle HAL_InitializeNotifier(int32_t* status) { hal::init::CheckInit(); std::shared_ptr notifier = std::make_shared(); HAL_NotifierHandle handle = notifierHandles->Allocate(notifier); if (handle == HAL_kInvalidHandle) { *status = HAL_HANDLE_ERROR; return HAL_kInvalidHandle; } return handle; } void HAL_SetNotifierName(HAL_NotifierHandle notifierHandle, const char* name, int32_t* status) { auto notifier = notifierHandles->Get(notifierHandle); if (!notifier) return; std::scoped_lock lock(notifier->mutex); notifier->name = name; } void HAL_StopNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) { auto notifier = notifierHandles->Get(notifierHandle); if (!notifier) return; { std::scoped_lock lock(notifier->mutex); notifier->active = false; notifier->running = false; } notifier->cond.notify_all(); } void HAL_CleanNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) { auto notifier = notifierHandles->Free(notifierHandle); if (!notifier) return; // Just in case HAL_StopNotifier() wasn't called... { std::scoped_lock lock(notifier->mutex); notifier->active = false; notifier->running = false; } notifier->cond.notify_all(); } void HAL_UpdateNotifierAlarm(HAL_NotifierHandle notifierHandle, uint64_t triggerTime, int32_t* status) { auto notifier = notifierHandles->Get(notifierHandle); if (!notifier) return; { std::scoped_lock lock(notifier->mutex); notifier->waitTime = triggerTime; notifier->running = true; } // We wake up any waiters to change how long they're sleeping for notifier->cond.notify_all(); } void HAL_CancelNotifierAlarm(HAL_NotifierHandle notifierHandle, int32_t* status) { auto notifier = notifierHandles->Get(notifierHandle); if (!notifier) return; { std::scoped_lock lock(notifier->mutex); notifier->running = false; } } uint64_t HAL_WaitForNotifierAlarm(HAL_NotifierHandle notifierHandle, int32_t* status) { auto notifier = notifierHandles->Get(notifierHandle); if (!notifier) return 0; std::unique_lock lock(notifier->mutex); while (notifier->active) { double waitTime; if (!notifier->running || notifiersPaused) { waitTime = (HAL_GetFPGATime(status) * 1e-6) + 1000.0; // If not running, wait 1000 seconds } else { waitTime = notifier->waitTime * 1e-6; } auto timeoutTime = hal::fpga_clock::epoch() + std::chrono::duration(waitTime); notifier->cond.wait_until(lock, timeoutTime); if (!notifier->running) continue; if (!notifier->active) break; uint64_t curTime = HAL_GetFPGATime(status); if (curTime < notifier->waitTime) continue; notifier->running = false; return curTime; } return 0; } uint64_t HALSIM_GetNextNotifierTimeout(void) { uint64_t timeout = UINT64_MAX; notifierHandles->ForEach([&](HAL_NotifierHandle, Notifier* notifier) { std::scoped_lock lock(notifier->mutex); if (notifier->active && notifier->running && timeout > notifier->waitTime) timeout = notifier->waitTime; }); return timeout; } int32_t HALSIM_GetNumNotifiers(void) { int32_t count = 0; notifierHandles->ForEach([&](HAL_NotifierHandle, Notifier* notifier) { std::scoped_lock lock(notifier->mutex); if (notifier->active) ++count; }); 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); if (!notifier->active) return; if (num < size) { arr[num].handle = handle; if (notifier->name.empty()) { std::snprintf(arr[num].name, sizeof(arr[num].name), "Notifier%d", static_cast(getHandleIndex(handle))); } else { std::strncpy(arr[num].name, notifier->name.c_str(), sizeof(arr[num].name)); arr[num].name[sizeof(arr[num].name) - 1] = '\0'; } arr[num].timeout = notifier->waitTime; arr[num].running = notifier->running; } ++num; }); return num; } } // extern "C"