Skip to content

Instantly share code, notes, and snippets.

@3noch
Created September 16, 2015 16:43
Show Gist options
  • Save 3noch/5848cb2ccf4850772c51 to your computer and use it in GitHub Desktop.
Save 3noch/5848cb2ccf4850772c51 to your computer and use it in GitHub Desktop.
#pragma once
#include <boost/thread.hpp>
#include <CeBaseTypes.h>
#include <Maybe.h>
/**
* A concurrency primitive which may contain a single value or be empty.
*
* An exclusive box can be empty or full:
* * "Putting" fills the box with a value.
* * "Taking" empties the box and returns it to the caller.
* * "Reading" returns a copy to the caller without emptying the box.
* * When full, "putters" will block until it's empty.
* * When empty, both "takers" and "readers" will block until it's full.
*
* If you need scoped, atomic access to data, use ce::Atomic instead.
*
* Modeled directly after Haskell's MVar (mutable variable):
* https://hackage.haskell.org/package/base/docs/Control-Concurrent-MVar.html
* Implementation based on:
* https://gist.github.com/qzchenwl/ec43b275f8ad27c5b629#file-mvar-hpp
* (mirrored at https://gist.github.com/3noch/a583e43ead2a8b008ddd#file-mvar-hpp)
*/
template<typename T>
struct ExclusiveBox final : private Noncopyable
{
ExclusiveBox() {}
ExclusiveBox(T t)
: slot_(std::move(t)) {}
/**
* Fills the slot, blocking until it's empty if necessary.
*/
void put(T t)
{
boost::unique_lock<boost::shared_mutex> lock(mtx_);
while (slot_.full()) { putCond_.wait(lock); }
slot_ = std::move(t);
readCond_.notify_all();
takeCond_.notify_one();
}
/**
* Empties and returns the contents of the slot, blocking until it's full if necessary.
*/
T take()
{
boost::unique_lock<boost::shared_mutex> lock(mtx_);
while (!slot_.full()) { takeCond_.wait(lock); }
T x = std::move(*slot_);
slot_ = Nothing();
putCond_.notify_one();
return std::move(x);
}
/**
* Returns a copy of the contents of the slot, blocking until it's full if necessary.
*/
T read() const
{
boost::shared_lock<boost::shared_mutex> lock(mtx_);
while (!slot_.full()) { readCond_.wait(lock); }
return *slot_;
}
private:
mutable boost::shared_mutex mtx_;
boost::condition_variable_any putCond_;
boost::condition_variable_any takeCond_;
mutable boost::condition_variable_any readCond_;
Maybe<T> slot_;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment