Skip to content

Commit dbd05f2

Browse files
committed
add locked members
1 parent 129921a commit dbd05f2

File tree

2 files changed

+99
-27
lines changed

2 files changed

+99
-27
lines changed

include/sieve.hpp

Lines changed: 94 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
#include <cstddef>
1313
#include <memory_resource>
14+
#include <mutex>
15+
#include <shared_mutex>
1416
#if __cplusplus >= 202002L
1517
#include <compare>
1618
#endif
@@ -24,7 +26,7 @@ class SieveCache {
2426
values_ = static_cast<V*>(mem_resource_->allocate(capacity_ * sizeof(V)));
2527
}
2628

27-
SieveCache(size_t capacity, std::pmr::memory_resource* mem_resource)
29+
explicit SieveCache(size_t capacity, std::pmr::memory_resource* mem_resource)
2830
: capacity_(capacity), size_(0), mem_resource_(mem_resource) {
2931
keys_ = static_cast<K*>(mem_resource_->allocate(capacity_ * sizeof(K)));
3032
values_ = static_cast<V*>(mem_resource_->allocate(capacity_ * sizeof(V)));
@@ -79,22 +81,24 @@ class SieveCache {
7981
return !(*this == other);
8082
}
8183

82-
V& operator[](const K& key) {
84+
inline V& operator[](const K& key) noexcept {
85+
std::shared_lock<std::shared_mutex> lock(rw_mutex_);
8386
for (size_t i = 0; i < size_; ++i) {
8487
if (keys_[i] == key) {
8588
return values_[i];
8689
}
8790
}
8891
if (size_ < capacity_) {
89-
new (&keys_[size_]) K(key);
90-
new (&values_[size_]) V();
92+
construct(&keys_[size_], key);
93+
construct(&values_[size_]);
9194
++size_;
9295
return values_[size_ - 1];
9396
}
94-
return values_[0]; // Simplified eviction policy
97+
// Handle the case where cache is full
98+
return values_[0];
9599
}
96100

97-
V* get(const K& key) {
101+
inline V* get(const K& key) noexcept {
98102
for (size_t i = 0; i < size_; ++i) {
99103
if (keys_[i] == key) {
100104
return &values_[i];
@@ -103,34 +107,74 @@ class SieveCache {
103107
return nullptr;
104108
}
105109

106-
size_t capacity() const {
110+
inline V* get_locked(const K& key) noexcept {
111+
std::shared_lock<std::shared_mutex> lock(rw_mutex_);
112+
for (size_t i = 0; i < size_; ++i) {
113+
if (keys_[i] == key) {
114+
return &values_[i];
115+
}
116+
}
117+
return nullptr;
118+
}
119+
120+
constexpr size_t capacity() const {
107121
return capacity_;
108122
}
109123

110-
bool empty() const {
124+
constexpr bool empty() const {
111125
return size_ == 0;
112126
}
113127

114-
bool insert(const K& key, const V& value) {
128+
inline bool insert(const K& key, const V& value) noexcept {
129+
if (size_ < capacity_) {
130+
construct(&keys_[size_], key);
131+
construct(&values_[size_], value);
132+
++size_;
133+
return true;
134+
}
135+
return false; // Cache is full
136+
}
137+
138+
inline bool insert_locked(const K& key, const V& value) noexcept {
139+
std::unique_lock<std::shared_mutex> lock(rw_mutex_);
115140
if (size_ < capacity_) {
116-
new (&keys_[size_]) K(key);
117-
new (&values_[size_]) V(value);
141+
construct(&keys_[size_], key);
142+
construct(&values_[size_], value);
118143
++size_;
119144
return true;
120145
}
121146
return false; // Cache is full
122147
}
123148

124-
bool remove(const K& key) {
149+
inline bool remove(const K& key) noexcept {
150+
for (size_t i = 0; i < size_; ++i) {
151+
if (keys_[i] == key) {
152+
destroy(&keys_[i]);
153+
destroy(&values_[i]);
154+
for (size_t j = i; j < size_ - 1; ++j) {
155+
construct(&keys_[j], std::move(keys_[j + 1]));
156+
construct(&values_[j], std::move(values_[j + 1]));
157+
destroy(&keys_[j + 1]);
158+
destroy(&values_[j + 1]);
159+
}
160+
--size_;
161+
return true;
162+
}
163+
}
164+
return false; // Key not found
165+
}
166+
167+
inline bool remove_locked(const K& key) noexcept {
168+
std::unique_lock<std::shared_mutex> lock(rw_mutex_);
125169
for (size_t i = 0; i < size_; ++i) {
126170
if (keys_[i] == key) {
127-
keys_[i].~K();
128-
values_[i].~V();
171+
destroy(&keys_[i]);
172+
destroy(&values_[i]);
129173
for (size_t j = i; j < size_ - 1; ++j) {
130-
new (&keys_[j]) K(std::move(keys_[j + 1]));
131-
new (&values_[j]) V(std::move(values_[j + 1]));
132-
keys_[j + 1].~K();
133-
values_[j + 1].~V();
174+
construct(&keys_[j], std::move(keys_[j + 1]));
175+
construct(&values_[j], std::move(values_[j + 1]));
176+
destroy(&keys_[j + 1]);
177+
destroy(&values_[j + 1]);
134178
}
135179
--size_;
136180
return true;
@@ -139,7 +183,17 @@ class SieveCache {
139183
return false; // Key not found
140184
}
141185

142-
bool contains(const K& key) const {
186+
inline bool contains(const K& key) const noexcept {
187+
for (size_t i = 0; i < size_; ++i) {
188+
if (keys_[i] == key) {
189+
return true;
190+
}
191+
}
192+
return false;
193+
}
194+
195+
inline bool contains_locked(const K& key) const noexcept {
196+
std::lock_guard<std::shared_mutex> lock(rw_mutex_);
143197
for (size_t i = 0; i < size_; ++i) {
144198
if (keys_[i] == key) {
145199
return true;
@@ -148,24 +202,43 @@ class SieveCache {
148202
return false;
149203
}
150204

151-
void clear() {
205+
inline void clear() noexcept {
152206
for (size_t i = 0; i < size_; ++i) {
153207
keys_[i].~K();
154208
values_[i].~V();
155209
}
156210
size_ = 0;
157211
}
158212

159-
size_t length() const {
213+
inline void clear_locked() noexcept {
214+
std::unique_lock<std::shared_mutex> lock(rw_mutex_);
215+
for (size_t i = 0; i < size_; ++i) {
216+
keys_[i].~K();
217+
values_[i].~V();
218+
}
219+
size_ = 0;
220+
}
221+
222+
constexpr size_t length() const noexcept {
160223
return size_;
161224
}
162225

163226
private:
227+
template<typename T, typename... Args>
228+
void construct(T* ptr, Args&&... args) noexcept {
229+
new (ptr) T(std::forward<Args>(args)...);
230+
}
231+
232+
template<typename T>
233+
void destroy(T* ptr) noexcept {
234+
ptr->~T();
235+
}
164236
size_t capacity_;
165237
size_t size_;
166238
K* keys_;
167239
V* values_;
168240
std::pmr::memory_resource* mem_resource_;
241+
mutable std::shared_mutex rw_mutex_;
169242
};
170243

171244
#endif // SIEVE_HPP

tests/test.cc

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,18 +93,17 @@ TEST_CASE("Testing SieveCache functionality") {
9393
}
9494

9595
SUBCASE("Thread Safety") {
96-
constexpr size_t capacity = 100;
97-
SieveCache<int, std::string> cache3(capacity);
96+
SieveCache<int, std::string> cache3(100);
9897

9998
auto insert_task = [&cache3]() {
100-
for (int i = 0; i < 50; ++i) {
101-
cache3.insert(i, "value" + std::to_string(i));
99+
for (int i = 0; i < 100; ++i) {
100+
cache3.insert_locked(i, "value" + std::to_string(i));
102101
}
103102
};
104103

105104
auto get_task = [&cache3]() {
106-
for (int i = 0; i < 50; ++i) {
107-
auto value = cache3.get(i);
105+
for (int i = 0; i < 100; ++i) {
106+
auto value = cache3.get_locked(i);
108107
if (value) {
109108
CHECK(*value == "value" + std::to_string(i));
110109
}

0 commit comments

Comments
 (0)