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:
Thad House
2016-06-05 07:29:47 -07:00
committed by Peter Johnson
parent 8527f2c2a1
commit 776a991d61
13 changed files with 197 additions and 176 deletions

View File

@@ -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;
}