mirror of
https://github.com/enduro2d/enduro2d.git
synced 2025-12-13 23:58:18 +07:00
remove invoke module. use jobber and scheduler from promise.hpp.
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -25,9 +25,6 @@
|
||||
[submodule "toolset/model_converter/modules/assimp"]
|
||||
path = toolset/model_converter/modules/assimp
|
||||
url = https://github.com/assimp/assimp
|
||||
[submodule "modules/invoke.hpp"]
|
||||
path = modules/invoke.hpp
|
||||
url = https://github.com/BlackMATov/invoke.hpp
|
||||
[submodule "modules/promise.hpp"]
|
||||
path = modules/promise.hpp
|
||||
url = https://github.com/BlackMATov/promise.hpp
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "invoke.hpp"
|
||||
* This file is part of the "https://github.com/blackmatov/invoke.hpp"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
2
headers/3rdparty/promise.hpp/jobber.hpp
vendored
2
headers/3rdparty/promise.hpp/jobber.hpp
vendored
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "promise.hpp"
|
||||
* This file is part of the "https://github.com/blackmatov/promise.hpp"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
251
headers/3rdparty/promise.hpp/promise.hpp
vendored
251
headers/3rdparty/promise.hpp/promise.hpp
vendored
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "promise.hpp"
|
||||
* This file is part of the "https://github.com/blackmatov/promise.hpp"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
@@ -23,254 +23,7 @@
|
||||
#include <type_traits>
|
||||
#include <condition_variable>
|
||||
|
||||
//
|
||||
// invoke.hpp
|
||||
// https://github.com/BlackMATov/invoke.hpp
|
||||
//
|
||||
|
||||
namespace promise_hpp
|
||||
{
|
||||
#define INVOKE_HPP_NOEXCEPT_RETURN(...) \
|
||||
noexcept(noexcept(__VA_ARGS__)) { return __VA_ARGS__; }
|
||||
|
||||
#define INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN(...) \
|
||||
noexcept(noexcept(__VA_ARGS__)) -> decltype (__VA_ARGS__) { return __VA_ARGS__; }
|
||||
|
||||
//
|
||||
// void_t
|
||||
//
|
||||
|
||||
namespace invoke_hpp
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
template < typename... Args >
|
||||
struct make_void {
|
||||
using type = void;
|
||||
};
|
||||
}
|
||||
|
||||
template < typename... Args >
|
||||
using void_t = typename impl::make_void<Args...>::type;
|
||||
}
|
||||
|
||||
//
|
||||
// is_reference_wrapper
|
||||
//
|
||||
|
||||
namespace invoke_hpp
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
template < typename T >
|
||||
struct is_reference_wrapper_impl
|
||||
: std::false_type {};
|
||||
|
||||
template < typename U >
|
||||
struct is_reference_wrapper_impl<std::reference_wrapper<U>>
|
||||
: std::true_type {};
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
struct is_reference_wrapper
|
||||
: impl::is_reference_wrapper_impl<std::remove_cv_t<T>> {};
|
||||
}
|
||||
|
||||
//
|
||||
// invoke
|
||||
//
|
||||
|
||||
namespace invoke_hpp
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
//
|
||||
// invoke_member_object_impl
|
||||
//
|
||||
|
||||
template
|
||||
<
|
||||
typename Base, typename F, typename Derived,
|
||||
typename std::enable_if_t<std::is_base_of<Base, std::decay_t<Derived>>::value, int> = 0
|
||||
>
|
||||
constexpr auto invoke_member_object_impl(F Base::* f, Derived&& ref)
|
||||
INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
||||
std::forward<Derived>(ref).*f)
|
||||
|
||||
template
|
||||
<
|
||||
typename Base, typename F, typename RefWrap,
|
||||
typename std::enable_if_t<is_reference_wrapper<std::decay_t<RefWrap>>::value, int> = 0
|
||||
>
|
||||
constexpr auto invoke_member_object_impl(F Base::* f, RefWrap&& ref)
|
||||
INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
||||
ref.get().*f)
|
||||
|
||||
template
|
||||
<
|
||||
typename Base, typename F, typename Pointer,
|
||||
typename std::enable_if_t<
|
||||
!std::is_base_of<Base, std::decay_t<Pointer>>::value &&
|
||||
!is_reference_wrapper<std::decay_t<Pointer>>::value
|
||||
, int> = 0
|
||||
>
|
||||
constexpr auto invoke_member_object_impl(F Base::* f, Pointer&& ptr)
|
||||
INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
||||
(*std::forward<Pointer>(ptr)).*f)
|
||||
|
||||
//
|
||||
// invoke_member_function_impl
|
||||
//
|
||||
|
||||
template
|
||||
<
|
||||
typename Base, typename F, typename Derived, typename... Args,
|
||||
typename std::enable_if_t<std::is_base_of<Base, std::decay_t<Derived>>::value, int> = 0
|
||||
>
|
||||
constexpr auto invoke_member_function_impl(F Base::* f, Derived&& ref, Args&&... args)
|
||||
INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
||||
(std::forward<Derived>(ref).*f)(std::forward<Args>(args)...))
|
||||
|
||||
template
|
||||
<
|
||||
typename Base, typename F, typename RefWrap, typename... Args,
|
||||
typename std::enable_if_t<is_reference_wrapper<std::decay_t<RefWrap>>::value, int> = 0
|
||||
>
|
||||
constexpr auto invoke_member_function_impl(F Base::* f, RefWrap&& ref, Args&&... args)
|
||||
INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
||||
(ref.get().*f)(std::forward<Args>(args)...))
|
||||
|
||||
template
|
||||
<
|
||||
typename Base, typename F, typename Pointer, typename... Args,
|
||||
typename std::enable_if_t<
|
||||
!std::is_base_of<Base, std::decay_t<Pointer>>::value &&
|
||||
!is_reference_wrapper<std::decay_t<Pointer>>::value
|
||||
, int> = 0
|
||||
>
|
||||
constexpr auto invoke_member_function_impl(F Base::* f, Pointer&& ptr, Args&&... args)
|
||||
INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
||||
((*std::forward<Pointer>(ptr)).*f)(std::forward<Args>(args)...))
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
typename F, typename... Args,
|
||||
typename std::enable_if_t<!std::is_member_pointer<std::decay_t<F>>::value, int> = 0
|
||||
>
|
||||
constexpr auto invoke(F&& f, Args&&... args)
|
||||
INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
||||
std::forward<F>(f)(std::forward<Args>(args)...))
|
||||
|
||||
template
|
||||
<
|
||||
typename F, typename T,
|
||||
typename std::enable_if_t<std::is_member_object_pointer<std::decay_t<F>>::value, int> = 0
|
||||
>
|
||||
constexpr auto invoke(F&& f, T&& t)
|
||||
INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
||||
impl::invoke_member_object_impl(std::forward<F>(f), std::forward<T>(t)))
|
||||
|
||||
template
|
||||
<
|
||||
typename F, typename... Args,
|
||||
typename std::enable_if_t<std::is_member_function_pointer<std::decay_t<F>>::value, int> = 0
|
||||
>
|
||||
constexpr auto invoke(F&& f, Args&&... args)
|
||||
INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
||||
impl::invoke_member_function_impl(std::forward<F>(f), std::forward<Args>(args)...))
|
||||
}
|
||||
|
||||
//
|
||||
// invoke_result
|
||||
//
|
||||
|
||||
namespace invoke_hpp
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
struct invoke_result_impl_tag {};
|
||||
|
||||
template < typename Void, typename F, typename... Args >
|
||||
struct invoke_result_impl {};
|
||||
|
||||
template < typename F, typename... Args >
|
||||
struct invoke_result_impl<void_t<invoke_result_impl_tag, decltype(invoke_hpp::invoke(std::declval<F>(), std::declval<Args>()...))>, F, Args...> {
|
||||
using type = decltype(invoke_hpp::invoke(std::declval<F>(), std::declval<Args>()...));
|
||||
};
|
||||
}
|
||||
|
||||
template < typename F, typename... Args >
|
||||
struct invoke_result
|
||||
: impl::invoke_result_impl<void, F, Args...> {};
|
||||
|
||||
template < typename F, typename... Args >
|
||||
using invoke_result_t = typename invoke_result<F, Args...>::type;
|
||||
}
|
||||
|
||||
//
|
||||
// is_invocable
|
||||
//
|
||||
|
||||
namespace invoke_hpp
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
struct is_invocable_r_impl_tag {};
|
||||
|
||||
template < typename Void, typename R, typename F, typename... Args >
|
||||
struct is_invocable_r_impl
|
||||
: std::false_type {};
|
||||
|
||||
template < typename R, typename F, typename... Args >
|
||||
struct is_invocable_r_impl<void_t<is_invocable_r_impl_tag, invoke_result_t<F, Args...>>, R, F, Args...>
|
||||
: std::conditional_t<
|
||||
std::is_void<R>::value,
|
||||
std::true_type,
|
||||
std::is_convertible<invoke_result_t<F, Args...>, R>> {};
|
||||
}
|
||||
|
||||
template < typename R, typename F, typename... Args >
|
||||
struct is_invocable_r
|
||||
: impl::is_invocable_r_impl<void, R, F, Args...> {};
|
||||
|
||||
template < typename F, typename... Args >
|
||||
using is_invocable = is_invocable_r<void, F, Args...>;
|
||||
}
|
||||
|
||||
//
|
||||
// apply
|
||||
//
|
||||
|
||||
namespace invoke_hpp
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
template < typename F, typename Tuple, std::size_t... I >
|
||||
constexpr decltype(auto) apply_impl(F&& f, Tuple&& args, std::index_sequence<I...>)
|
||||
INVOKE_HPP_NOEXCEPT_RETURN(
|
||||
invoke_hpp::invoke(
|
||||
std::forward<F>(f),
|
||||
std::get<I>(std::forward<Tuple>(args))...))
|
||||
}
|
||||
|
||||
template < typename F, typename Tuple >
|
||||
constexpr decltype(auto) apply(F&& f, Tuple&& args)
|
||||
INVOKE_HPP_NOEXCEPT_RETURN(
|
||||
impl::apply_impl(
|
||||
std::forward<F>(f),
|
||||
std::forward<Tuple>(args),
|
||||
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>()))
|
||||
}
|
||||
|
||||
#undef INVOKE_HPP_NOEXCEPT_RETURN
|
||||
#undef INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN
|
||||
}
|
||||
|
||||
//
|
||||
// promise.hpp
|
||||
// https://github.com/BlackMATov/promise.hpp
|
||||
//
|
||||
#include "invoke.hpp"
|
||||
|
||||
namespace promise_hpp
|
||||
{
|
||||
|
||||
2
headers/3rdparty/promise.hpp/scheduler.hpp
vendored
2
headers/3rdparty/promise.hpp/scheduler.hpp
vendored
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "promise.hpp"
|
||||
* This file is part of the "https://github.com/blackmatov/promise.hpp"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
#include <3rdparty/invoke.hpp/invoke.hpp>
|
||||
#include <3rdparty/promise.hpp/promise.hpp>
|
||||
#include <3rdparty/variant/variant.hpp>
|
||||
#include <3rdparty/promise.hpp/invoke.hpp>
|
||||
#include <3rdparty/promise.hpp/jobber.hpp>
|
||||
#include <3rdparty/promise.hpp/promise.hpp>
|
||||
#include <3rdparty/promise.hpp/scheduler.hpp>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "macros.hpp"
|
||||
|
||||
//
|
||||
// variant
|
||||
// https://github.com/mpark/variant
|
||||
//
|
||||
|
||||
namespace e2d { namespace stdex
|
||||
@@ -18,21 +18,15 @@ namespace e2d { namespace stdex
|
||||
}}
|
||||
|
||||
//
|
||||
// invoke
|
||||
// https://github.com/blackmatov/promise.hpp
|
||||
//
|
||||
|
||||
namespace e2d { namespace stdex
|
||||
{
|
||||
using namespace invoke_hpp;
|
||||
}}
|
||||
|
||||
//
|
||||
// promise
|
||||
//
|
||||
|
||||
namespace e2d { namespace stdex
|
||||
{
|
||||
using namespace jobber_hpp;
|
||||
using namespace promise_hpp;
|
||||
using namespace scheduler_hpp;
|
||||
}}
|
||||
|
||||
//
|
||||
|
||||
@@ -126,8 +126,8 @@ namespace e2d
|
||||
u32 frame_count() const noexcept;
|
||||
f32 realtime_time() const noexcept;
|
||||
|
||||
scheduler& main_thread_scheduler() noexcept;
|
||||
const scheduler& main_thread_scheduler() const noexcept;
|
||||
stdex::scheduler& main_thread_scheduler() noexcept;
|
||||
const stdex::scheduler& main_thread_scheduler() const noexcept;
|
||||
private:
|
||||
class internal_state;
|
||||
std::unique_ptr<internal_state> state_;
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace e2d
|
||||
input_stream_uptr open(const url& url) const;
|
||||
std::pair<buffer,bool> load(const url& url) const;
|
||||
output_stream_uptr write(const url& url, bool append) const;
|
||||
std::future<std::pair<buffer,bool>> load_async(const url& url) const;
|
||||
stdex::promise<std::pair<buffer,bool>> load_async(const url& url) const;
|
||||
|
||||
template < typename Iter >
|
||||
bool extract(const url& url, Iter result_iter) const;
|
||||
|
||||
@@ -14,11 +14,9 @@
|
||||
#include "filesystem.hpp"
|
||||
#include "filesystem.inl"
|
||||
#include "image.hpp"
|
||||
#include "jobber.hpp"
|
||||
#include "mesh.hpp"
|
||||
#include "module.hpp"
|
||||
#include "path.hpp"
|
||||
#include "scheduler.hpp"
|
||||
#include "streams.hpp"
|
||||
#include "strfmts.hpp"
|
||||
#include "strings.hpp"
|
||||
|
||||
@@ -15,9 +15,7 @@ namespace e2d
|
||||
class color;
|
||||
class color32;
|
||||
class image;
|
||||
class jobber;
|
||||
class mesh;
|
||||
class scheduler;
|
||||
class input_stream;
|
||||
class output_stream;
|
||||
class input_sequence;
|
||||
|
||||
@@ -1,237 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_utils.hpp"
|
||||
#include "time.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class jobber final : private noncopyable {
|
||||
public:
|
||||
enum class priority {
|
||||
lowest,
|
||||
below_normal,
|
||||
normal,
|
||||
above_normal,
|
||||
highest
|
||||
};
|
||||
public:
|
||||
explicit jobber(u32 threads);
|
||||
~jobber() noexcept;
|
||||
|
||||
template < typename F, typename... Args >
|
||||
using async_invoke_result_t = stdex::invoke_result_t<
|
||||
std::decay_t<F>,
|
||||
std::decay_t<Args>...>;
|
||||
|
||||
template < typename F, typename... Args
|
||||
, typename R = async_invoke_result_t<F, Args...> >
|
||||
std::future<R> async(F&& f, Args&&... args);
|
||||
|
||||
template < typename F, typename... Args
|
||||
, typename R = async_invoke_result_t<F, Args...> >
|
||||
std::future<R> async(priority priority, F&& f, Args&&... args);
|
||||
|
||||
void pause() noexcept;
|
||||
void resume() noexcept;
|
||||
bool is_paused() const noexcept;
|
||||
|
||||
void wait_all() const noexcept;
|
||||
void active_wait_all() noexcept;
|
||||
|
||||
template < typename T, typename TimeTag >
|
||||
void wait_for(const unit<T, TimeTag>& time_for) const noexcept;
|
||||
|
||||
template < typename T, typename TimeTag >
|
||||
void wait_until(const unit<T, TimeTag>& time_until) const noexcept;
|
||||
|
||||
template < typename T, typename TimeTag >
|
||||
void active_wait_for(const unit<T, TimeTag>& time_for) noexcept;
|
||||
|
||||
template < typename T, typename TimeTag >
|
||||
void active_wait_until(const unit<T, TimeTag>& time_until) noexcept;
|
||||
private:
|
||||
class task;
|
||||
using task_ptr = std::unique_ptr<task>;
|
||||
template < typename R, typename F, typename... Args >
|
||||
class concrete_task;
|
||||
private:
|
||||
void push_task_(priority priority, task_ptr task);
|
||||
task_ptr pop_task_() noexcept;
|
||||
void shutdown_() noexcept;
|
||||
void worker_main_() noexcept;
|
||||
void process_task_(std::unique_lock<std::mutex> lock) noexcept;
|
||||
private:
|
||||
vector<std::thread> threads_;
|
||||
vector<std::pair<priority, task_ptr>> tasks_;
|
||||
std::mutex tasks_mutex_;
|
||||
std::condition_variable cond_var_;
|
||||
std::atomic<bool> paused_{false};
|
||||
std::atomic<bool> cancelled_{false};
|
||||
std::atomic<size_t> active_task_count_{0};
|
||||
};
|
||||
|
||||
class jobber::task : private noncopyable {
|
||||
public:
|
||||
virtual ~task() noexcept = default;
|
||||
virtual void run() noexcept = 0;
|
||||
};
|
||||
|
||||
template < typename R, typename F, typename... Args >
|
||||
class jobber::concrete_task : public task {
|
||||
F f_;
|
||||
std::tuple<Args...> args_;
|
||||
std::promise<R> promise_;
|
||||
public:
|
||||
template < typename U >
|
||||
concrete_task(U&& u, std::tuple<Args...>&& args);
|
||||
void run() noexcept final;
|
||||
std::future<R> future() noexcept;
|
||||
};
|
||||
|
||||
template < typename F, typename... Args >
|
||||
class jobber::concrete_task<void, F, Args...> : public task {
|
||||
F f_;
|
||||
std::tuple<Args...> args_;
|
||||
std::promise<void> promise_;
|
||||
public:
|
||||
template < typename U >
|
||||
concrete_task(U&& u, std::tuple<Args...>&& args);
|
||||
void run() noexcept final;
|
||||
std::future<void> future() noexcept;
|
||||
};
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
//
|
||||
// async
|
||||
//
|
||||
|
||||
template < typename F, typename... Args, typename R >
|
||||
std::future<R> jobber::async(F&& f, Args&&... args) {
|
||||
return async(
|
||||
priority::normal,
|
||||
std::forward<F>(f),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template < typename F, typename... Args, typename R >
|
||||
std::future<R> jobber::async(priority priority, F&& f, Args&&... args) {
|
||||
using task_t = concrete_task<
|
||||
R,
|
||||
std::decay_t<F>,
|
||||
std::decay_t<Args>...>;
|
||||
std::unique_ptr<task_t> task = std::make_unique<task_t>(
|
||||
std::forward<F>(f),
|
||||
std::make_tuple(std::forward<Args>(args)...));
|
||||
std::future<R> future = task->future();
|
||||
std::lock_guard<std::mutex> guard(tasks_mutex_);
|
||||
push_task_(priority, std::move(task));
|
||||
return future;
|
||||
}
|
||||
|
||||
//
|
||||
// wait
|
||||
//
|
||||
|
||||
template < typename T, typename TimeTag >
|
||||
void jobber::wait_for(const unit<T, TimeTag>& time_for) const noexcept {
|
||||
if ( time_for.value > T(0) ) {
|
||||
wait_until(
|
||||
time::now<TimeTag, u64>() +
|
||||
time_for.template cast_to<u64>());
|
||||
}
|
||||
}
|
||||
|
||||
template < typename T, typename TimeTag >
|
||||
void jobber::wait_until(const unit<T, TimeTag>& time_until) const noexcept {
|
||||
while ( active_task_count_ ) {
|
||||
const auto time_now = time::now<TimeTag, u64>();
|
||||
if ( time_now >= time_until.template cast_to<u64>() ) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// active_wait
|
||||
//
|
||||
|
||||
template < typename T, typename TimeTag >
|
||||
void jobber::active_wait_for(const unit<T, TimeTag>& time_for) noexcept {
|
||||
if ( time_for.value > T(0) ) {
|
||||
active_wait_until(
|
||||
time::now<TimeTag, u64>() +
|
||||
time_for.template cast_to<u64>());
|
||||
}
|
||||
}
|
||||
|
||||
template < typename T, typename TimeTag >
|
||||
void jobber::active_wait_until(const unit<T, TimeTag>& time_until) noexcept {
|
||||
while ( active_task_count_ ) {
|
||||
const auto time_now = time::now<TimeTag, u64>();
|
||||
if ( time_now >= time_until.template cast_to<u64>() ) {
|
||||
break;
|
||||
}
|
||||
std::unique_lock<std::mutex> lock(tasks_mutex_);
|
||||
process_task_(std::move(lock));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// concrete_task<R, F, Args...>
|
||||
//
|
||||
|
||||
template < typename R, typename F, typename... Args >
|
||||
template < typename U >
|
||||
jobber::concrete_task<R, F, Args...>::concrete_task(U&& u, std::tuple<Args...>&& args)
|
||||
: f_(std::forward<U>(u))
|
||||
, args_(std::move(args)) {}
|
||||
|
||||
template < typename R, typename F, typename... Args >
|
||||
void jobber::concrete_task<R, F, Args...>::run() noexcept {
|
||||
try {
|
||||
R value = stdex::apply(std::move(f_), std::move(args_));
|
||||
promise_.set_value(std::move(value));
|
||||
} catch (...) {
|
||||
promise_.set_exception(std::current_exception());
|
||||
}
|
||||
}
|
||||
|
||||
template < typename R, typename F, typename... Args >
|
||||
std::future<R> jobber::concrete_task<R, F, Args...>::future() noexcept {
|
||||
return promise_.get_future();
|
||||
}
|
||||
|
||||
//
|
||||
// concrete_task<void, F, Args...>
|
||||
//
|
||||
|
||||
template < typename F, typename... Args >
|
||||
template < typename U >
|
||||
jobber::concrete_task<void, F, Args...>::concrete_task(U&& u, std::tuple<Args...>&& args)
|
||||
: f_(std::forward<U>(u))
|
||||
, args_(std::move(args)) {}
|
||||
|
||||
template < typename F, typename... Args >
|
||||
void jobber::concrete_task<void, F, Args...>::run() noexcept {
|
||||
try {
|
||||
stdex::apply(std::move(f_), std::move(args_));
|
||||
promise_.set_value();
|
||||
} catch (...) {
|
||||
promise_.set_exception(std::current_exception());
|
||||
}
|
||||
}
|
||||
|
||||
template < typename F, typename... Args >
|
||||
std::future<void> jobber::concrete_task<void, F, Args...>::future() noexcept {
|
||||
return promise_.get_future();
|
||||
}
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_utils.hpp"
|
||||
#include "time.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class scheduler final : private noncopyable {
|
||||
public:
|
||||
enum class priority {
|
||||
lowest,
|
||||
below_normal,
|
||||
normal,
|
||||
above_normal,
|
||||
highest
|
||||
};
|
||||
public:
|
||||
scheduler();
|
||||
~scheduler() noexcept;
|
||||
|
||||
template < typename F, typename... Args >
|
||||
using schedule_invoke_result_t = stdex::invoke_result_t<
|
||||
std::decay_t<F>,
|
||||
std::decay_t<Args>...>;
|
||||
|
||||
template < typename F, typename... Args
|
||||
, typename R = schedule_invoke_result_t<F, Args...> >
|
||||
std::future<R> schedule(F&& f, Args&&... args);
|
||||
|
||||
template < typename F, typename... Args
|
||||
, typename R = schedule_invoke_result_t<F, Args...> >
|
||||
std::future<R> schedule(priority priority, F&& f, Args&&... args);
|
||||
|
||||
void process_all_tasks() noexcept;
|
||||
|
||||
template < typename T, typename TimeTag >
|
||||
void process_tasks_for(const unit<T, TimeTag>& time_for) noexcept;
|
||||
|
||||
template < typename T, typename TimeTag >
|
||||
void process_tasks_until(const unit<T, TimeTag>& time_until) noexcept;
|
||||
private:
|
||||
class task;
|
||||
using task_ptr = std::unique_ptr<task>;
|
||||
template < typename R, typename F, typename... Args >
|
||||
class concrete_task;
|
||||
private:
|
||||
void push_task_(priority priority, task_ptr task);
|
||||
task_ptr pop_task_() noexcept;
|
||||
void process_task_(std::unique_lock<std::mutex> lock) noexcept;
|
||||
private:
|
||||
std::mutex tasks_mutex_;
|
||||
std::atomic<size_t> active_task_count_{0};
|
||||
vector<std::pair<priority, task_ptr>> tasks_;
|
||||
};
|
||||
|
||||
class scheduler::task : private noncopyable {
|
||||
public:
|
||||
virtual ~task() noexcept = default;
|
||||
virtual void run() noexcept = 0;
|
||||
};
|
||||
|
||||
template < typename R, typename F, typename... Args >
|
||||
class scheduler::concrete_task : public task {
|
||||
F f_;
|
||||
std::tuple<Args...> args_;
|
||||
std::promise<R> promise_;
|
||||
public:
|
||||
template < typename U >
|
||||
concrete_task(U&& u, std::tuple<Args...>&& args);
|
||||
void run() noexcept final;
|
||||
std::future<R> future() noexcept;
|
||||
};
|
||||
|
||||
template < typename F, typename... Args >
|
||||
class scheduler::concrete_task<void, F, Args...> : public task {
|
||||
F f_;
|
||||
std::tuple<Args...> args_;
|
||||
std::promise<void> promise_;
|
||||
public:
|
||||
template < typename U >
|
||||
concrete_task(U&& u, std::tuple<Args...>&& args);
|
||||
void run() noexcept final;
|
||||
std::future<void> future() noexcept;
|
||||
};
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
//
|
||||
// schedule
|
||||
//
|
||||
|
||||
template < typename F, typename... Args, typename R >
|
||||
std::future<R> scheduler::schedule(F&& f, Args&&... args) {
|
||||
return schedule(
|
||||
priority::normal,
|
||||
std::forward<F>(f),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template < typename F, typename... Args, typename R >
|
||||
std::future<R> scheduler::schedule(priority priority, F&& f, Args&&... args) {
|
||||
using task_t = concrete_task<
|
||||
R,
|
||||
std::decay_t<F>,
|
||||
std::decay_t<Args>...>;
|
||||
std::unique_ptr<task_t> task = std::make_unique<task_t>(
|
||||
std::forward<F>(f),
|
||||
std::make_tuple(std::forward<Args>(args)...));
|
||||
std::future<R> future = task->future();
|
||||
std::lock_guard<std::mutex> guard(tasks_mutex_);
|
||||
push_task_(priority, std::move(task));
|
||||
return future;
|
||||
}
|
||||
|
||||
//
|
||||
// process_all_tasks
|
||||
//
|
||||
|
||||
template < typename T, typename TimeTag >
|
||||
void scheduler::process_tasks_for(const unit<T, TimeTag>& time_for) noexcept {
|
||||
if ( time_for.value > T(0) ) {
|
||||
process_tasks_until(
|
||||
time::now<TimeTag, u64>() +
|
||||
time_for.template cast_to<u64>());
|
||||
}
|
||||
}
|
||||
|
||||
template < typename T, typename TimeTag >
|
||||
void scheduler::process_tasks_until(const unit<T, TimeTag>& time_until) noexcept {
|
||||
while ( active_task_count_ ) {
|
||||
const auto time_now = time::now<TimeTag, u64>();
|
||||
if ( time_now >= time_until.template cast_to<u64>() ) {
|
||||
break;
|
||||
}
|
||||
std::unique_lock<std::mutex> lock(tasks_mutex_);
|
||||
process_task_(std::move(lock));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// concrete_task<R, F, Args...>
|
||||
//
|
||||
|
||||
template < typename R, typename F, typename... Args >
|
||||
template < typename U >
|
||||
scheduler::concrete_task<R, F, Args...>::concrete_task(U&& u, std::tuple<Args...>&& args)
|
||||
: f_(std::forward<U>(u))
|
||||
, args_(std::move(args)) {}
|
||||
|
||||
template < typename R, typename F, typename... Args >
|
||||
void scheduler::concrete_task<R, F, Args...>::run() noexcept {
|
||||
try {
|
||||
R value = stdex::apply(std::move(f_), std::move(args_));
|
||||
promise_.set_value(std::move(value));
|
||||
} catch (...) {
|
||||
promise_.set_exception(std::current_exception());
|
||||
}
|
||||
}
|
||||
|
||||
template < typename R, typename F, typename... Args >
|
||||
std::future<R> scheduler::concrete_task<R, F, Args...>::future() noexcept {
|
||||
return promise_.get_future();
|
||||
}
|
||||
|
||||
//
|
||||
// concrete_task<void, F, Args...>
|
||||
//
|
||||
|
||||
template < typename F, typename... Args >
|
||||
template < typename U >
|
||||
scheduler::concrete_task<void, F, Args...>::concrete_task(U&& u, std::tuple<Args...>&& args)
|
||||
: f_(std::forward<U>(u))
|
||||
, args_(std::move(args)) {}
|
||||
|
||||
template < typename F, typename... Args >
|
||||
void scheduler::concrete_task<void, F, Args...>::run() noexcept {
|
||||
try {
|
||||
stdex::apply(std::move(f_), std::move(args_));
|
||||
promise_.set_value();
|
||||
} catch (...) {
|
||||
promise_.set_exception(std::current_exception());
|
||||
}
|
||||
}
|
||||
|
||||
template < typename F, typename... Args >
|
||||
std::future<void> scheduler::concrete_task<void, F, Args...>::future() noexcept {
|
||||
return promise_.get_future();
|
||||
}
|
||||
}
|
||||
Submodule modules/invoke.hpp deleted from de0183febe
@@ -22,10 +22,8 @@ git submodule update --remote --recursive
|
||||
mkdir -p $UNTESTS_DIR/catch
|
||||
cp -fv $MODULES_DIR/catch2/single_include/catch2/catch.hpp $UNTESTS_DIR/catch/catch.hpp
|
||||
|
||||
mkdir -p $HEADERS_RDPARTY_DIR/invoke.hpp
|
||||
cp -rfv $MODULES_DIR/invoke.hpp/invoke.hpp $HEADERS_RDPARTY_DIR/invoke.hpp/invoke.hpp
|
||||
|
||||
mkdir -p $HEADERS_RDPARTY_DIR/promise.hpp
|
||||
cp -rfv $MODULES_DIR/promise.hpp/invoke.hpp $HEADERS_RDPARTY_DIR/promise.hpp/invoke.hpp
|
||||
cp -rfv $MODULES_DIR/promise.hpp/jobber.hpp $HEADERS_RDPARTY_DIR/promise.hpp/jobber.hpp
|
||||
cp -rfv $MODULES_DIR/promise.hpp/promise.hpp $HEADERS_RDPARTY_DIR/promise.hpp/promise.hpp
|
||||
cp -rfv $MODULES_DIR/promise.hpp/scheduler.hpp $HEADERS_RDPARTY_DIR/promise.hpp/scheduler.hpp
|
||||
|
||||
@@ -251,11 +251,11 @@ namespace e2d
|
||||
return time::to_seconds(delta_us.cast_to<f32>()).value;
|
||||
}
|
||||
|
||||
scheduler& main_thread_scheduler() noexcept {
|
||||
stdex::scheduler& main_thread_scheduler() noexcept {
|
||||
return main_thread_scheduler_;
|
||||
}
|
||||
|
||||
const scheduler& main_thread_scheduler() const noexcept {
|
||||
const stdex::scheduler& main_thread_scheduler() const noexcept {
|
||||
return main_thread_scheduler_;
|
||||
}
|
||||
public:
|
||||
@@ -298,7 +298,7 @@ namespace e2d
|
||||
}
|
||||
private:
|
||||
timer_parameters timer_params_;
|
||||
scheduler main_thread_scheduler_;
|
||||
stdex::scheduler main_thread_scheduler_;
|
||||
microseconds<u64> init_time_{time::now_us<u64>()};
|
||||
microseconds<u64> prev_frame_time_{time::now_us<u64>()};
|
||||
microseconds<u64> prev_frame_rate_time_{time::now_us<u64>()};
|
||||
@@ -422,11 +422,11 @@ namespace e2d
|
||||
return state_->realtime_time();
|
||||
}
|
||||
|
||||
scheduler& engine::main_thread_scheduler() noexcept {
|
||||
stdex::scheduler& engine::main_thread_scheduler() noexcept {
|
||||
return state_->main_thread_scheduler();
|
||||
}
|
||||
|
||||
const scheduler& engine::main_thread_scheduler() const noexcept {
|
||||
const stdex::scheduler& engine::main_thread_scheduler() const noexcept {
|
||||
return state_->main_thread_scheduler();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace e2d
|
||||
class vfs::state final : private e2d::noncopyable {
|
||||
public:
|
||||
std::mutex mutex;
|
||||
jobber worker{1};
|
||||
stdex::jobber worker{1};
|
||||
hash_map<str, url> aliases;
|
||||
hash_map<str, file_source_uptr> schemes;
|
||||
public:
|
||||
@@ -164,7 +164,7 @@ namespace e2d
|
||||
}, output_stream_uptr());
|
||||
}
|
||||
|
||||
std::future<std::pair<buffer,bool>> vfs::load_async(const url& url) const {
|
||||
stdex::promise<std::pair<buffer,bool>> vfs::load_async(const url& url) const {
|
||||
return state_->worker.async([](input_stream_uptr stream){
|
||||
buffer buf;
|
||||
return streams::try_read_tail(buf, stream)
|
||||
@@ -196,7 +196,6 @@ namespace e2d
|
||||
using stream_ptr = std::shared_ptr<input_stream>;
|
||||
archive_ptr archive;
|
||||
stream_ptr stream;
|
||||
jobber worker{1};
|
||||
public:
|
||||
state(input_stream_uptr nstream)
|
||||
: archive(open_archive_(nstream))
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include <enduro2d/utils/jobber.hpp>
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
jobber::jobber(u32 threads) {
|
||||
try {
|
||||
threads_.resize(math::max(1u, threads));
|
||||
for ( std::thread& thread : threads_ ) {
|
||||
thread = std::thread(&jobber::worker_main_, this);
|
||||
}
|
||||
} catch (...) {
|
||||
shutdown_();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
jobber::~jobber() noexcept {
|
||||
shutdown_();
|
||||
}
|
||||
|
||||
void jobber::pause() noexcept {
|
||||
paused_.store(true);
|
||||
}
|
||||
|
||||
void jobber::resume() noexcept {
|
||||
paused_.store(false);
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(tasks_mutex_);
|
||||
cond_var_.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
bool jobber::is_paused() const noexcept {
|
||||
return paused_;
|
||||
}
|
||||
|
||||
void jobber::wait_all() const noexcept {
|
||||
while ( active_task_count_ ) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
void jobber::active_wait_all() noexcept {
|
||||
while ( active_task_count_ ) {
|
||||
std::unique_lock<std::mutex> lock(tasks_mutex_);
|
||||
process_task_(std::move(lock));
|
||||
}
|
||||
}
|
||||
|
||||
void jobber::push_task_(priority priority, task_ptr task) {
|
||||
tasks_.emplace_back(priority, std::move(task));
|
||||
std::push_heap(tasks_.begin(), tasks_.end());
|
||||
++active_task_count_;
|
||||
cond_var_.notify_one();
|
||||
}
|
||||
|
||||
jobber::task_ptr jobber::pop_task_() noexcept {
|
||||
if ( !tasks_.empty() ) {
|
||||
std::pop_heap(tasks_.begin(), tasks_.end());
|
||||
task_ptr task = std::move(tasks_.back().second);
|
||||
tasks_.pop_back();
|
||||
return task;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void jobber::shutdown_() noexcept {
|
||||
cancelled_.store(true);
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(tasks_mutex_);
|
||||
cond_var_.notify_all();
|
||||
}
|
||||
for ( std::thread& thread : threads_ ) {
|
||||
if ( thread.joinable() ) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void jobber::worker_main_() noexcept {
|
||||
for (;;) {
|
||||
std::unique_lock<std::mutex> lock(tasks_mutex_);
|
||||
cond_var_.wait(lock, [this](){
|
||||
return cancelled_ || (!paused_ && !tasks_.empty());
|
||||
});
|
||||
if ( cancelled_ ) {
|
||||
break;
|
||||
}
|
||||
process_task_(std::move(lock));
|
||||
}
|
||||
}
|
||||
|
||||
void jobber::process_task_(std::unique_lock<std::mutex> lock) noexcept {
|
||||
E2D_ASSERT(lock.owns_lock());
|
||||
task_ptr task = pop_task_();
|
||||
if ( task ) {
|
||||
lock.unlock();
|
||||
task->run();
|
||||
--active_task_count_;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include <enduro2d/utils/scheduler.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace e2d;
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
scheduler::scheduler() = default;
|
||||
scheduler::~scheduler() noexcept = default;
|
||||
|
||||
void scheduler::process_all_tasks() noexcept {
|
||||
while ( active_task_count_ ) {
|
||||
std::unique_lock<std::mutex> lock(tasks_mutex_);
|
||||
process_task_(std::move(lock));
|
||||
}
|
||||
}
|
||||
|
||||
void scheduler::push_task_(priority priority, task_ptr task) {
|
||||
tasks_.emplace_back(priority, std::move(task));
|
||||
std::push_heap(tasks_.begin(), tasks_.end());
|
||||
++active_task_count_;
|
||||
}
|
||||
|
||||
scheduler::task_ptr scheduler::pop_task_() noexcept {
|
||||
if ( !tasks_.empty() ) {
|
||||
std::pop_heap(tasks_.begin(), tasks_.end());
|
||||
task_ptr task = std::move(tasks_.back().second);
|
||||
tasks_.pop_back();
|
||||
return task;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void scheduler::process_task_(std::unique_lock<std::mutex> lock) noexcept {
|
||||
E2D_ASSERT(lock.owns_lock());
|
||||
task_ptr task = pop_task_();
|
||||
if ( task ) {
|
||||
lock.unlock();
|
||||
task->run();
|
||||
--active_task_count_;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "_utils.hpp"
|
||||
using namespace e2d;
|
||||
|
||||
TEST_CASE("jobber") {
|
||||
{
|
||||
jobber j(1);
|
||||
auto pv0 = j.async([](){
|
||||
throw exception();
|
||||
});
|
||||
REQUIRE_THROWS_AS(pv0.get(), exception);
|
||||
}
|
||||
{
|
||||
i32 v5 = 5;
|
||||
|
||||
jobber j(1);
|
||||
auto pv0 = j.async([](i32 v){
|
||||
REQUIRE(v == 5);
|
||||
throw exception();
|
||||
}, v5);
|
||||
REQUIRE_THROWS_AS(pv0.get(), exception);
|
||||
|
||||
auto pv1 = j.async([](int& v){
|
||||
REQUIRE(v == 5);
|
||||
return v != 5
|
||||
? 0
|
||||
: throw exception();
|
||||
}, std::ref(v5));
|
||||
REQUIRE_THROWS_AS(pv1.get(), exception);
|
||||
|
||||
auto pv3 = j.async([](int& v){
|
||||
v = 4;
|
||||
return v;
|
||||
}, std::ref(v5));
|
||||
REQUIRE(pv3.get() == v5);
|
||||
REQUIRE(v5 == 4);
|
||||
}
|
||||
{
|
||||
jobber j(1);
|
||||
auto p0 = j.async([](rad<f32> angle){
|
||||
return math::sin(angle);
|
||||
}, math::pi<f32>());
|
||||
auto p1 = j.async([](rad<f32> angle){
|
||||
return math::cos(angle);
|
||||
}, math::two_pi<f32>());
|
||||
REQUIRE(math::approximately(p0.get(), 0.f));
|
||||
REQUIRE(math::approximately(p1.get(), 1.f));
|
||||
}
|
||||
{
|
||||
jobber j(1);
|
||||
j.pause();
|
||||
jobber::priority max_priority = jobber::priority::highest;
|
||||
j.async([](){
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2));
|
||||
});
|
||||
for ( std::size_t i = 0; i < 10; ++i ) {
|
||||
jobber::priority p = static_cast<jobber::priority>(
|
||||
i % static_cast<std::size_t>(jobber::priority::highest));
|
||||
j.async(p, [&max_priority](jobber::priority priority) {
|
||||
REQUIRE(priority <= max_priority);
|
||||
max_priority = priority;
|
||||
}, p);
|
||||
}
|
||||
j.resume();
|
||||
j.wait_all();
|
||||
}
|
||||
{
|
||||
jobber j(1);
|
||||
i32 counter = 0;
|
||||
j.pause();
|
||||
for ( std::size_t i = 0; i < 50; ++i ) {
|
||||
j.async([&counter](){
|
||||
++counter;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
});
|
||||
}
|
||||
|
||||
const auto b = time::now_ms();
|
||||
|
||||
j.wait_for(make_milliseconds(15));
|
||||
REQUIRE(time::now_ms() - b > make_milliseconds<i64>(10));
|
||||
REQUIRE(counter == 0);
|
||||
|
||||
j.wait_until(time::now_ms() + make_milliseconds<i64>(15));
|
||||
REQUIRE(time::now_ms() - b > make_milliseconds<i64>(20));
|
||||
REQUIRE(counter == 0);
|
||||
|
||||
j.active_wait_for(make_milliseconds(60));
|
||||
REQUIRE(time::now_ms() - b > make_milliseconds<i64>(70));
|
||||
REQUIRE(counter > 2);
|
||||
REQUIRE(counter < 50);
|
||||
|
||||
j.active_wait_until(time::now_s() + make_seconds<i64>(3));
|
||||
REQUIRE(counter == 50);
|
||||
}
|
||||
{
|
||||
jobber j(2);
|
||||
jobber g(2);
|
||||
|
||||
std::vector<std::future<f32>> jp(50);
|
||||
for ( std::size_t i = 0; i < jp.size(); ++i ) {
|
||||
jp[i] = j.async([&g](){
|
||||
std::vector<std::future<f32>> gp(50);
|
||||
for ( std::size_t ii = 0; ii < gp.size(); ++ii ) {
|
||||
gp[ii] = g.async([](rad<f32> angle){
|
||||
return math::sin(angle);
|
||||
}, make_rad(ii).cast_to<f32>());
|
||||
}
|
||||
return std::accumulate(gp.begin(), gp.end(), 0.f,
|
||||
[](f32 r, std::future<f32>& f){
|
||||
return r + f.get();
|
||||
});
|
||||
});
|
||||
}
|
||||
f32 r0 = std::accumulate(jp.begin(), jp.end(), 0.f,
|
||||
[](f32 r, std::future<f32>& f){
|
||||
return r + f.get();
|
||||
});
|
||||
f32 r1 = 0.f;
|
||||
for ( std::size_t i = 0; i < 50; ++i ) {
|
||||
r1 += math::sin(make_rad(i).cast_to<f32>());
|
||||
}
|
||||
REQUIRE(math::approximately(r0, r1 * 50.f));
|
||||
}
|
||||
SECTION("performance") {
|
||||
std::printf("-= jobber::performance tests =-\n");
|
||||
#if defined(E2D_BUILD_MODE) && E2D_BUILD_MODE == E2D_BUILD_MODE_DEBUG
|
||||
const std::size_t task_n = 100'000;
|
||||
#else
|
||||
const std::size_t task_n = 1'000'000;
|
||||
#endif
|
||||
const auto big_task = [](std::size_t b, std::size_t n){
|
||||
f32 result = 0.f;
|
||||
for ( std::size_t i = 0; i < n; ++i ) {
|
||||
result += math::sin(make_rad(b + i).cast_to<f32>());
|
||||
result += math::cos(make_rad(b + i).cast_to<f32>());
|
||||
}
|
||||
return result;
|
||||
};
|
||||
const auto multi_task = [&](u32 threads, std::size_t tasks){
|
||||
e2d_untests::verbose_profiler_ms p(
|
||||
str("cores: ") + std::to_string(threads) +
|
||||
str(", tasks: ") + std::to_string(tasks));
|
||||
jobber j(threads);
|
||||
std::vector<std::future<f32>> rs(tasks);
|
||||
for ( std::size_t i = 0; i < tasks; ++i ) {
|
||||
const std::size_t n = task_n / tasks;
|
||||
rs[i] = j.async(big_task, i * n, n);
|
||||
}
|
||||
f32 result = std::accumulate(rs.begin(), rs.end(), 0.f,
|
||||
[](f32 r, std::future<f32>& f){
|
||||
return r + f.get();
|
||||
});
|
||||
p.done(result);
|
||||
};
|
||||
for ( u32 i = 1; i <= 4; ++i ) {
|
||||
multi_task(i, 10);
|
||||
multi_task(i, 100);
|
||||
multi_task(i, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "_utils.hpp"
|
||||
using namespace e2d;
|
||||
|
||||
TEST_CASE("scheduler") {
|
||||
{
|
||||
scheduler s;
|
||||
i32 counter = 0;
|
||||
s.schedule([&counter](){ ++counter; });
|
||||
REQUIRE(counter == 0);
|
||||
s.process_all_tasks();
|
||||
REQUIRE(counter == 1);
|
||||
s.schedule([&counter](){ ++counter; });
|
||||
REQUIRE(counter == 1);
|
||||
s.process_all_tasks();
|
||||
REQUIRE(counter == 2);
|
||||
}
|
||||
{
|
||||
scheduler s;
|
||||
i32 counter = 0;
|
||||
for ( std::size_t i = 0; i < 50; ++i ) {
|
||||
s.schedule([&counter](){
|
||||
++counter;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
});
|
||||
}
|
||||
s.process_tasks_for(make_milliseconds(-1));
|
||||
s.process_tasks_for(make_milliseconds(0));
|
||||
REQUIRE(counter == 0);
|
||||
s.process_tasks_for(make_milliseconds(60));
|
||||
REQUIRE(counter > 2);
|
||||
REQUIRE(counter < 50);
|
||||
s.process_tasks_for(make_seconds(3));
|
||||
REQUIRE(counter == 50);
|
||||
}
|
||||
{
|
||||
scheduler s;
|
||||
i32 counter = 0;
|
||||
for ( std::size_t i = 0; i < 50; ++i ) {
|
||||
s.schedule([&counter](){
|
||||
++counter;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
});
|
||||
}
|
||||
s.process_tasks_until(time::now_ms() - make_milliseconds<i64>(1));
|
||||
s.process_tasks_until(time::now_ms());
|
||||
REQUIRE(counter == 0);
|
||||
s.process_tasks_until(time::now_ms() + make_milliseconds<i64>(60));
|
||||
REQUIRE(counter > 2);
|
||||
REQUIRE(counter < 50);
|
||||
s.process_tasks_until(time::now_s() + make_seconds<i64>(3));
|
||||
REQUIRE(counter == 50);
|
||||
}
|
||||
{
|
||||
scheduler s;
|
||||
str accumulator;
|
||||
s.schedule(scheduler::priority::lowest, [](str& acc){
|
||||
acc.append("o");
|
||||
}, std::ref(accumulator));
|
||||
s.schedule(scheduler::priority::below_normal, [](str& acc){
|
||||
acc.append("l");
|
||||
}, std::ref(accumulator));
|
||||
s.schedule(scheduler::priority::highest, [](str& acc){
|
||||
acc.append("h");
|
||||
}, std::ref(accumulator));
|
||||
s.schedule(scheduler::priority::above_normal, [](str& acc){
|
||||
acc.append("e");
|
||||
}, std::ref(accumulator));
|
||||
s.schedule(scheduler::priority::normal, [](str& acc){
|
||||
acc.append("l");
|
||||
}, std::ref(accumulator));
|
||||
s.process_all_tasks();
|
||||
REQUIRE(accumulator == "hello");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user