Last active
April 27, 2022 09:42
-
-
Save deepanshululla/9a94c26e8acd73c19fc3ea1a01c6595d to your computer and use it in GitHub Desktop.
thread_safe_queue.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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