mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-24 01:31:46 +00:00
artf4156: Replaced synchronization primitives with C++11 equivalents
Change-Id: I90da739347e875efda2a29dd5484b6dda3cd4753
This commit is contained in:
committed by
James Kuszmaul
parent
7f5ee01d3e
commit
3f59f3472a
@@ -6,12 +6,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Errors.hpp"
|
||||
#include "Synchronized.hpp"
|
||||
#ifdef __vxworks
|
||||
#include <vxWorks.h>
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
#include "HAL/cpp/priority_mutex.h"
|
||||
|
||||
/**
|
||||
* The Resource class is a convenient way to track allocated resources.
|
||||
@@ -25,6 +25,8 @@
|
||||
class Resource
|
||||
{
|
||||
public:
|
||||
Resource(const Resource&) = delete;
|
||||
Resource& operator=(const Resource&) = delete;
|
||||
virtual ~Resource();
|
||||
static void CreateResourceObject(Resource **r, uint32_t elements);
|
||||
uint32_t Allocate(const char *resourceDesc);
|
||||
@@ -35,11 +37,9 @@ private:
|
||||
explicit Resource(uint32_t size);
|
||||
|
||||
bool *m_isAllocated;
|
||||
ReentrantSemaphore m_allocateLock;
|
||||
priority_recursive_mutex m_allocateLock;
|
||||
uint32_t m_size;
|
||||
|
||||
static ReentrantSemaphore m_createLock;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Resource);
|
||||
static priority_recursive_mutex m_createLock;
|
||||
};
|
||||
|
||||
|
||||
30
hal/include/HAL/cpp/Semaphore.hpp
Normal file
30
hal/include/HAL/cpp/Semaphore.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "HAL/cpp/priority_mutex.h"
|
||||
|
||||
class Semaphore {
|
||||
public:
|
||||
explicit Semaphore(uint32_t count = 0);
|
||||
Semaphore(Semaphore&&);
|
||||
Semaphore& operator=(Semaphore&&);
|
||||
|
||||
void give();
|
||||
void take();
|
||||
|
||||
// @return true if semaphore was locked successfully. false if not.
|
||||
bool tryTake();
|
||||
|
||||
static const int32_t kNoWait = 0;
|
||||
static const int32_t kWaitForever = -1;
|
||||
|
||||
static const uint32_t kEmpty = 0;
|
||||
static const uint32_t kFull = 1;
|
||||
|
||||
private:
|
||||
priority_mutex m_mutex;
|
||||
std::condition_variable_any m_condition;
|
||||
uint32_t m_count = 0;
|
||||
};
|
||||
@@ -1,105 +0,0 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008. 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 $(WIND_BASE)/WPILib. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
#pragma once
|
||||
|
||||
#include "HAL/Semaphore.hpp"
|
||||
|
||||
// A macro for making a class move-only
|
||||
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&) = delete; \
|
||||
TypeName& operator=(const TypeName&) = delete; \
|
||||
TypeName(TypeName&&) = default; \
|
||||
TypeName& operator=(TypeName&&) = default
|
||||
|
||||
#define CRITICAL_REGION(s) { Synchronized _sync(s);
|
||||
#define END_REGION }
|
||||
// TODO: is this Better?
|
||||
#define SYNCHRONIZE(s) for (Synchronized _sync(s), int _i=0;i < 1; i++)
|
||||
|
||||
class Synchronized;
|
||||
|
||||
/**
|
||||
* Wrap a vxWorks semaphore (SEM_ID) for easier use in C++. For a static
|
||||
* instance, the constructor runs at program load time before main() can spawn
|
||||
* any tasks. Use that to fix race conditions in setup code.
|
||||
*
|
||||
* This uses a semM semaphore which is "reentrant" in the sense that the owning
|
||||
* task can "take" the semaphore more than once. It will need to "give" the
|
||||
* semaphore the same number of times to unlock it.
|
||||
*
|
||||
* This class is safe to use in static variables because it does not depend on
|
||||
* any other C++ static constructors or destructors.
|
||||
*/
|
||||
class ReentrantSemaphore
|
||||
{
|
||||
public:
|
||||
explicit ReentrantSemaphore()
|
||||
{
|
||||
m_semaphore = initializeMutexRecursive();
|
||||
}
|
||||
~ReentrantSemaphore()
|
||||
{
|
||||
deleteMutex(m_semaphore);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock the semaphore, blocking until it's available.
|
||||
* @return 0 for success, -1 for error. If -1, the error will be in errno.
|
||||
*/
|
||||
int take()
|
||||
{
|
||||
return takeMutex(m_semaphore);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock the semaphore.
|
||||
* @return 0 for success, -1 for error. If -1, the error will be in errno.
|
||||
*/
|
||||
int give()
|
||||
{
|
||||
return giveMutex(m_semaphore);
|
||||
}
|
||||
|
||||
private:
|
||||
MUTEX_ID m_semaphore;
|
||||
|
||||
friend class Synchronized;DISALLOW_COPY_AND_ASSIGN(ReentrantSemaphore);
|
||||
};
|
||||
|
||||
/**
|
||||
* Provide easy support for critical regions.
|
||||
*
|
||||
* A critical region is an area of code that is always executed under mutual exclusion. Only
|
||||
* one task can be executing this code at any time. The idea is that code that manipulates data
|
||||
* that is shared between two or more tasks has to be prevented from executing at the same time
|
||||
* otherwise a race condition is possible when both tasks try to update the data. Typically
|
||||
* semaphores are used to ensure only single task access to the data.
|
||||
*
|
||||
* Synchronized objects are a simple wrapper around semaphores to help ensure
|
||||
* that semaphores are always unlocked (semGive) after locking (semTake).
|
||||
*
|
||||
* You allocate a Synchronized as a local variable, *not* on the heap. That
|
||||
* makes it a "stack object" whose destructor runs automatically when it goes
|
||||
* out of scope. E.g.
|
||||
*
|
||||
* { Synchronized _sync(aReentrantSemaphore); ... critical region ... }
|
||||
*/
|
||||
class Synchronized
|
||||
{
|
||||
public:
|
||||
explicit Synchronized(MUTEX_ID);
|
||||
#ifndef __vxworks
|
||||
explicit Synchronized(SEMAPHORE_ID);
|
||||
#endif
|
||||
explicit Synchronized(ReentrantSemaphore&);
|
||||
virtual ~Synchronized();
|
||||
private:
|
||||
MUTEX_ID m_mutex;
|
||||
SEMAPHORE_ID m_semaphore;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Synchronized);
|
||||
};
|
||||
|
||||
116
hal/include/HAL/cpp/priority_condition_variable.h
Normal file
116
hal/include/HAL/cpp/priority_condition_variable.h
Normal file
@@ -0,0 +1,116 @@
|
||||
#pragma once
|
||||
|
||||
/* std::condition_variable provides the native_handle() method to return its
|
||||
* underlying pthread_cond_t*. WPILib uses this to interface with the FRC
|
||||
* network communication library. Since WPILib uses a custom mutex class and
|
||||
* not std::mutex, std::condition_variable_any must be used instead.
|
||||
* std::condition_variable_any doesn't expose its internal handle, so this
|
||||
* class provides the same interface and implementation in addition to a
|
||||
* native_handle() method.
|
||||
*/
|
||||
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include "priority_mutex.h"
|
||||
|
||||
class priority_condition_variable {
|
||||
typedef pthread_cond_t* native_handle_type;
|
||||
typedef std::chrono::system_clock clock_t;
|
||||
|
||||
public:
|
||||
priority_condition_variable() : m_mutex(std::make_shared<std::mutex>()) {}
|
||||
~priority_condition_variable() = default;
|
||||
|
||||
priority_condition_variable(const priority_condition_variable&) = delete;
|
||||
priority_condition_variable& operator=(const priority_condition_variable&) = delete;
|
||||
|
||||
void notify_one() noexcept {
|
||||
std::lock_guard<std::mutex> lock(*m_mutex);
|
||||
m_cond.notify_one();
|
||||
}
|
||||
|
||||
void notify_all() noexcept {
|
||||
std::lock_guard<std::mutex> lock(*m_mutex);
|
||||
m_cond.notify_all();
|
||||
}
|
||||
|
||||
template<typename Lock>
|
||||
void wait(Lock& _lock) {
|
||||
std::shared_ptr<std::mutex> _mutex = m_mutex;
|
||||
std::unique_lock<std::mutex> my_lock(*_mutex);
|
||||
Unlock<Lock> unlock(_lock);
|
||||
|
||||
// *mutex must be unlocked before re-locking _lock so move
|
||||
// ownership of *_mutex lock to an object with shorter lifetime.
|
||||
std::unique_lock<std::mutex> my_lock2(std::move(my_lock));
|
||||
m_cond.wait(my_lock2);
|
||||
}
|
||||
|
||||
template<typename Lock, typename Predicate>
|
||||
void wait(Lock& lock, Predicate p) {
|
||||
while (!p()) { wait(lock); }
|
||||
}
|
||||
|
||||
template<typename Lock, typename Clock, typename Duration>
|
||||
std::cv_status wait_until(Lock& _lock,
|
||||
const std::chrono::time_point<Clock, Duration>& atime) {
|
||||
std::shared_ptr<std::mutex> _mutex = m_mutex;
|
||||
std::unique_lock<std::mutex> my_lock(*_mutex);
|
||||
Unlock<Lock> unlock(_lock);
|
||||
|
||||
// *_mutex must be unlocked before re-locking _lock so move
|
||||
// ownership of *_mutex lock to an object with shorter lifetime.
|
||||
std::unique_lock<std::mutex> my_lock2(std::move(my_lock));
|
||||
return m_cond.wait_until(my_lock2, atime);
|
||||
}
|
||||
|
||||
template<typename Lock, typename Clock, typename Duration, typename Predicate>
|
||||
bool wait_until(Lock& lock,
|
||||
const std::chrono::time_point<Clock, Duration>& atime, Predicate p) {
|
||||
while (!p()) {
|
||||
if (wait_until(lock, atime) == std::cv_status::timeout) {
|
||||
return p();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Lock, typename Rep, typename Period>
|
||||
std::cv_status wait_for(Lock& lock, const std::chrono::duration<Rep, Period>& rtime) {
|
||||
return wait_until(lock, clock_t::now() + rtime);
|
||||
}
|
||||
|
||||
template<typename Lock, typename Rep, typename Period, typename Predicate>
|
||||
bool wait_for(Lock& lock, const std::chrono::duration<Rep, Period>& rtime,
|
||||
Predicate p) {
|
||||
return wait_until(lock, clock_t::now() + rtime, std::move(p));
|
||||
}
|
||||
|
||||
native_handle_type native_handle() {
|
||||
return m_cond.native_handle();
|
||||
}
|
||||
|
||||
private:
|
||||
std::condition_variable m_cond;
|
||||
std::shared_ptr<std::mutex> m_mutex;
|
||||
|
||||
// scoped unlock - unlocks in ctor, re-locks in dtor
|
||||
template<typename Lock>
|
||||
struct Unlock {
|
||||
explicit Unlock(Lock& lk) : m_lock(lk) { lk.unlock(); }
|
||||
|
||||
~Unlock() noexcept(false) {
|
||||
if (std::uncaught_exception()) {
|
||||
try { m_lock.lock(); } catch(...) {}
|
||||
}
|
||||
else {
|
||||
m_lock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
Unlock(const Unlock&) = delete;
|
||||
Unlock& operator=(const Unlock&) = delete;
|
||||
|
||||
Lock& m_lock;
|
||||
};
|
||||
};
|
||||
67
hal/include/HAL/cpp/priority_mutex.h
Normal file
67
hal/include/HAL/cpp/priority_mutex.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
// Allows usage with std::unique_lock without including <mutex> separately
|
||||
#include <mutex>
|
||||
|
||||
class priority_recursive_mutex {
|
||||
public:
|
||||
typedef pthread_mutex_t *native_handle_type;
|
||||
|
||||
constexpr priority_recursive_mutex() noexcept = default;
|
||||
priority_recursive_mutex(const priority_recursive_mutex &) = delete;
|
||||
priority_recursive_mutex &operator=(const priority_recursive_mutex &) = delete;
|
||||
|
||||
// Lock the mutex, blocking until it's available.
|
||||
void lock();
|
||||
|
||||
// Unlock the mutex.
|
||||
void unlock();
|
||||
|
||||
// Tries to lock the mutex.
|
||||
bool try_lock() noexcept;
|
||||
|
||||
pthread_mutex_t *native_handle();
|
||||
|
||||
private:
|
||||
// Do the equivalent of setting PTHREAD_PRIO_INHERIT and
|
||||
// PTHREAD_MUTEX_RECURSIVE_NP.
|
||||
#if __WORDSIZE == 64
|
||||
pthread_mutex_t m_mutex = {
|
||||
{0, 0, 0, 0, 0x20 | PTHREAD_MUTEX_RECURSIVE_NP, 0, 0, {0, 0}}};
|
||||
#else
|
||||
pthread_mutex_t m_mutex = {
|
||||
{0, 0, 0, 0x20 | PTHREAD_MUTEX_RECURSIVE_NP, 0, {0}}};
|
||||
#endif
|
||||
};
|
||||
|
||||
class priority_mutex {
|
||||
public:
|
||||
typedef pthread_mutex_t *native_handle_type;
|
||||
|
||||
constexpr priority_mutex() noexcept = default;
|
||||
priority_mutex(const priority_mutex &) = delete;
|
||||
priority_mutex &operator=(const priority_mutex &) = delete;
|
||||
|
||||
// Lock the mutex, blocking until it's available.
|
||||
void lock();
|
||||
|
||||
// Unlock the mutex.
|
||||
void unlock();
|
||||
|
||||
// Tries to lock the mutex.
|
||||
bool try_lock() noexcept;
|
||||
|
||||
pthread_mutex_t *native_handle();
|
||||
|
||||
private:
|
||||
// Do the equivalent of setting PTHREAD_PRIO_INHERIT.
|
||||
#if __WORDSIZE == 64
|
||||
pthread_mutex_t m_mutex = {
|
||||
{0, 0, 0, 0, 0x20, 0, 0, {0, 0}}};
|
||||
#else
|
||||
pthread_mutex_t m_mutex = {
|
||||
{0, 0, 0, 0x20, 0, {0}}};
|
||||
#endif
|
||||
};
|
||||
Reference in New Issue
Block a user