-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSharedStack.hpp
More file actions
213 lines (186 loc) · 6.91 KB
/
Copy pathSharedStack.hpp
File metadata and controls
213 lines (186 loc) · 6.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#pragma once
#include "IWaitingStack.hpp"
#include <stack>
#include <mutex>
#include <condition_variable>
#include <memory>
#include <chrono>
#include <stdexcept>
/**
* @brief A thread-safe LIFO stack implementation using std::shared_ptr for internal storage.
* * This class implements the IWaitingStack interface. By storing elements as std::shared_ptr,
* the stack minimizes expensive copy operations and provides strong exception safety
* guarantees. It is ideal for handling large objects or non-trivial types in
* highly concurrent environments.
* * Key Features:
* - Thread-safe synchronization via std::mutex.
* - Efficient resource management using shared ownership.
* - Support for blocking and timed waiting operations via std::condition_variable.
* * @tparam ElementType The type of elements managed by the stack.
*/
template<typename ElementType>
class SharedStack : public IWaitingStack<ElementType> {
private:
/// Underlying container storing shared pointers to elements.
std::stack<std::shared_ptr<ElementType>> internalStack_;
/// Mutex used to synchronize access to the internal stack.
mutable std::mutex stackMutex_;
/// Condition variable to notify waiting threads when new data arrives.
std::condition_variable dataCondition_;
public:
/**
* @brief Default constructor.
*/
SharedStack() = default;
/**
* @brief Thread-safe copy constructor.
* Locks the source stack to ensure a consistent state during the copy operation.
* @param other The source stack instance to copy from.
*/
SharedStack(const SharedStack& other) {
std::lock_guard<std::mutex> lock(other.stackMutex_);
internalStack_ = other.internalStack_;
}
/**
* @brief Copy assignment operator is disabled to prevent unsafe concurrent assignment.
*/
SharedStack& operator=(const SharedStack&) = delete;
/**
* @brief Thread-safe move constructor.
* @param other The source stack instance to move from.
*/
SharedStack(SharedStack&& other) noexcept {
std::lock_guard<std::mutex> lock(other.stackMutex_);
internalStack_ = std::move(other.internalStack_);
}
/**
* @brief Virtual destructor.
*/
~SharedStack() override = default;
// --- IStack Implementation ---
/**
* @brief Pushes a copy of the value onto the stack.
* Wraps the value in a shared_ptr before acquiring the lock to minimize the critical section.
* @param value The value to be copied and pushed.
*/
void push(const ElementType& value) override {
auto data = std::make_shared<ElementType>(value);
{
std::lock_guard<std::mutex> lock(stackMutex_);
internalStack_.push(data);
}
dataCondition_.notify_one();
}
/**
* @brief Pushes a value onto the stack using move semantics.
* Memory allocation for the shared_ptr occurs outside the lock to reduce contention.
* @param value The rvalue to be moved and pushed.
*/
void push(ElementType&& value) override {
auto data = std::make_shared<ElementType>(std::move(value));
{
std::lock_guard<std::mutex> lock(stackMutex_);
internalStack_.push(data);
}
dataCondition_.notify_one();
}
/**
* @brief Removes the top element and returns it.
* @return Shared pointer to the popped element.
* @throws std::runtime_error If the stack is empty.
*/
std::shared_ptr<ElementType> pop() override {
std::lock_guard<std::mutex> lock(stackMutex_);
if (internalStack_.empty()) {
throw std::runtime_error("SharedStack::pop(): stack is empty");
}
auto result = internalStack_.top();
internalStack_.pop();
return result;
}
/**
* @brief Attempts to pop the top element without blocking.
* @param value Reference to store the popped value if successful.
* @return true if an element was popped, false if the stack was empty.
*/
bool tryPop(ElementType& value) override {
std::lock_guard<std::mutex> lock(stackMutex_);
if (internalStack_.empty()) return false;
value = std::move(*internalStack_.top());
internalStack_.pop();
return true;
}
/**
* @brief Attempts to pop the top element and return a shared pointer.
* @return Shared pointer to the element, or nullptr if empty.
*/
std::shared_ptr<ElementType> tryPop() override {
std::lock_guard<std::mutex> lock(stackMutex_);
if (internalStack_.empty()) return nullptr;
auto result = internalStack_.top();
internalStack_.pop();
return result;
}
/**
* @brief Checks if the stack is currently empty.
* @return true if empty, false otherwise.
*/
bool isEmpty() const override {
std::lock_guard<std::mutex> lock(stackMutex_);
return internalStack_.empty();
}
/**
* @brief Returns the number of elements in the stack.
* @return Current stack size.
*/
size_t getSize() const override {
std::lock_guard<std::mutex> lock(stackMutex_);
return internalStack_.size();
}
/**
* @brief Removes all elements from the stack.
*/
void clear() override {
std::lock_guard<std::mutex> lock(stackMutex_);
while (!internalStack_.empty()) {
internalStack_.pop();
}
}
// --- IWaitingStack Implementation ---
/**
* @brief Blocks until an element is available and pops it.
* @param value Reference to receive the popped value.
*/
void waitAndPop(ElementType& value) override {
std::unique_lock<std::mutex> lock(stackMutex_);
dataCondition_.wait(lock, [this] { return !internalStack_.empty(); });
value = std::move(*internalStack_.top());
internalStack_.pop();
}
/**
* @brief Blocks until an element is available and returns it as a shared_ptr.
* @return Shared pointer to the popped element.
*/
std::shared_ptr<ElementType> waitAndPop() override {
std::unique_lock<std::mutex> lock(stackMutex_);
dataCondition_.wait(lock, [this] { return !internalStack_.empty(); });
auto result = internalStack_.top();
internalStack_.pop();
return result;
}
/**
* @brief Blocks for a limited time until an element is available.
* @param value Reference to receive the popped value.
* @param timeout Maximum duration to wait.
* @return true if an element was popped before timeout, false otherwise.
*/
bool waitAndPopFor(ElementType& value, const std::chrono::milliseconds& timeout) override {
std::unique_lock<std::mutex> lock(stackMutex_);
if (!dataCondition_.wait_for(lock, timeout, [this] { return !internalStack_.empty(); })) {
return false;
}
value = std::move(*internalStack_.top());
internalStack_.pop();
return true;
}
};