#pragma once #include #include // Single item mailbox. template struct Mailbox { Mailbox() : refcount_(1) {} std::mutex mutex_; std::condition_variable condvar_; T *data_ = nullptr; T *Wait() { T *data; { std::unique_lock lock(mutex_); while (!data_) { condvar_.wait(lock); } data = data_; } return data; } bool Poll(T **data) { bool retval = false; { std::unique_lock lock(mutex_); if (data_) { *data = data_; retval = true; } } return retval; } bool Send(T *data) { bool success = false; { std::unique_lock lock(mutex_); if (!data_) { data_ = data; success = true; } condvar_.notify_one(); } return success; } void AddRef() { refcount_.fetch_add(1); } void Release() { int count = refcount_.fetch_sub(1); if (count == 1) { // was definitely decreased to 0 delete this; } } private: std::atomic refcount_; }; // POD so can be moved around freely. // TODO: I can't quite get this to work with all the moves, the refcount blows up. I'll just use mailboxes directly. /* template class Rx { public: Rx() : mbx_(nullptr) {} Rx(Mailbox *mbx) : mbx_(mbx) { mbx_->AddRef(); } Rx(Rx &&rx) { mbx_ = rx.mbx_; } Rx& operator=(Rx &&rx) { mbx_ = rx.mbx_; return *this; } ~Rx() { mbx_->Release(); } bool Poll(T **data) { return mbx_->Poll(data); } T *Wait() { return mbx_->Wait(); } private: Mailbox *mbx_; }; // POD so can be moved around freely. template class Tx { public: Tx() : mbx_(nullptr) {} Tx(Mailbox *mbx) : mbx_(mbx) { mbx_->AddRef(); } Tx(Tx &&rx) { mbx_ = rx.mbx_; } Tx& operator=(Tx &&rx) { mbx_ = rx.mbx_; return *this; } ~Tx() { mbx_->Release(); } bool Send(T *t) { return mbx_->Send(t); } private: Mailbox *mbx_; }; template std::pair, Tx> CreateChannel() { Mailbox *mbx = new Mailbox(); auto retval = std::make_pair(Rx(mbx), Tx(mbx)); mbx->Release(); return retval; } */