// // Created by versustune on 29.02.20. // #ifndef ELIYA_THREADSAFEDEQUE_H #define ELIYA_THREADSAFEDEQUE_H #include #include #include #include #include #include template struct Tsqueue { /* Create Tsqueue object. Set maximum size of the queue to max_size. */ inline explicit Tsqueue(size_t max_size = -1UL) : maxsize(max_size), end(false) {}; /* Push T to the queue. Many threads can push at the same time. * If the queue is full, calling thread will be suspended until * some other thread pop() data. */ void push(const T &); void push(T &&); /* Close the queue. * Be sure all writing threads done their writes before call this. * Push data to closed queue is forbidden. */ void close(); /* Pop and return T from the queue. Many threads can pop at the same time. * If the queue is empty, calling thread will be suspended. * If the queue is empty and closed, nullopt returned. */ std::optional pop(); std::optional waitAndPop(); int getSize() { return que.size(); } private: std::queue que; std::mutex mtx; std::condition_variable cv_empty, cv_full; const size_t maxsize; std::atomic end; }; template void Tsqueue::push(T &&t) { std::unique_lock lck(mtx); while (que.size() == maxsize && !end) cv_full.wait(lck); assert(!end); que.push(std::move(t)); cv_empty.notify_one(); } template void Tsqueue::push(const T &t) { std::unique_lock lck(mtx); while (que.size() == maxsize && !end) cv_full.wait(lck); assert(!end); que.push(std::move(t)); cv_empty.notify_one(); } template std::optional Tsqueue::pop() { std::unique_lock lck(mtx); if (que.empty()) return {}; T t = std::move(que.front()); que.pop(); cv_full.notify_one(); return t; } template std::optional Tsqueue::waitAndPop() { std::unique_lock lck(mtx); while (que.empty() && !end) cv_empty.wait(lck); if (que.empty()) return {}; T t = std::move(que.front()); que.pop(); cv_full.notify_one(); return t; } template void Tsqueue::close() { end = true; std::lock_guard lck(mtx); cv_empty.notify_one(); cv_full.notify_one(); } #endif //ELIYA_THREADSAFEDEQUE_H