text: /*----------------------------------------------------------------------------*/
  /* Copyright (c) 2016-2020 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 <atomic>
  #include <cstdlib>  // For std::atexit()
  #include <memory>
  #include <thread>
  
  #include <wpi/condition_variable.h>
  #include <wpi/mutex.h>
  
  #include "HALInitializer.h"
  #include "HALInternal.h"
  #include "hal/ChipObject.h"
  #include "hal/Errors.h"
  #include "hal/HAL.h"
  #include "hal/handles/UnlimitedHandleResource.h"
  
  using namespace hal;
  
  static constexpr int32_t kTimerInterruptNumber = 28;
  
  static wpi::mutex notifierMutex;
  static std::unique_ptr<tAlarm> notifierAlarm;
  static std::thread notifierThread;
  static uint64_t closestTrigger{UINT64_MAX};
  
  namespace {
  
  struct Notifier {
    uint64_t triggerTime = UINT64_MAX;
    uint64_t triggeredTime = UINT64_MAX;
    bool active = true;
    wpi::mutex mutex;
    wpi::condition_variable cond;
  };
  
  }  // namespace
  
  static std::atomic_flag notifierAtexitRegistered{ATOMIC_FLAG_INIT};
  static std::atomic_int notifierRefCount{0};
  static std::atomic_bool notifierRunning{false};
  
  using namespace hal;
  
  class NotifierHandleContainer
      : public UnlimitedHandleResource<HAL_NotifierHandle, Notifier,
                                       HAL_HandleEnum::Notifier> {
   public:
    ~NotifierHandleContainer() {
      ForEach([](HAL_NotifierHandle handle, Notifier* notifier) {
        {
          std::scoped_lock lock(notifier->mutex);
          notifier->triggerTime = UINT64_MAX;
          notifier->triggeredTime = 0;
          notifier->active = false;
        }
        notifier->cond.notify_all();  // wake up any waiting threads
      });
    }
  };
  
  static NotifierHandleContainer* notifierHandles;
  
  static void alarmCallback() {
    std::scoped_lock lock(notifierMutex);
    int32_t status = 0;
    uint64_t currentTime = 0;
  
    // the hardware disables itself after each alarm
    closestTrigger = UINT64_MAX;
  
    // process all notifiers
    notifierHandles->ForEach([&](HAL_NotifierHandle handle, Notifier* notifier) {
      if (notifier->triggerTime == UINT64_MAX) return;
      if (currentTime == 0) currentTime = HAL_GetFPGATime(&status);
      std::unique_lock lock(notifier->mutex);
      if (notifier->triggerTime < currentTime) {
        notifier->triggerTime = UINT64_MAX;
        notifier->triggeredTime = currentTime;
        lock.unlock();
        notifier->cond.notify_all();
      } else if (notifier->triggerTime < closestTrigger) {
        closestTrigger = notifier->triggerTime;
      }
    });
  
    if (notifierAlarm && closestTrigger != UINT64_MAX) {
      // Simply truncate the hardware trigger time to 32-bit.
      notifierAlarm->writeTriggerTime(static_cast<uint32_t>(closestTrigger),
                                      &status);
      // Enable the alarm.  The hardware disables itself after each alarm.
      notifierAlarm->writeEnable(true, &status);
    }
  }
  
  static void notifierThreadMain() {
    tRioStatusCode status = 0;
    tInterruptManager manager{1 << kTimerInterruptNumber, true, &status};
    while (notifierRunning) {
      auto triggeredMask = manager.watch(10000, false, &status);
      if (!notifierRunning) break;
      if (triggeredMask == 0) continue;
      alarmCallback();
    }
  }
  
  static void cleanupNotifierAtExit() {
    int32_t status = 0;
    if (notifierAlarm) notifierAlarm->writeEnable(false, &status);
    notifierAlarm = nullptr;
    notifierRunning = false;
    hal::ReleaseFPGAInterrupt(kTimerInterruptNumber);
    if (notifierThread.joinable()) notifierThread.join();
  }
  
  namespace hal {
  namespace init {
  void InitializeNotifier() {
    static NotifierHandleContainer nH;
    notifierHandles = &nH;
  }
  }  // namespace init
  }  // namespace hal
  
  extern "C" {
  
  HAL_NotifierHandle HAL_InitializeNotifier(int32_t* status) {
    hal::init::CheckInit();
    if (!notifierAtexitRegistered.test_and_set())
      std::atexit(cleanupNotifierAtExit);
  
    if (notifierRefCount.fetch_add(1) == 0) {
      std::scoped_lock lock(notifierMutex);
      notifierThread = std::thread(notifierThreadMain);
      notifierAlarm.reset(tAlarm::create(status));
    }
  
    std::shared_ptr<Notifier> notifier = std::make_shared<Notifier>();
    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) {}
  
  void HAL_StopNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
    auto notifier = notifierHandles->Get(notifierHandle);
    if (!notifier) return;
  
    {
      std::scoped_lock lock(notifier->mutex);
      notifier->triggerTime = UINT64_MAX;
      notifier->triggeredTime = 0;
      notifier->active = false;
    }
    notifier->cond.notify_all();  // wake up any waiting threads
  }
  
  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->triggerTime = UINT64_MAX;
      notifier->triggeredTime = 0;
      notifier->active = false;
    }
    notifier->cond.notify_all();
  
    if (notifierRefCount.fetch_sub(1) == 1) {
      // if this was the last notifier, clean up alarm and thread
      // the notifier can call back into our callback, so don't hold the lock
      // here (the atomic fetch_sub will prevent multiple parallel entries
      // into this function)
  
      if (notifierAlarm) notifierAlarm->writeEnable(false, status);
      notifierRunning = false;
      hal::ReleaseFPGAInterrupt(kTimerInterruptNumber);
      if (notifierThread.joinable()) notifierThread.join();
  
      std::scoped_lock lock(notifierMutex);
      notifierAlarm = nullptr;
      closestTrigger = UINT64_MAX;
    }
  }
  
  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->triggerTime = triggerTime;
      notifier->triggeredTime = UINT64_MAX;
    }
  
    std::scoped_lock lock(notifierMutex);
    // Update alarm time if closer than current.
    if (triggerTime < closestTrigger) {
      bool wasActive = (closestTrigger != UINT64_MAX);
      closestTrigger = triggerTime;
      // Simply truncate the hardware trigger time to 32-bit.
      notifierAlarm->writeTriggerTime(static_cast<uint32_t>(closestTrigger),
                                      status);
      // Enable the alarm.
      if (!wasActive) notifierAlarm->writeEnable(true, status);
    }
  }
  
  void HAL_CancelNotifierAlarm(HAL_NotifierHandle notifierHandle,
                               int32_t* status) {
    auto notifier = notifierHandles->Get(notifierHandle);
    if (!notifier) return;
  
    {
      std::scoped_lock lock(notifier->mutex);
      notifier->triggerTime = UINT64_MAX;
    }
  }
  
  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);
    notifier->cond.wait(lock, [&] {
      return !notifier->active || notifier->triggeredTime != UINT64_MAX;
    });
    return notifier->active ? notifier->triggeredTime : 0;
  }
  
  }  // extern "C"
  