6161#include " openssl/digest.hpp"
6262#include " vm/dict.h"
6363
64+ #include < condition_variable>
65+ #include < latch>
6466#include < numeric>
6567#include < optional>
68+ #include < queue>
6669
6770namespace vm {
71+ class ThreadExecutor : public DynamicBagOfCellsDb ::AsyncExecutor {
72+ public:
73+ explicit ThreadExecutor (size_t threads_n) {
74+ for (size_t i = 0 ; i < threads_n; ++i) {
75+ threads_.emplace_back ([this ]() {
76+ while (true ) {
77+ auto task = pop_task ();
78+ if (!task) {
79+ break ;
80+ }
81+ CHECK (generation_.load () % 2 == 1 );
82+ task ();
83+ }
84+ });
85+ }
86+ }
87+
88+ ~ThreadExecutor () override {
89+ for (size_t i = 0 ; i < threads_.size (); ++i) {
90+ push_task ({});
91+ }
92+ for (auto &t : threads_) {
93+ t.join ();
94+ }
95+ }
96+
97+ void execute_async (std::function<void ()> f) override {
98+ push_task (std::move (f));
99+ }
100+
101+ void execute_sync (std::function<void ()> f) override {
102+ auto x = generation_.load ();
103+ std::scoped_lock lock (sync_mutex_);
104+ CHECK (x == generation_);
105+ CHECK (generation_.load () % 2 == 1 );
106+ f ();
107+ CHECK (generation_.load () % 2 == 1 );
108+ }
109+ void inc_generation () {
110+ generation_.fetch_add (1 );
111+ }
112+
113+ private:
114+ std::atomic<size_t > generation_{0 };
115+ std::queue<std::pair<std::function<void ()>, size_t >> queue_;
116+ std::mutex queue_mutex_;
117+ std::condition_variable cv_;
118+ std::mutex sync_mutex_;
119+ std::vector<td::thread> threads_;
120+
121+ std::function<void ()> pop_task () {
122+ std::unique_lock lock (queue_mutex_);
123+ cv_.wait (lock, [&] { return !queue_.empty (); });
124+ CHECK (!queue_.empty ());
125+ auto task = std::move (queue_.front ());
126+ queue_.pop ();
127+ CHECK (task.second == generation_);
128+ return task.first ;
129+ }
130+
131+ void push_task (std::function<void ()> task) {
132+ {
133+ std::scoped_lock lock (queue_mutex_);
134+ queue_.emplace (std::move (task), generation_.load ());
135+ }
136+ cv_.notify_one ();
137+ }
138+ };
68139
69140std::vector<int > do_get_serialization_modes () {
70141 std::vector<int > res;
@@ -890,25 +961,91 @@ TEST(TonDb, InMemoryDynamicBocSimple) {
890961 boc = DynamicBagOfCellsDb::create_in_memory (kv.get (), {});
891962}
892963
893- void test_dynamic_boc (std::optional<DynamicBagOfCellsDb::CreateInMemoryOptions> o_in_memory) {
964+ int VERBOSITY_NAME (boc) = VERBOSITY_NAME(DEBUG) + 10;
965+
966+ struct BocOptions {
967+ std::shared_ptr<ThreadExecutor> async_executor;
968+ std::optional<DynamicBagOfCellsDb::CreateInMemoryOptions> o_in_memory;
969+ td::uint64 seed{123 };
970+
971+ auto create_dboc (td::KeyValueReader *kv, std::optional<td::int64> o_root_n) {
972+ if (o_in_memory) {
973+ auto res = DynamicBagOfCellsDb::create_in_memory (kv, *o_in_memory);
974+ auto stats = res->get_stats ().move_as_ok ();
975+ if (o_root_n) {
976+ ASSERT_EQ (*o_root_n, stats.roots_total_count );
977+ }
978+ VLOG (boc) << " reset roots_n=" << stats.roots_total_count << " cells_n=" << stats.cells_total_count ;
979+ return res;
980+ }
981+ return DynamicBagOfCellsDb::create ();
982+ };
983+ void prepare_commit (DynamicBagOfCellsDb &dboc) {
984+ if (async_executor) {
985+ async_executor->inc_generation ();
986+ std::latch latch (1 );
987+ td::Result<td::Unit> res;
988+ async_executor->execute_sync ([&] {
989+ dboc.prepare_commit_async (async_executor, [&](auto r) {
990+ res = std::move (r);
991+ latch.count_down ();
992+ });
993+ });
994+ latch.wait ();
995+ async_executor->execute_sync ([&] {});
996+ async_executor->inc_generation ();
997+ } else {
998+ dboc.prepare_commit ();
999+ }
1000+ }
1001+ };
1002+
1003+ template <class F >
1004+ void with_all_boc_options (F &&f, size_t tests_n = 500 ) {
1005+ LOG (INFO) << " Test dynamic boc" ;
1006+ auto counter = [] { return td::NamedThreadSafeCounter::get_default ().get_counter (" DataCell" ).sum (); };
1007+ auto run = [&](BocOptions options) {
1008+ LOG (INFO) << " \t " << (options.o_in_memory ? " in memory" : " on disk" ) << (options.async_executor ? " async" : " " );
1009+ if (options.o_in_memory ) {
1010+ LOG (INFO) << " \t\t use_arena=" << options.o_in_memory ->use_arena
1011+ << " less_memory=" << options.o_in_memory ->use_less_memory_during_creation ;
1012+ }
1013+ for (td::uint32 i = 0 ; i < tests_n; i++) {
1014+ auto before = counter ();
1015+ options.seed = i == 0 ? 123 : i;
1016+ f (options);
1017+ auto after = counter ();
1018+ LOG_CHECK ((options.o_in_memory && options.o_in_memory ->use_arena ) || before == after)
1019+ << before << " vs " << after;
1020+ }
1021+ };
1022+ run ({.async_executor = std::make_shared<ThreadExecutor>(4 )});
1023+ run ({});
1024+ for (auto use_arena : {false , true }) {
1025+ for (auto less_memory : {false , true }) {
1026+ run ({.o_in_memory =
1027+ DynamicBagOfCellsDb::CreateInMemoryOptions{.extra_threads = std::thread::hardware_concurrency (),
1028+ .verbose = false ,
1029+ .use_arena = use_arena,
1030+ .use_less_memory_during_creation = less_memory}});
1031+ }
1032+ }
1033+ }
1034+
1035+ void test_dynamic_boc (BocOptions options) {
8941036 auto counter = [] { return td::NamedThreadSafeCounter::get_default ().get_counter (" DataCell" ).sum (); };
8951037 auto before = counter ();
8961038 SCOPE_EXIT {
897- LOG_CHECK ((o_in_memory && o_in_memory->use_arena ) || before == counter ()) << before << " vs " << counter ();
898- ;
1039+ LOG_CHECK ((options. o_in_memory && options. o_in_memory ->use_arena ) || before == counter ())
1040+ << before << " vs " << counter () ;
8991041 };
900- td::Random::Xorshift128plus rnd{123 };
1042+ td::Random::Xorshift128plus rnd{options. seed };
9011043 std::string old_root_hash;
9021044 std::string old_root_serialization;
9031045 auto kv = std::make_shared<td::MemoryKeyValue>();
9041046 auto create_dboc = [&]() {
905- if (o_in_memory) {
906- auto res = DynamicBagOfCellsDb::create_in_memory (kv.get (), *o_in_memory);
907- auto roots_n = old_root_hash.empty () ? 0 : 1 ;
908- ASSERT_EQ (roots_n, res->get_stats ().ok ().roots_total_count );
909- return res;
910- }
911- return DynamicBagOfCellsDb::create ();
1047+ auto roots_n = old_root_hash.empty () ? 0 : 1 ;
1048+ return options.create_dboc (kv.get (), roots_n);
9121049 };
9131050 auto dboc = create_dboc ();
9141051 dboc->set_loader (std::make_unique<CellLoader>(kv));
@@ -947,51 +1084,28 @@ void test_dynamic_boc(std::optional<DynamicBagOfCellsDb::CreateInMemoryOptions>
9471084 ASSERT_EQ (0u , kv->count (" " ).ok ());
9481085}
9491086
950- template <class F >
951- void with_all_boc_options (F &&f) {
952- LOG (INFO) << " Test dynamic boc" ;
953- LOG (INFO) << " \t on disk" ;
954- f ({});
955- for (auto use_arena : {false , true }) {
956- for (auto less_memory : {false , true }) {
957- LOG (INFO) << " \t use_arena=" << use_arena << " less_memory=" << less_memory;
958- f (DynamicBagOfCellsDb::CreateInMemoryOptions{.extra_threads = std::thread::hardware_concurrency (),
959- .verbose = false ,
960- .use_arena = use_arena,
961- .use_less_memory_during_creation = less_memory});
962- }
963- }
964- }
9651087TEST (TonDb, DynamicBoc) {
966- with_all_boc_options (test_dynamic_boc);
1088+ with_all_boc_options (test_dynamic_boc, 1 );
9671089};
9681090
969- void test_dynamic_boc2 (std::optional<DynamicBagOfCellsDb::CreateInMemoryOptions> o_in_memory ) {
970- int VERBOSITY_NAME (boc) = VERBOSITY_NAME (DEBUG) + 10 ;
971- td::Random::Xorshift128plus rnd{ 123 };
972- int total_roots = 10000 ;
973- int max_roots = 20 ;
1091+ void test_dynamic_boc2 (BocOptions options ) {
1092+ td::Random::Xorshift128plus rnd{options. seed } ;
1093+
1094+ int total_roots = rnd. fast ( 1 , !rnd. fast ( 0 , 10 ) * 100 + 10 ) ;
1095+ int max_roots = rnd. fast ( 1 , 20 ) ;
9741096 int last_commit_at = 0 ;
9751097 int first_root_id = 0 ;
9761098 int last_root_id = 0 ;
9771099 auto kv = std::make_shared<td::MemoryKeyValue>();
978- auto create_dboc = [&](td::int64 root_n) {
979- if (o_in_memory) {
980- auto res = DynamicBagOfCellsDb::create_in_memory (kv.get (), *o_in_memory);
981- auto stats = res->get_stats ().move_as_ok ();
982- ASSERT_EQ (root_n, stats.roots_total_count );
983- VLOG (boc) << " reset roots_n=" << stats.roots_total_count << " cells_n=" << stats.cells_total_count ;
984- return res;
985- }
986- return DynamicBagOfCellsDb::create ();
987- };
1100+ auto create_dboc = [&](td::int64 root_n) { return options.create_dboc (kv.get (), root_n); };
9881101 auto dboc = create_dboc (0 );
9891102 dboc->set_loader (std::make_unique<CellLoader>(kv));
9901103
9911104 auto counter = [] { return td::NamedThreadSafeCounter::get_default ().get_counter (" DataCell" ).sum (); };
9921105 auto before = counter ();
993- SCOPE_EXIT {
994- LOG_CHECK ((o_in_memory && o_in_memory->use_arena ) || before == counter ()) << before << " vs " << counter ();
1106+ SCOPE_EXIT{
1107+ // LOG_CHECK((options.o_in_memory && options.o_in_memory->use_arena) || before == counter())
1108+ // << before << " vs " << counter();
9951109 };
9961110
9971111 std::vector<Ref<Cell>> roots (max_roots);
@@ -1009,7 +1123,7 @@ void test_dynamic_boc2(std::optional<DynamicBagOfCellsDb::CreateInMemoryOptions>
10091123 if (from_root.is_null ()) {
10101124 VLOG (boc) << " from db" ;
10111125 auto from_root_hash = root_hashes[root_id % max_roots];
1012- if (o_in_memory && ( rnd () % 2 == 0 ) ) {
1126+ if (rnd () % 2 == 0 ) {
10131127 from_root = dboc->load_root (from_root_hash).move_as_ok ();
10141128 } else {
10151129 from_root = dboc->load_cell (from_root_hash).move_as_ok ();
@@ -1041,7 +1155,8 @@ void test_dynamic_boc2(std::optional<DynamicBagOfCellsDb::CreateInMemoryOptions>
10411155
10421156 auto commit = [&] {
10431157 VLOG (boc) << " commit" ;
1044- dboc->prepare_commit ();
1158+ // rnd.fast(0, 1);
1159+ options.prepare_commit (*dboc);
10451160 {
10461161 CellStorer cell_storer (*kv);
10471162 dboc->commit (cell_storer);
@@ -2147,18 +2262,18 @@ TEST(TonDb, BocRespectsUsageCell) {
21472262 ASSERT_STREQ (serialization, serialization_of_virtualized_cell);
21482263}
21492264
2150- void test_dynamic_boc_respectes_usage_cell (std::optional< vm::DynamicBagOfCellsDb::CreateInMemoryOptions> o_in_memory ) {
2151- td::Random::Xorshift128plus rnd (123 );
2265+ void test_dynamic_boc_respectes_usage_cell (vm::BocOptions options ) {
2266+ td::Random::Xorshift128plus rnd (options. seed );
21522267 auto cell = vm::gen_random_cell (20 , rnd, true );
21532268 auto usage_tree = std::make_shared<vm::CellUsageTree>();
21542269 auto usage_cell = vm::UsageCell::create (cell, usage_tree->root_ptr ());
21552270
21562271 auto kv = std::make_shared<td::MemoryKeyValue>();
2157- auto dboc = o_in_memory ? vm::DynamicBagOfCellsDb::create_in_memory (kv.get (), *o_in_memory)
2158- : vm::DynamicBagOfCellsDb::create ();
2272+ auto dboc = options.create_dboc (kv.get (), {});
21592273 dboc->set_loader (std::make_unique<vm::CellLoader>(kv));
21602274 dboc->inc (usage_cell);
21612275 {
2276+ options.prepare_commit (*dboc);
21622277 vm::CellStorer cell_storer (*kv);
21632278 dboc->commit (cell_storer);
21642279 }
@@ -2171,7 +2286,7 @@ void test_dynamic_boc_respectes_usage_cell(std::optional<vm::DynamicBagOfCellsDb
21712286}
21722287
21732288TEST (TonDb, DynamicBocRespectsUsageCell) {
2174- vm::with_all_boc_options (test_dynamic_boc_respectes_usage_cell);
2289+ vm::with_all_boc_options (test_dynamic_boc_respectes_usage_cell, 20 );
21752290}
21762291
21772292TEST (TonDb, LargeBocSerializer) {
0 commit comments