Skip to content

Commit cbbee3b

Browse files
authored
Fix instance pool (#1881)
* Fix instance pool multithreading * Fix locks on compiling_modules * Don't create redundant promises
1 parent 3130861 commit cbbee3b

File tree

3 files changed

+32
-43
lines changed

3 files changed

+32
-43
lines changed

core/runtime/common/runtime_instances_pool.cpp

+18-25
Original file line numberDiff line numberDiff line change
@@ -79,58 +79,51 @@ namespace kagome::runtime {
7979

8080
if (!pool_opt) {
8181
lock.unlock();
82-
if (auto future = getFutureCompiledModule(code_hash)) {
83-
lock.lock();
84-
pool_opt = pools_.get(code_hash);
85-
} else {
86-
OUTCOME_TRY(module, tryCompileModule(code_hash, code_zstd));
87-
BOOST_ASSERT(module != nullptr);
88-
lock.lock();
82+
OUTCOME_TRY(module, tryCompileModule(code_hash, code_zstd));
83+
lock.lock();
84+
pool_opt = pools_.get(code_hash);
85+
if (!pool_opt) {
8986
pool_opt = std::ref(pools_.put(code_hash, InstancePool{module, {}}));
9087
}
9188
}
89+
BOOST_ASSERT(pool_opt);
9290
auto instance = pool_opt->get().instantiate(lock);
9391
return std::make_shared<BorrowedInstance>(
9492
weak_from_this(), code_hash, std::move(instance));
9593
}
9694

97-
std::optional<std::shared_future<RuntimeInstancesPool::CompilationResult>>
98-
RuntimeInstancesPool::getFutureCompiledModule(
99-
const CodeHash &code_hash) const {
100-
std::unique_lock l{compiling_modules_mtx_};
101-
auto iter = compiling_modules_.find(code_hash);
102-
if (iter == compiling_modules_.end()) {
103-
return std::nullopt;
104-
}
105-
auto future = iter->second;
106-
l.unlock();
107-
return future;
108-
}
109-
11095
RuntimeInstancesPool::CompilationResult
11196
RuntimeInstancesPool::tryCompileModule(const CodeHash &code_hash,
11297
common::BufferView code_zstd) {
11398
std::unique_lock l{compiling_modules_mtx_};
99+
if (auto iter = compiling_modules_.find(code_hash);
100+
iter != compiling_modules_.end()) {
101+
std::shared_future<CompilationResult> future = iter->second;
102+
l.unlock();
103+
return future.get();
104+
}
114105
std::promise<CompilationResult> promise;
115-
auto [iter, inserted] =
106+
auto [iter, is_inserted] =
116107
compiling_modules_.insert({code_hash, promise.get_future()});
117-
BOOST_ASSERT(inserted);
108+
BOOST_ASSERT(is_inserted);
109+
BOOST_ASSERT(iter != compiling_modules_.end());
118110
l.unlock();
119111

120112
common::Buffer code;
121-
CompilationResult res{nullptr};
113+
std::optional<CompilationResult> res;
122114
if (!uncompressCodeIfNeeded(code_zstd, code)) {
123115
res = CompilationError{"Failed to uncompress code"};
124116
} else {
125117
res = common::map_result(module_factory_->make(code), [](auto &&module) {
126118
return std::shared_ptr<const Module>(module);
127119
});
128120
}
129-
promise.set_value(res);
121+
BOOST_ASSERT(res);
130122

131123
l.lock();
132124
compiling_modules_.erase(iter);
133-
return res;
125+
promise.set_value(*res);
126+
return *res;
134127
}
135128

136129
outcome::result<std::shared_ptr<ModuleInstance>>

core/runtime/common/runtime_instances_pool.hpp

-3
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,6 @@ namespace kagome::runtime {
9090
CompilationResult tryCompileModule(const CodeHash &code_hash,
9191
common::BufferView code_zstd);
9292

93-
std::optional<std::shared_future<CompilationResult>> getFutureCompiledModule(
94-
const CodeHash &code_hash) const;
95-
9693
std::shared_ptr<ModuleFactory> module_factory_;
9794

9895
std::mutex pools_mtx_;

test/core/runtime/instance_pool_test.cpp

+14-15
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55

66
#include <gtest/gtest.h>
77

8-
#if defined(BACKWARD_HAS_BACKTRACE)
9-
#include <backward.hpp>
10-
#endif
8+
#include <algorithm>
9+
#include <random>
10+
#include <ranges>
1111

1212
#include "testutil/literals.hpp"
1313
#include "testutil/outcome.hpp"
@@ -33,10 +33,6 @@ RuntimeInstancesPool::CodeHash make_code_hash(int i) {
3333
TEST(InstancePoolTest, HeavilyMultithreadedCompilation) {
3434
using namespace std::chrono_literals;
3535

36-
#if defined(BACKWARD_HAS_BACKTRACE)
37-
backward::SignalHandling sh;
38-
#endif
39-
4036
auto module_instance_mock = std::make_shared<ModuleInstanceMock>();
4137

4238
auto module_mock = std::make_shared<ModuleMock>();
@@ -54,13 +50,16 @@ TEST(InstancePoolTest, HeavilyMultithreadedCompilation) {
5450
return module_mock;
5551
}));
5652

57-
RuntimeInstancesPool pool{module_factory, 5};
53+
static constexpr int THREAD_NUM = 100;
54+
static constexpr int POOL_SIZE = 10;
55+
56+
RuntimeInstancesPool pool{module_factory, POOL_SIZE};
5857

5958
std::vector<std::thread> threads;
60-
for (int i = 0; i < 10; i++) {
61-
threads.emplace_back(std::thread([&pool, i, &code]() {
59+
for (int i = 0; i < THREAD_NUM; i++) {
60+
threads.emplace_back(std::thread([&pool, &code, i]() {
6261
ASSERT_OUTCOME_SUCCESS_TRY(
63-
pool.instantiateFromCode(make_code_hash(i % 5), code.view()));
62+
pool.instantiateFromCode(make_code_hash(i % POOL_SIZE), code));
6463
}));
6564
}
6665

@@ -69,12 +68,12 @@ TEST(InstancePoolTest, HeavilyMultithreadedCompilation) {
6968
}
7069

7170
// check that 'make' was only called 5 times
72-
ASSERT_EQ(times_make_called.load(), 5);
71+
ASSERT_EQ(times_make_called.load(), POOL_SIZE);
7372

74-
// check that all 10 instances are in cache
75-
for (int i = 0; i < 5; i++) {
73+
// check that all POOL_SIZE instances are in cache
74+
for (int i = 0; i < POOL_SIZE; i++) {
7675
ASSERT_OUTCOME_SUCCESS_TRY(
7776
pool.instantiateFromCode(make_code_hash(i), code.view()));
7877
}
79-
ASSERT_EQ(times_make_called.load(), 5);
78+
ASSERT_EQ(times_make_called.load(), POOL_SIZE);
8079
}

0 commit comments

Comments
 (0)