See More

/** * * @file JobSystem.cpp * @author Gaspard Kirira * * Copyright 2026, Gaspard Kirira. * All rights reserved. * https://github.com/vixcpp/game * * Use of this source code is governed by a MIT license * that can be found in the LICENSE file. * * Vix.cpp * */ #include #include #include namespace vix::game { JobSystem::JobSystem() : JobSystem(std::thread::hardware_concurrency()) { } JobSystem::JobSystem(std::size_t worker_count) : events_(nullptr), worker_count_(worker_count == 0 ? 1 : worker_count), pool_(nullptr), shutdown_done_(false) { } JobSystem::~JobSystem() { shutdown(); } void JobSystem::set_event_bus(EventBus *bus) noexcept { events_ = bus; } void JobSystem::set_runtime_dispatcher(RuntimeDispatcher *dispatcher) noexcept { dispatcher_ = dispatcher; } GameBoolResult JobSystem::start() { if (shutdown_done_) { return make_game_error( GameErrorCode::InvalidState, "job system has been shut down and cannot be restarted"); } if (pool_) { return true; } pool_ = std::make_unique<:threadpool::threadpool>(make_config()); return true; } void JobSystem::stop() noexcept { shutdown(); } void JobSystem::shutdown() noexcept { if (shutdown_done_) { return; } if (pool_) { pool_->shutdown(); pool_.reset(); } shutdown_done_ = true; } bool JobSystem::running() const noexcept { return pool_ != nullptr && pool_->running(); } GameResult JobSystem::submit( Job job, JobPriority priority) { if (!job) { return make_game_error( GameErrorCode::InvalidArgument, "job cannot be empty"); } auto started = start(); if (!started) { return started.error(); } auto options = make_options(priority, false); try { const JobId id = pool_->next_task_id(); auto handle = pool_->handle_with_id( id, [this, id, job = std::move(job)]() mutable { execute_job(std::move(job), id); }, options); dispatch_job_event(EventType::JobStarted, id); return JobHandle(std::move(handle)); } catch (...) { return make_game_error( GameErrorCode::JobRejected, "failed to submit job"); } } GameBoolResult JobSystem::submit_detached( Job job, JobPriority priority) { if (!job) { return make_game_error( GameErrorCode::InvalidArgument, "job cannot be empty"); } auto started = start(); if (!started) { return started.error(); } auto options = make_options(priority, true); try { const JobId id = pool_->next_task_id(); auto result = pool_->post( [this, id, job = std::move(job)]() mutable { execute_job(std::move(job), id); }, options); if (!result) { return make_game_error( GameErrorCode::JobRejected, "failed to submit detached job"); } dispatch_job_event(EventType::JobStarted, id); return true; } catch (...) { return make_game_error( GameErrorCode::JobRejected, "failed to submit detached job"); } } vix::threadpool::ThreadPoolStats JobSystem::stats() const { if (!pool_) { return {}; } return pool_->stats(); } std::size_t JobSystem::worker_count() const noexcept { return worker_count_; } vix::threadpool::ThreadPool &JobSystem::native() { if (!pool_) { throw std::logic_error("job system is not running; call start() before native()"); } return *pool_; } const vix::threadpool::ThreadPool &JobSystem::native() const { if (!pool_) { throw std::logic_error("job system is not running; call start() before native()"); } return *pool_; } vix::threadpool::ThreadPoolConfig JobSystem::make_config() const { vix::threadpool::ThreadPoolConfig config; config.thread_count = worker_count_; config.max_thread_count = worker_count_; return config.normalized(); } vix::threadpool::TaskOptions JobSystem::make_options( JobPriority priority, bool detached) const { vix::threadpool::TaskOptions options; options.set_priority(to_task_priority(priority)); options.set_detached(detached); return options; } void JobSystem::execute_job(Job job, JobId id) { try { job(); dispatch_job_event(EventType::JobCompleted, id); } catch (...) { dispatch_job_event(EventType::JobFailed, id); throw; } } void JobSystem::dispatch_job_event(EventType type, JobId id) { if (!events_) { return; } auto dispatch = [events = events_, type, id]() { Event event(type); event.set_source("job_system"); if (is_valid_job_id(id)) { event.set_target(std::to_string(id)); event.set_field("job_id", std::to_string(id)); } (void)events->dispatch(std::move(event)); }; if (dispatcher_) { // During shutdown close() rejects this hand-off; EventBus then cannot be // touched after its runtime lifetime has ended. (void)dispatcher_->post(std::move(dispatch)); return; } // Standalone JobSystem keeps its historical immediate-event behavior. dispatch(); } } // namespace vix::game