// config-store, срез 11: многопоточный доступ
// Раздел учебника: Том II, глава 11, «Многопоточность», §11.2 «Sticky threads».
// Что добавляется: несколько потоков читателей и один писатель на одном
//   окружении; параллельные читатели видят стабильный снапшот.
// Сборка: g++ -std=c++17 -I<libmdbx-include> -I../common config-store-11.c++ -lmdbx -pthread
// Запуск: ./config-store-11
// Ожидаемый вывод:
//   readers saw the same snapshot (10 keys)
//   after writer finished, total keys = 20
//   ok: config-store-11 works
// При сбое — сообщение в stderr и exit 1.
//
// SPDX-License-Identifier: Apache-2.0

#include <atomic>
#include <iostream>
#include <string>
#include <thread>
#include <vector>

#include "common.h++"
#include <mdbx.h++>

int main() {
  try {
    const std::string path = std::string(example::tmpdir()) + "/config-store-11.mdbx";
    mdbx::env::remove(path);
    mdbx::env_managed env = example::env_open(path);

    {
      auto txn = env.start_write();
      auto table = txn.open_map(nullptr);
      for (int i = 0; i < 10; ++i) {
        const auto key = "key." + std::to_string(i);
        txn.insert(table, mdbx::slice(key), mdbx::slice("v"));
      }
      txn.commit();
    }

    auto base = env.start_read();
    auto base_table = base.open_map(nullptr);
    const uint64_t snapshot_size = base.get_map_stat(base_table).ms_entries;

    // Писатель добавляет ещё 10 ключей.
    std::atomic<bool> writer_done{false};
    std::thread writer([&] {
      auto txn = env.start_write();
      auto table = txn.open_map(nullptr);
      for (int i = 10; i < 20; ++i) {
        const auto key = "key." + std::to_string(i);
        txn.insert(table, mdbx::slice(key), mdbx::slice("w"));
      }
      txn.commit();
      writer_done = true;
    });

    // Два читателя работают по клонам общего снапшота.
    std::atomic<uint64_t> counts[2];
    std::vector<std::thread> readers;
    for (int r = 0; r < 2; ++r) {
      readers.emplace_back([&, r] {
        auto clone = base.clone();
        auto table = clone.open_map(nullptr);
        counts[r] = clone.get_map_stat(table).ms_entries;
        clone.abort();
      });
    }
    for (auto &t : readers)
      t.join();
    writer.join();
    base.abort();

    if (counts[0] != snapshot_size || counts[1] != snapshot_size) {
      std::cerr << "FAIL: readers saw different snapshots\n";
      return EXIT_FAILURE;
    }
    std::cout << "readers saw the same snapshot (" << snapshot_size << " keys)\n";

    {
      auto txn = env.start_read();
      auto table = txn.open_map(nullptr);
      std::cout << "after writer finished, total keys = " << txn.get_map_stat(table).ms_entries << "\n";
      txn.abort();
    }

    std::cout << "ok: config-store-11 works\n";
    return EXIT_SUCCESS;
  } catch (const std::exception &ex) {
    std::cerr << "error: " << ex.what() << "\n";
    return EXIT_FAILURE;
  }
}