I have a somewhat unique and interesting (and terrible) scenario that is forcing me to do some tricky stuff. The problem is as follows:
- We need an atomic type to do lock-free synchronization between a realtime thread and a background loading thread.
-
The type (unfortunatley) must compile on:
a. QNX which only has c++03, but supports
boost::atomic
.b. Nucleus which has c++11 (but cannot build
boost::atomic
, and the fullstd::atomic
api is not available eg.atomic::compare_exchange_weak
won't work)
This is forcing me to consider using both boost::atomic
and std::atomic
. They way I am approaching this is to generate a new type, that forwards all of the functionality to the relevant atomic
type on the platform. The idea is something like this:
Atomic.hpp
namespace osal { namespace detail {
template <typename T, template <class> class TAtomic >
struct AtomicImpl {
void store(T desired, memory_order order = osal::memory_order_seq_cst) {
_atomic.store(desired, order);
}
// ... Other api
private:
AtomicImpl& operator=(const AtomicImpl& rhs);
// ... Other blocked operations
TAtomic<T> _atomic;
};
}}
#if QNX
#include <osal/QNX/Atomic.hpp>
#elif NUCLEUS
#include <osal/NUCLEUS/Atomic.hpp>
#endif
This lays out a class which will forward to the atomic
type with the correct API. Luckily the boost
and std
implementation match almost exactly.
Then in each of the individual OS files, something like this:
Nucleus/Atomic.hpp
#include <atomic>
namespace osal {
template <typename T> struct atomic
{
typedef detail::AtomicImpl<T, std::atomic> type;
};
}
This creates the atomic type with std::atomic
allowing use like:
osal::atomic<uint8_t>::type a;
a.store(1);
In order to deal with the concept of memory order, there exists a similar system with a file to delegate to each of the OS implementations using the preprocessor. In the implementation, for eg. Nucleus, we have this:
Nucleus/MemoryOrder.hpp
#include <memory>
namespace osal {
using std::memory_order;
using std::memory_order_relaxed;
using std::memory_order_consume;
using std::memory_order_acquire;
using std::memory_order_release;
using std::memory_order_acq_rel;
using std::memory_order_seq_cst;
}
Obviously the equivalent using boost::...
will be in the QNX file (lucky that they match again!).
The question
This appears to work. I can make an atomic and do operations on it. I have a concern:
Is this still atomic?
Now that we have introduced a indirection in the API, could there be problems with the order of the calls to the atomic object?
As a bonus question, given the constraints, is there a better way to do this?
Aucun commentaire:
Enregistrer un commentaire