Skip to content

Instantly share code, notes, and snippets.

@deepanshululla
Last active April 27, 2022 09:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save deepanshululla/9a94c26e8acd73c19fc3ea1a01c6595d to your computer and use it in GitHub Desktop.
Save deepanshululla/9a94c26e8acd73c19fc3ea1a01c6595d to your computer and use it in GitHub Desktop.
thread_safe_queue.cpp
#include <exception>
#include <memory>
#include <mutex>
#include <queue>
struct EmptyQueueException : std::exception {
const char * what() const throw() {
return "Empty queue";
}
};
template <class T>
class ThreadSafeQueue {
private:
std::queue<T> d_queue;
mutable std::mutex d_mutex;
public:
ThreadSafeQueue(){};
ThreadSafeQueue(const ThreadSafeQueue& other) {
std::lock_guard<std::mutex> lock(other.d_mutex);
d_queue = other.d_queue; // copy performed in constructor body
// the reason behind it is mutex can't be locked in initializer list
}
// although we are allowing for copy constructor, but copy assignment is deleted
ThreadSafeQueue& operator=(const ThreadSafeQueue& other)=delete;
void push(T new_val) {
std::lock_guard lock(d_mutex);
d_queue.push(std::move(new_val));
}
std::shared_ptr<T> pop() {
/*
* The advantage here is that pointers can be freely copied without throwing an exception,
* so you’ve avoided Cargill’s exception problem. The disadvantage is that returning
* a pointer requires a means of managing the memory allocated to the object,
* and for simple types such as integers, the overhead of such memory management
* can exceed the cost of returning the type by value. For any interface
* that uses this option, std::shared_ptr would be a good choice of pointer type;
* not only does it avoid memory leaks, because the object is destroyed once
* the last pointer is destroyed, but the library is in full control of the
* memory allocation scheme and doesn’t have to use new and delete.
* This can be important for optimization purposes: requiring that
* each object in the stack be allocated separately with new would
* impose quite an overhead compared to the original non-thread-safe version.
*/
std::lock_guard<std::mutex> lock(d_mutex);
if (d_queue.empty()) {
throw EmptyQueueException();
}
std::shared_ptr<T> const res(std::make_shared<T>(d_queue.front()));
d_queue.pop();
return res;
}
void pop(T& value) {
std::lock_guard lock(d_mutex);
if (d_queue.empty()) {
throw EmptyQueueException();
}
value = d_queue.front();
d_queue.pop();
}
bool empty() {
std::lock_guard lock(d_mutex);
return d_queue.empty();
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment