Skip to content

Instantly share code, notes, and snippets.

@dermesser
Created October 7, 2015 19:16
Show Gist options
  • Save dermesser/33133b15226d6c275f56 to your computer and use it in GitHub Desktop.
Save dermesser/33133b15226d6c275f56 to your computer and use it in GitHub Desktop.
Golang-like channels. Not *entirely* threadsafe yet, but almost (a.k.a. quick sketch)
# ifndef _CHAN_HPP
# define _CHAN_HPP
# include <deque>
# include <mutex>
# include <condition_variable>
# include <utility>
template<typename T>
class Chan {
public:
Chan();
Chan(unsigned int len);
void send(const T& o);
T& recv();
private:
unsigned int capacity;
std::deque<T> q;
std::mutex q_m, sndcv_mx, rcvcv_mx;
std::condition_variable sndable_cv, rcvable_cv;
};
template<typename T>
Chan<T>::Chan()
: Chan(0) {
}
template<typename T>
Chan<T>::Chan(unsigned int len)
: capacity(len) {
}
template<typename T>
void Chan<T>::send(const T& obj) {
q_m.lock();
bool was_empty = q.size() == 0;
q.push_back(obj);
q_m.unlock();
if ( was_empty ) {
rcvcv_mx.lock();
rcvable_cv.notify_one();
rcvcv_mx.unlock();
}
if ( q.size() > capacity ) {
std::unique_lock<std::mutex> ul(sndcv_mx);
sndable_cv.wait(ul, [this]{ return q.size() <= capacity; });
}
return;
}
template<typename T>
T& Chan<T>::recv() {
q_m.lock();
if ( q.size() > 0 ) {
T& o = q.front();
q.pop_front();
q_m.unlock();
if ( q.size()+1 > capacity ) {
sndcv_mx.lock();
sndable_cv.notify_one();
sndcv_mx.unlock();
}
return o;
} else {
q_m.unlock();
std::unique_lock<std::mutex> ul(rcvcv_mx);
rcvable_cv.wait(ul, [this]{ return q.size() > 0; });
q_m.lock();
T& o = q.front();
q.pop_front();
q_m.unlock();
if ( q.size()+1 > capacity ) {
sndcv_mx.lock();
sndable_cv.notify_one();
sndcv_mx.unlock();
}
return o;
}
}
# endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment