Skip to content

Commit b919012

Browse files
committed
refactor(block_cache): factor out periodic_executor
1 parent 6e55ddf commit b919012

File tree

4 files changed

+215
-57
lines changed

4 files changed

+215
-57
lines changed

cmake/libdwarfs.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ add_library(
106106
src/reader/internal/inode_reader_v2.cpp
107107
src/reader/internal/metadata_types.cpp
108108
src/reader/internal/metadata_v2.cpp
109+
src/reader/internal/periodic_executor.cpp
109110
)
110111

111112
add_library(
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/* vim:set ts=2 sw=2 sts=2 et: */
2+
/**
3+
* \author Marcus Holland-Moritz ([email protected])
4+
* \copyright Copyright (c) Marcus Holland-Moritz
5+
*
6+
* This file is part of dwarfs.
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the “Software”), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24+
* SOFTWARE.
25+
*
26+
* SPDX-License-Identifier: MIT
27+
*/
28+
29+
#pragma once
30+
31+
#include <chrono>
32+
#include <functional>
33+
#include <memory>
34+
#include <mutex>
35+
#include <string_view>
36+
37+
namespace dwarfs::reader::internal {
38+
39+
class periodic_executor {
40+
public:
41+
periodic_executor(std::mutex& mx, std::chrono::nanoseconds period,
42+
std::string_view name, std::function<void()> func);
43+
44+
void start() const { impl_->start(); }
45+
46+
void stop() const { impl_->stop(); }
47+
48+
bool running() const { return impl_->running(); }
49+
50+
void set_period(std::chrono::nanoseconds period) const {
51+
impl_->set_period(period);
52+
}
53+
54+
class impl {
55+
public:
56+
virtual ~impl() = default;
57+
58+
virtual void start() const = 0;
59+
virtual void stop() const = 0;
60+
virtual bool running() const = 0;
61+
virtual void set_period(std::chrono::nanoseconds period) const = 0;
62+
};
63+
64+
private:
65+
std::unique_ptr<impl const> impl_;
66+
};
67+
68+
} // namespace dwarfs::reader::internal

src/reader/internal/block_cache.cpp

Lines changed: 32 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,12 @@
3030
#include <atomic>
3131
#include <cassert>
3232
#include <chrono>
33-
#include <condition_variable>
3433
#include <exception>
3534
#include <future>
3635
#include <iterator>
3736
#include <mutex>
3837
#include <new>
3938
#include <shared_mutex>
40-
#include <thread>
4139
#include <utility>
4240
#include <vector>
4341

@@ -62,6 +60,7 @@
6260
#include <dwarfs/reader/internal/block_cache.h>
6361
#include <dwarfs/reader/internal/block_cache_byte_buffer_factory.h>
6462
#include <dwarfs/reader/internal/cached_block.h>
63+
#include <dwarfs/reader/internal/periodic_executor.h>
6564

6665
namespace dwarfs::reader::internal {
6766

@@ -231,6 +230,7 @@ class block_cache_ final : public block_cache::impl {
231230
std::shared_ptr<performance_monitor const> const& perfmon
232231
[[maybe_unused]])
233232
: cache_(0)
233+
, tidy_runner_{mx_, {}, "tidy-blkcache", [this] { tidy_cache(); }}
234234
, mm_(std::move(mm))
235235
, buffer_factory_{block_cache_byte_buffer_factory::create(
236236
options.allocation_mode)}
@@ -256,9 +256,7 @@ class block_cache_ final : public block_cache::impl {
256256
~block_cache_() noexcept override {
257257
LOG_DEBUG << "stopping cache workers";
258258

259-
if (tidy_running_) {
260-
stop_tidy_thread();
261-
}
259+
tidy_runner_.stop();
262260

263261
if (wg_) {
264262
wg_.stop();
@@ -362,23 +360,21 @@ class block_cache_ final : public block_cache::impl {
362360

363361
void set_tidy_config(cache_tidy_config const& cfg) override {
364362
if (cfg.strategy == cache_tidy_strategy::NONE) {
365-
if (tidy_running_) {
366-
stop_tidy_thread();
367-
}
363+
tidy_runner_.stop();
368364
} else {
369365
if (cfg.interval == std::chrono::milliseconds::zero()) {
370366
DWARFS_THROW(runtime_error, "tidy interval is zero");
371367
}
372368

373-
std::lock_guard lock(mx_);
369+
{
370+
std::lock_guard lock(mx_);
371+
tidy_config_ = cfg;
372+
}
374373

375-
tidy_config_ = cfg;
374+
tidy_runner_.set_period(cfg.interval);
376375

377-
if (tidy_running_) {
378-
tidy_cond_.notify_all();
379-
} else {
380-
tidy_running_ = true;
381-
tidy_thread_ = std::thread(&block_cache_::tidy_thread, this);
376+
if (!tidy_runner_.running()) {
377+
tidy_runner_.start();
382378
}
383379
}
384380
}
@@ -600,15 +596,6 @@ class block_cache_ final : public block_cache::impl {
600596
}
601597
}
602598

603-
void stop_tidy_thread() {
604-
{
605-
std::lock_guard lock(mx_);
606-
tidy_running_ = false;
607-
}
608-
tidy_cond_.notify_all();
609-
tidy_thread_.join();
610-
}
611-
612599
void update_block_stats(cached_block const& cb) {
613600
if (cb.range_end() < cb.uncompressed_size()) {
614601
partially_decompressed_.fetch_add(1, std::memory_order_relaxed);
@@ -749,36 +736,26 @@ class block_cache_ final : public block_cache::impl {
749736
}
750737
}
751738

752-
void tidy_thread() {
753-
folly::setThreadName("cache-tidy");
754-
755-
std::unique_lock lock(mx_);
756-
757-
while (tidy_running_) {
758-
if (tidy_cond_.wait_for(lock, tidy_config_.interval) ==
759-
std::cv_status::timeout) {
760-
switch (tidy_config_.strategy) {
761-
case cache_tidy_strategy::EXPIRY_TIME:
762-
LOG_DEBUG << "tidying cache by expiry time";
763-
remove_block_if(
764-
[tp = std::chrono::steady_clock::now() -
765-
tidy_config_.expiry_time](cached_block const& blk) {
766-
return blk.last_used_before(tp);
767-
});
768-
break;
769-
770-
case cache_tidy_strategy::BLOCK_SWAPPED_OUT: {
771-
LOG_DEBUG << "tidying cache by swapped out blocks";
772-
std::vector<uint8_t> tmp;
773-
remove_block_if([&tmp](cached_block const& blk) {
774-
return blk.any_pages_swapped_out(tmp);
775-
});
776-
} break;
777-
778-
default:
779-
break;
780-
}
781-
}
739+
void tidy_cache() {
740+
switch (tidy_config_.strategy) {
741+
case cache_tidy_strategy::EXPIRY_TIME:
742+
LOG_DEBUG << "tidying cache by expiry time";
743+
remove_block_if([tp = std::chrono::steady_clock::now() -
744+
tidy_config_.expiry_time](cached_block const& blk) {
745+
return blk.last_used_before(tp);
746+
});
747+
break;
748+
749+
case cache_tidy_strategy::BLOCK_SWAPPED_OUT: {
750+
LOG_DEBUG << "tidying cache by swapped out blocks";
751+
std::vector<uint8_t> tmp;
752+
remove_block_if([&tmp](cached_block const& blk) {
753+
return blk.any_pages_swapped_out(tmp);
754+
});
755+
} break;
756+
757+
default:
758+
break;
782759
}
783760
}
784761

@@ -791,9 +768,7 @@ class block_cache_ final : public block_cache::impl {
791768
mutable lru_type cache_;
792769
mutable fast_map_type<size_t, std::vector<std::weak_ptr<block_request_set>>>
793770
active_;
794-
std::thread tidy_thread_;
795-
std::condition_variable tidy_cond_;
796-
bool tidy_running_{false};
771+
periodic_executor tidy_runner_;
797772

798773
mutable std::mutex mx_dec_;
799774
mutable fast_map_type<size_t, std::weak_ptr<block_request_set>>
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/* vim:set ts=2 sw=2 sts=2 et: */
2+
/**
3+
* \author Marcus Holland-Moritz ([email protected])
4+
* \copyright Copyright (c) Marcus Holland-Moritz
5+
*
6+
* This file is part of dwarfs.
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the “Software”), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24+
* SOFTWARE.
25+
*
26+
* SPDX-License-Identifier: MIT
27+
*/
28+
29+
#include <atomic>
30+
#include <condition_variable>
31+
#include <optional>
32+
#include <thread>
33+
34+
#include <folly/system/ThreadName.h>
35+
36+
#include <dwarfs/reader/internal/periodic_executor.h>
37+
38+
namespace dwarfs::reader::internal {
39+
40+
namespace {
41+
42+
class periodic_executor_ final : public periodic_executor::impl {
43+
public:
44+
periodic_executor_(std::mutex& mx, std::chrono::nanoseconds period,
45+
std::string_view name, std::function<void()> func)
46+
: mx_{mx}
47+
, period_{period}
48+
, name_{name}
49+
, func_{std::move(func)} {}
50+
51+
~periodic_executor_() override { stop(); }
52+
53+
void start() const override {
54+
std::lock_guard lock(mx_);
55+
if (!running_.load()) {
56+
running_.store(true);
57+
thread_.emplace(&periodic_executor_::run, this);
58+
}
59+
}
60+
61+
void stop() const override {
62+
std::unique_lock lock(mx_);
63+
if (running_.load()) {
64+
running_.store(false);
65+
lock.unlock();
66+
cv_.notify_all();
67+
thread_->join();
68+
thread_.reset();
69+
}
70+
}
71+
72+
bool running() const override { return running_.load(); }
73+
74+
void set_period(std::chrono::nanoseconds period) const override {
75+
{
76+
std::lock_guard lock(mx_);
77+
period_ = period;
78+
}
79+
80+
if (running_.load()) {
81+
cv_.notify_all();
82+
}
83+
}
84+
85+
private:
86+
void run() const {
87+
folly::setThreadName(name_);
88+
std::unique_lock lock(mx_);
89+
while (running_.load()) {
90+
if (cv_.wait_for(lock, period_) == std::cv_status::timeout) {
91+
func_();
92+
}
93+
}
94+
}
95+
96+
std::mutex& mx_;
97+
std::condition_variable mutable cv_;
98+
std::atomic<bool> mutable running_{false};
99+
std::optional<std::thread> mutable thread_;
100+
std::chrono::nanoseconds mutable period_;
101+
std::string const name_;
102+
std::function<void()> func_;
103+
};
104+
105+
} // namespace
106+
107+
periodic_executor::periodic_executor(std::mutex& mx,
108+
std::chrono::nanoseconds period,
109+
std::string_view name,
110+
std::function<void()> func)
111+
: impl_{std::make_unique<periodic_executor_>(mx, period, name,
112+
std::move(func))} {}
113+
114+
} // namespace dwarfs::reader::internal

0 commit comments

Comments
 (0)