150 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			150 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // No copyright
 | |
| #pragma once
 | |
| 
 | |
| #include "core/luajit/context.hh"
 | |
| 
 | |
| #include <gtest/gtest.h>
 | |
| 
 | |
| #include <chrono>
 | |
| #include <cstdlib>
 | |
| #include <iostream>
 | |
| #include <memory>
 | |
| #include <thread>
 | |
| #include <utility>
 | |
| 
 | |
| #include "iface/common/exception.hh"
 | |
| #include "iface/common/task.hh"
 | |
| #include "iface/subsys/concurrency.hh"
 | |
| #include "iface/subsys/parallelism.hh"
 | |
| #include "iface/env.hh"
 | |
| 
 | |
| namespace nf7::core::luajit::test {
 | |
| 
 | |
| class ContextFixture : public ::testing::TestWithParam<Context::Kind> {
 | |
|  private:
 | |
|   class AsyncDriver final {
 | |
|    public:
 | |
|     explicit AsyncDriver(ContextFixture& parent) noexcept : parent_(parent) { }
 | |
| 
 | |
|     void BeginBusy() noexcept { async_busy_ = true; }
 | |
|     void EndBusy() noexcept {
 | |
|       async_busy_ = false;
 | |
|       async_busy_.notify_all();
 | |
|     }
 | |
|     void Drive(AsyncTask&& task) noexcept {
 | |
|       try {
 | |
|         task(param_);
 | |
|       } catch (const Exception& e) {
 | |
|         std::cerr
 | |
|             << "unexpected exception while async task execution: " << e.what()
 | |
|             << std::endl;
 | |
|         std::abort();
 | |
|       }
 | |
|     }
 | |
|     AsyncTask::Time tick() const noexcept {
 | |
|       const auto now = std::chrono::system_clock::now();
 | |
|       return std::chrono::time_point_cast<AsyncTask::Time::duration>(now);
 | |
|     }
 | |
|     bool nextIdleInterruption() const noexcept { return !parent_.alive_; }
 | |
|     bool nextTaskInterruption() const noexcept { return false; }
 | |
| 
 | |
|     void Wait() { async_busy_.wait(true); }
 | |
| 
 | |
|    private:
 | |
|     ContextFixture& parent_;
 | |
|     AsyncTaskContext param_;
 | |
| 
 | |
|     std::atomic<bool> async_busy_ = false;
 | |
|   };
 | |
| 
 | |
|   class SyncDriver final {
 | |
|    public:
 | |
|     void BeginBusy() noexcept { }
 | |
|     void EndBusy() noexcept { interrupt_ = true; }
 | |
|     void Drive(SyncTask&& task) noexcept {
 | |
|       try {
 | |
|         task(param_);
 | |
|       } catch (const Exception& e) {
 | |
|         std::cerr
 | |
|             << "unexpected exception while sync task execution: " << e.what()
 | |
|             << std::endl;
 | |
|         std::abort();
 | |
|       }
 | |
|     }
 | |
|     SyncTask::Time tick() const noexcept {
 | |
|       const auto now = std::chrono::system_clock::now();
 | |
|       return std::chrono::time_point_cast<SyncTask::Time::duration>(now);
 | |
|     }
 | |
|     bool nextIdleInterruption() const noexcept { return interrupt_; }
 | |
|     bool nextTaskInterruption() const noexcept { return false; }
 | |
| 
 | |
|    private:
 | |
|     bool interrupt_ = false;
 | |
|     SyncTaskContext param_;
 | |
|   };
 | |
| 
 | |
|  public:
 | |
|   ContextFixture() noexcept : async_driver_(*this) { }
 | |
| 
 | |
|  protected:
 | |
|   void SetUp() override {
 | |
|     syncq_  = std::make_shared<SimpleTaskQueue<SyncTask>>();
 | |
|     asyncq_ = std::make_shared<SimpleTaskQueue<AsyncTask>>();
 | |
|     env_.emplace(SimpleEnv::FactoryMap {
 | |
|       {
 | |
|         typeid(subsys::Concurrency), [this](auto&) {
 | |
|           return std::make_shared<
 | |
|               WrappedTaskQueue<subsys::Concurrency>>(syncq_);
 | |
|         },
 | |
|       },
 | |
|       {
 | |
|         typeid(subsys::Parallelism), [this](auto&) {
 | |
|           return std::make_shared<
 | |
|               WrappedTaskQueue<subsys::Parallelism>>(asyncq_);
 | |
|         },
 | |
|       },
 | |
|     });
 | |
|     thread_ = std::thread {[this]() { asyncq_->Drive(async_driver_); }};
 | |
|   }
 | |
|   void TearDown() override {
 | |
|     ConsumeTasks();
 | |
|     env_ = std::nullopt;
 | |
| 
 | |
|     WaitAsyncTasks(std::chrono::seconds(3));
 | |
|     alive_ = false;
 | |
|     asyncq_->Wake();
 | |
|     thread_.join();
 | |
| 
 | |
|     asyncq_ = nullptr;
 | |
|     syncq_  = nullptr;
 | |
|   }
 | |
| 
 | |
|   void ConsumeTasks() noexcept {
 | |
|     SyncDriver sync_driver;
 | |
|     syncq_->Drive(sync_driver);
 | |
| 
 | |
|     WaitAsyncTasks(std::chrono::seconds(1));
 | |
|   }
 | |
|   void WaitAsyncTasks(auto dur) noexcept {
 | |
|     if (!asyncq_->WaitForEmpty(dur)) {
 | |
|       std::cerr << "timeout while waiting for task execution" << std::endl;
 | |
|       std::abort();
 | |
|     }
 | |
|     async_driver_.Wait();
 | |
|   }
 | |
| 
 | |
|  protected:
 | |
|   std::shared_ptr<SimpleTaskQueue<SyncTask>> syncq_;
 | |
|   std::shared_ptr<SimpleTaskQueue<AsyncTask>> asyncq_;
 | |
|   std::optional<SimpleEnv> env_;
 | |
| 
 | |
|  private:
 | |
|   std::atomic<bool> alive_ = true;
 | |
|   uint32_t async_cycle_ = 0;
 | |
| 
 | |
|   std::thread thread_;
 | |
|   AsyncDriver async_driver_;
 | |
| };
 | |
| 
 | |
| }  // namespace nf7::core::luajit::test
 |