mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-03 03:01:44 +00:00
Moves Notifier over to handles (#97)
Internally, the linked list now uses shared_ptrs instead of raw pointers. In addition, in the WPILib the notifier handle is now made atomic. Then before the class is destructed, the handle is now set to 0. This should help solve one of the existing race conditions. A 0 handle is correctly handled down at the HAL level.
This commit is contained in:
committed by
Peter Johnson
parent
8527f2c2a1
commit
776a991d61
@@ -10,11 +10,13 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "ChipObject.h"
|
||||
#include "HAL/HAL.h"
|
||||
#include "HAL/cpp/priority_mutex.h"
|
||||
#include "handles/UnlimitedHandleResource.h"
|
||||
|
||||
static const uint32_t kTimerInterruptNumber = 28;
|
||||
|
||||
@@ -23,143 +25,33 @@ static priority_recursive_mutex notifierMutex;
|
||||
static tAlarm* notifierAlarm = nullptr;
|
||||
static tInterruptManager* notifierManager = nullptr;
|
||||
static uint64_t closestTrigger = UINT64_MAX;
|
||||
|
||||
namespace {
|
||||
struct Notifier {
|
||||
Notifier *prev, *next, *freeNext;
|
||||
bool freed;
|
||||
std::shared_ptr<Notifier> prev, next;
|
||||
void* param;
|
||||
void (*process)(uint64_t, void*);
|
||||
uint64_t triggerTime = UINT64_MAX;
|
||||
};
|
||||
static Notifier* notifiers = nullptr;
|
||||
static Notifier* notifiersFreeList = nullptr;
|
||||
}
|
||||
static std::shared_ptr<Notifier> notifiers = nullptr;
|
||||
static std::atomic_flag notifierAtexitRegistered = ATOMIC_FLAG_INIT;
|
||||
static std::atomic_int notifierRefCount{0};
|
||||
|
||||
static void alarmCallback(uint32_t, void*) {
|
||||
std::unique_lock<priority_recursive_mutex> sync(notifierMutex);
|
||||
using namespace hal;
|
||||
|
||||
int32_t status = 0;
|
||||
uint64_t currentTime = 0;
|
||||
static UnlimitedHandleResource<HalNotifierHandle, Notifier,
|
||||
HalHandleEnum::Notifier>
|
||||
notifierHandles;
|
||||
|
||||
// the hardware disables itself after each alarm
|
||||
closestTrigger = UINT64_MAX;
|
||||
|
||||
// process all notifiers
|
||||
Notifier* notifier = notifiers;
|
||||
while (notifier) {
|
||||
if (!notifier->freed && notifier->triggerTime != UINT64_MAX) {
|
||||
if (currentTime == 0) currentTime = getFPGATime(&status);
|
||||
if (notifier->triggerTime < currentTime) {
|
||||
notifier->triggerTime = UINT64_MAX;
|
||||
auto process = notifier->process;
|
||||
auto param = notifier->param;
|
||||
sync.unlock();
|
||||
process(currentTime, param);
|
||||
sync.lock();
|
||||
} else if (notifier->triggerTime < closestTrigger) {
|
||||
updateNotifierAlarm(notifier, notifier->triggerTime, &status);
|
||||
}
|
||||
}
|
||||
notifier = notifier->next;
|
||||
}
|
||||
|
||||
// clean up freelist
|
||||
notifier = notifiersFreeList;
|
||||
while (notifier) {
|
||||
Notifier* next = notifier->freeNext;
|
||||
delete notifier;
|
||||
notifier = next;
|
||||
}
|
||||
notifiersFreeList = nullptr;
|
||||
}
|
||||
|
||||
static void cleanupNotifierAtExit() {
|
||||
notifierAlarm = nullptr;
|
||||
notifierManager = nullptr;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
void* initializeNotifier(void (*process)(uint64_t, void*), void* param,
|
||||
int32_t* status) {
|
||||
if (!process) {
|
||||
*status = NULL_PARAMETER;
|
||||
return nullptr;
|
||||
}
|
||||
if (!notifierAtexitRegistered.test_and_set())
|
||||
std::atexit(cleanupNotifierAtExit);
|
||||
if (notifierRefCount.fetch_add(1) == 0) {
|
||||
std::lock_guard<priority_mutex> sync(notifierInterruptMutex);
|
||||
// create manager and alarm if not already created
|
||||
if (!notifierManager) {
|
||||
notifierManager =
|
||||
new tInterruptManager(1 << kTimerInterruptNumber, false, status);
|
||||
notifierManager->registerHandler(alarmCallback, nullptr, status);
|
||||
notifierManager->enable(status);
|
||||
}
|
||||
if (!notifierAlarm) notifierAlarm = tAlarm::create(status);
|
||||
}
|
||||
|
||||
std::lock_guard<priority_recursive_mutex> sync(notifierMutex);
|
||||
// create notifier structure and add to list
|
||||
Notifier* notifier = new Notifier();
|
||||
notifier->prev = nullptr;
|
||||
notifier->next = notifiers;
|
||||
notifier->freeNext = nullptr;
|
||||
if (notifier->next) notifier->next->prev = notifier;
|
||||
notifier->freed = false;
|
||||
notifier->param = param;
|
||||
notifier->process = process;
|
||||
notifiers = notifier;
|
||||
return notifier;
|
||||
}
|
||||
|
||||
void cleanNotifier(void* notifier_pointer, int32_t* status) {
|
||||
{
|
||||
std::lock_guard<priority_recursive_mutex> sync(notifierMutex);
|
||||
Notifier* notifier = (Notifier*)notifier_pointer;
|
||||
|
||||
// ignore double-free
|
||||
if (notifier->freed) return;
|
||||
notifier->freed = true;
|
||||
|
||||
// remove from list
|
||||
if (notifier->prev) notifier->prev->next = notifier->next;
|
||||
if (notifier->next) notifier->next->prev = notifier->prev;
|
||||
if (notifiers == notifier) notifiers = notifier->next;
|
||||
|
||||
// add to freelist
|
||||
notifier->freeNext = notifiersFreeList;
|
||||
notifiersFreeList = notifier;
|
||||
}
|
||||
|
||||
if (notifierRefCount.fetch_sub(1) == 1) {
|
||||
std::lock_guard<priority_mutex> sync(notifierInterruptMutex);
|
||||
// if this was the last notifier, clean up alarm and manager
|
||||
if (notifierAlarm) {
|
||||
notifierAlarm->writeEnable(false, status);
|
||||
delete notifierAlarm;
|
||||
notifierAlarm = nullptr;
|
||||
}
|
||||
if (notifierManager) {
|
||||
notifierManager->disable(status);
|
||||
delete notifierManager;
|
||||
notifierManager = nullptr;
|
||||
}
|
||||
closestTrigger = UINT64_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
void* getNotifierParam(void* notifier_pointer, int32_t* status) {
|
||||
return ((Notifier*)notifier_pointer)->param;
|
||||
}
|
||||
|
||||
void updateNotifierAlarm(void* notifier_pointer, uint64_t triggerTime,
|
||||
int32_t* status) {
|
||||
// internal version of updateAlarm used during the alarmCallback when we know
|
||||
// that the pointer is a valid pointer.
|
||||
void updateNotifierAlarmInternal(std::shared_ptr<Notifier> notifier_pointer,
|
||||
uint64_t triggerTime, int32_t* status) {
|
||||
std::lock_guard<priority_recursive_mutex> sync(notifierMutex);
|
||||
|
||||
Notifier* notifier = (Notifier*)notifier_pointer;
|
||||
if (notifier->freed) return;
|
||||
auto notifier = notifier_pointer;
|
||||
// no need for a null check, as this must always be a valid pointer.
|
||||
notifier->triggerTime = triggerTime;
|
||||
bool wasActive = (closestTrigger != UINT64_MAX);
|
||||
|
||||
@@ -179,9 +71,122 @@ void updateNotifierAlarm(void* notifier_pointer, uint64_t triggerTime,
|
||||
notifierInterruptMutex.unlock();
|
||||
}
|
||||
|
||||
void stopNotifierAlarm(void* notifier_pointer, int32_t* status) {
|
||||
static void alarmCallback(uint32_t, void*) {
|
||||
std::unique_lock<priority_recursive_mutex> sync(notifierMutex);
|
||||
|
||||
int32_t status = 0;
|
||||
uint64_t currentTime = 0;
|
||||
|
||||
// the hardware disables itself after each alarm
|
||||
closestTrigger = UINT64_MAX;
|
||||
|
||||
// process all notifiers
|
||||
std::shared_ptr<Notifier> notifier = notifiers;
|
||||
while (notifier) {
|
||||
if (notifier->triggerTime != UINT64_MAX) {
|
||||
if (currentTime == 0) currentTime = getFPGATime(&status);
|
||||
if (notifier->triggerTime < currentTime) {
|
||||
notifier->triggerTime = UINT64_MAX;
|
||||
auto process = notifier->process;
|
||||
auto param = notifier->param;
|
||||
sync.unlock();
|
||||
process(currentTime, param);
|
||||
sync.lock();
|
||||
} else if (notifier->triggerTime < closestTrigger) {
|
||||
updateNotifierAlarmInternal(notifier, notifier->triggerTime, &status);
|
||||
}
|
||||
}
|
||||
notifier = notifier->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void cleanupNotifierAtExit() {
|
||||
notifierAlarm = nullptr;
|
||||
notifierManager = nullptr;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
HalNotifierHandle initializeNotifier(void (*process)(uint64_t, void*),
|
||||
void* param, int32_t* status) {
|
||||
if (!process) {
|
||||
*status = NULL_PARAMETER;
|
||||
return 0;
|
||||
}
|
||||
if (!notifierAtexitRegistered.test_and_set())
|
||||
std::atexit(cleanupNotifierAtExit);
|
||||
if (notifierRefCount.fetch_add(1) == 0) {
|
||||
std::lock_guard<priority_mutex> sync(notifierInterruptMutex);
|
||||
// create manager and alarm if not already created
|
||||
if (!notifierManager) {
|
||||
notifierManager =
|
||||
new tInterruptManager(1 << kTimerInterruptNumber, false, status);
|
||||
notifierManager->registerHandler(alarmCallback, nullptr, status);
|
||||
notifierManager->enable(status);
|
||||
}
|
||||
if (!notifierAlarm) notifierAlarm = tAlarm::create(status);
|
||||
}
|
||||
|
||||
std::lock_guard<priority_recursive_mutex> sync(notifierMutex);
|
||||
Notifier* notifier = (Notifier*)notifier_pointer;
|
||||
// create notifier structure and add to list
|
||||
std::shared_ptr<Notifier> notifier = std::make_shared<Notifier>();
|
||||
notifier->next = notifiers;
|
||||
if (notifier->next) notifier->next->prev = notifier;
|
||||
notifier->param = param;
|
||||
notifier->process = process;
|
||||
notifiers = notifier;
|
||||
return notifierHandles.Allocate(notifier);
|
||||
}
|
||||
|
||||
void cleanNotifier(HalNotifierHandle notifier_handle, int32_t* status) {
|
||||
{
|
||||
std::lock_guard<priority_recursive_mutex> sync(notifierMutex);
|
||||
auto notifier = notifierHandles.Get(notifier_handle);
|
||||
if (!notifier) return;
|
||||
|
||||
// remove from list
|
||||
if (notifier->prev) notifier->prev->next = notifier->next;
|
||||
if (notifier->next) notifier->next->prev = notifier->prev;
|
||||
if (notifiers == notifier) notifiers = notifier->next;
|
||||
notifierHandles.Free(notifier_handle);
|
||||
}
|
||||
|
||||
if (notifierRefCount.fetch_sub(1) == 1) {
|
||||
std::lock_guard<priority_mutex> sync(notifierInterruptMutex);
|
||||
// if this was the last notifier, clean up alarm and manager
|
||||
if (notifierAlarm) {
|
||||
notifierAlarm->writeEnable(false, status);
|
||||
delete notifierAlarm;
|
||||
notifierAlarm = nullptr;
|
||||
}
|
||||
if (notifierManager) {
|
||||
notifierManager->disable(status);
|
||||
delete notifierManager;
|
||||
notifierManager = nullptr;
|
||||
}
|
||||
closestTrigger = UINT64_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
void* getNotifierParam(HalNotifierHandle notifier_handle, int32_t* status) {
|
||||
auto notifier = notifierHandles.Get(notifier_handle);
|
||||
if (!notifier) return nullptr;
|
||||
return notifier->param;
|
||||
}
|
||||
|
||||
void updateNotifierAlarm(HalNotifierHandle notifier_handle,
|
||||
uint64_t triggerTime, int32_t* status) {
|
||||
std::lock_guard<priority_recursive_mutex> sync(notifierMutex);
|
||||
|
||||
auto notifier = notifierHandles.Get(notifier_handle);
|
||||
if (!notifier) return;
|
||||
updateNotifierAlarmInternal(notifier, triggerTime, status);
|
||||
}
|
||||
|
||||
void stopNotifierAlarm(HalNotifierHandle notifier_handle, int32_t* status) {
|
||||
std::lock_guard<priority_recursive_mutex> sync(notifierMutex);
|
||||
auto notifier = notifierHandles.Get(notifier_handle);
|
||||
if (!notifier) return;
|
||||
notifier->triggerTime = UINT64_MAX;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user