fix async redefined
This commit is contained in:
parent
0c6c0cfbd1
commit
cf8ee4a243
@ -19,7 +19,7 @@
|
|||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
#ifndef ASYNCXX_H_
|
#ifndef ASYNCXX_H_
|
||||||
# error "Do not include this header directly, include <async++.h> instead."
|
#error "Do not include this header directly, include <async++.h> instead."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace async {
|
namespace async {
|
||||||
@ -31,54 +31,51 @@ LIBASYNC_EXPORT std::size_t hardware_concurrency() LIBASYNC_NOEXCEPT;
|
|||||||
|
|
||||||
// Task handle used by a wait handler
|
// Task handle used by a wait handler
|
||||||
class task_wait_handle {
|
class task_wait_handle {
|
||||||
detail::task_base* handle;
|
detail::task_base *handle;
|
||||||
|
|
||||||
// Allow construction in wait_for_task()
|
// Allow construction in wait_for_task()
|
||||||
friend LIBASYNC_EXPORT void detail::wait_for_task(detail::task_base* t);
|
friend void detail::wait_for_task(detail::task_base *t);
|
||||||
task_wait_handle(detail::task_base* t)
|
friend void detail::generic_wait_for_task(detail::task_base *wait_task);
|
||||||
: handle(t) {}
|
|
||||||
|
|
||||||
// Execution function for use by wait handlers
|
task_wait_handle(detail::task_base *t) : handle(t) {}
|
||||||
template<typename Func>
|
|
||||||
struct wait_exec_func: private detail::func_base<Func> {
|
// Execution function for use by wait handlers
|
||||||
template<typename F>
|
template<typename Func>
|
||||||
explicit wait_exec_func(F&& f)
|
struct wait_exec_func : private detail::func_base<Func> {
|
||||||
: detail::func_base<Func>(std::forward<F>(f)) {}
|
template<typename F>
|
||||||
void operator()(detail::task_base*)
|
explicit wait_exec_func(F &&f) : detail::func_base<Func>(std::forward<F>(f))
|
||||||
{
|
{}
|
||||||
// Just call the function directly, all this wrapper does is remove
|
|
||||||
// the task_base* parameter.
|
void operator()(detail::task_base *)
|
||||||
this->get_func()();
|
{
|
||||||
}
|
// Just call the function directly, all this wrapper does is remove
|
||||||
};
|
// the task_base* parameter.
|
||||||
|
this->get_func()();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
task_wait_handle()
|
task_wait_handle() : handle(nullptr) {}
|
||||||
: handle(nullptr) {}
|
|
||||||
|
|
||||||
// Check if the handle is valid
|
// Check if the handle is valid
|
||||||
explicit operator bool() const
|
explicit operator bool() const { return handle != nullptr; }
|
||||||
{
|
|
||||||
return handle != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the task has finished executing
|
// Check if the task has finished executing
|
||||||
bool ready() const
|
bool ready() const { return detail::is_finished(handle->state.load(std::memory_order_acquire)); }
|
||||||
{
|
|
||||||
return detail::is_finished(handle->state.load(std::memory_order_acquire));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Queue a function to be executed when the task has finished executing.
|
// Queue a function to be executed when the task has finished executing.
|
||||||
template<typename Func>
|
template<typename Func>
|
||||||
void on_finish(Func&& func)
|
void on_finish(Func &&func)
|
||||||
{
|
{
|
||||||
// Make sure the function type is callable
|
// Make sure the function type is callable
|
||||||
static_assert(detail::is_callable<Func()>::value, "Invalid function type passed to on_finish()");
|
static_assert(detail::is_callable<Func()>::value, "Invalid function type passed to on_finish()");
|
||||||
|
|
||||||
auto cont = new detail::task_func<typename std::remove_reference<decltype(inline_scheduler())>::type, wait_exec_func<typename std::decay<Func>::type>, detail::fake_void>(std::forward<Func>(func));
|
auto cont = new detail::task_func<typename std::remove_reference<decltype(inline_scheduler())>::type,
|
||||||
cont->sched = std::addressof(inline_scheduler());
|
wait_exec_func<typename std::decay<Func>::type>,
|
||||||
handle->add_continuation(inline_scheduler(), detail::task_ptr(cont));
|
detail::fake_void>(std::forward<Func>(func));
|
||||||
}
|
cont->sched = std::addressof(inline_scheduler());
|
||||||
|
handle->add_continuation(inline_scheduler(), detail::task_ptr(cont));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Wait handler function prototype
|
// Wait handler function prototype
|
||||||
@ -95,81 +92,78 @@ struct LIBASYNC_EXPORT_EXCEPTION task_not_executed {};
|
|||||||
|
|
||||||
// Task handle used in scheduler, acts as a unique_ptr to a task object
|
// Task handle used in scheduler, acts as a unique_ptr to a task object
|
||||||
class task_run_handle {
|
class task_run_handle {
|
||||||
detail::task_ptr handle;
|
detail::task_ptr handle;
|
||||||
|
|
||||||
// Allow construction in schedule_task()
|
// Allow construction in schedule_task()
|
||||||
template<typename Sched>
|
template<typename Sched>
|
||||||
friend void detail::schedule_task(Sched& sched, detail::task_ptr t);
|
friend void detail::schedule_task(Sched &sched, detail::task_ptr t);
|
||||||
explicit task_run_handle(detail::task_ptr t)
|
|
||||||
: handle(std::move(t)) {}
|
explicit task_run_handle(detail::task_ptr t) : handle(std::move(t)) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Movable but not copyable
|
// Movable but not copyable
|
||||||
task_run_handle() = default;
|
task_run_handle() = default;
|
||||||
task_run_handle(task_run_handle&& other) LIBASYNC_NOEXCEPT
|
|
||||||
: handle(std::move(other.handle)) {}
|
|
||||||
task_run_handle& operator=(task_run_handle&& other) LIBASYNC_NOEXCEPT
|
|
||||||
{
|
|
||||||
handle = std::move(other.handle);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the task is not executed, cancel it with an exception
|
task_run_handle(task_run_handle &&other) LIBASYNC_NOEXCEPT : handle(std::move(other.handle)) {}
|
||||||
~task_run_handle()
|
|
||||||
{
|
|
||||||
if (handle)
|
|
||||||
handle->vtable->cancel(handle.get(), std::make_exception_ptr(task_not_executed()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the handle is valid
|
task_run_handle &operator=(task_run_handle &&other) LIBASYNC_NOEXCEPT
|
||||||
explicit operator bool() const
|
{
|
||||||
{
|
handle = std::move(other.handle);
|
||||||
return handle != nullptr;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the task and release the handle
|
// If the task is not executed, cancel it with an exception
|
||||||
void run()
|
~task_run_handle()
|
||||||
{
|
{
|
||||||
handle->vtable->run(handle.get());
|
if (handle) handle->vtable->cancel(handle.get(), std::make_exception_ptr(task_not_executed()));
|
||||||
handle = nullptr;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Run the task but run the given wait handler when waiting for a task,
|
// Check if the handle is valid
|
||||||
// instead of just sleeping.
|
explicit operator bool() const { return handle != nullptr; }
|
||||||
void run_with_wait_handler(wait_handler handler)
|
|
||||||
{
|
|
||||||
wait_handler old = set_thread_wait_handler(handler);
|
|
||||||
run();
|
|
||||||
set_thread_wait_handler(old);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conversion to and from void pointer. This allows the task handle to be
|
// Run the task and release the handle
|
||||||
// sent through C APIs which don't preserve types.
|
void run()
|
||||||
void* to_void_ptr()
|
{
|
||||||
{
|
handle->vtable->run(handle.get());
|
||||||
return handle.release();
|
handle = nullptr;
|
||||||
}
|
}
|
||||||
static task_run_handle from_void_ptr(void* ptr)
|
|
||||||
{
|
// Run the task but run the given wait handler when waiting for a task,
|
||||||
return task_run_handle(detail::task_ptr(static_cast<detail::task_base*>(ptr)));
|
// instead of just sleeping.
|
||||||
}
|
void run_with_wait_handler(wait_handler handler)
|
||||||
|
{
|
||||||
|
wait_handler old = set_thread_wait_handler(handler);
|
||||||
|
run();
|
||||||
|
set_thread_wait_handler(old);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conversion to and from void pointer. This allows the task handle to be
|
||||||
|
// sent through C APIs which don't preserve types.
|
||||||
|
void *to_void_ptr() { return handle.release(); }
|
||||||
|
|
||||||
|
static task_run_handle from_void_ptr(void *ptr)
|
||||||
|
{
|
||||||
|
return task_run_handle(detail::task_ptr(static_cast<detail::task_base *>(ptr)));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
// Schedule a task for execution using its scheduler
|
// Schedule a task for execution using its scheduler
|
||||||
template<typename Sched>
|
template<typename Sched>
|
||||||
void schedule_task(Sched& sched, task_ptr t)
|
void
|
||||||
|
schedule_task(Sched &sched, task_ptr t)
|
||||||
{
|
{
|
||||||
static_assert(is_scheduler<Sched>::value, "Type is not a valid scheduler");
|
static_assert(is_scheduler<Sched>::value, "Type is not a valid scheduler");
|
||||||
sched.schedule(task_run_handle(std::move(t)));
|
sched.schedule(task_run_handle(std::move(t)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inline scheduler implementation
|
// Inline scheduler implementation
|
||||||
inline void inline_scheduler_impl::schedule(task_run_handle t)
|
inline void
|
||||||
|
inline_scheduler_impl::schedule(task_run_handle t)
|
||||||
{
|
{
|
||||||
t.run();
|
t.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace detail
|
}// namespace detail
|
||||||
} // namespace async
|
}// namespace async
|
||||||
|
@ -63,10 +63,15 @@ typedef ref_count_ptr<task_base> task_ptr;
|
|||||||
template<typename Sched>
|
template<typename Sched>
|
||||||
void schedule_task(Sched &sched, task_ptr t);
|
void schedule_task(Sched &sched, task_ptr t);
|
||||||
|
|
||||||
|
void generic_wait_for_task(task_base *wait_task);
|
||||||
// Wait for the given task to finish. This will call the wait handler currently
|
// Wait for the given task to finish. This will call the wait handler currently
|
||||||
// active for this thread, which causes the thread to sleep by default.
|
// active for this thread, which causes the thread to sleep by default.
|
||||||
#ifndef LIBASYNC_CUSTOM_WAIT_FOR_TASK
|
#ifndef LIBASYNC_CUSTOM_WAIT_FOR_TASK
|
||||||
LIBASYNC_EXPORT void wait_for_task(task_base *wait_task);
|
inline void
|
||||||
|
wait_for_task(task_base *wait_task)
|
||||||
|
{
|
||||||
|
generic_wait_for_task(wait_task);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Forward-declaration for data used by threadpool_scheduler
|
// Forward-declaration for data used by threadpool_scheduler
|
||||||
|
@ -22,226 +22,233 @@
|
|||||||
|
|
||||||
// for pthread thread_local emulation
|
// for pthread thread_local emulation
|
||||||
#if defined(EMULATE_PTHREAD_THREAD_LOCAL)
|
#if defined(EMULATE_PTHREAD_THREAD_LOCAL)
|
||||||
# include <pthread.h>
|
#include <pthread.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace async {
|
namespace async {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
void* aligned_alloc(std::size_t size, std::size_t align)
|
void *
|
||||||
|
aligned_alloc(std::size_t size, std::size_t align)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
void* ptr = _aligned_malloc(size, align);
|
void *ptr = _aligned_malloc(size, align);
|
||||||
if (!ptr)
|
if (!ptr) LIBASYNC_THROW(std::bad_alloc());
|
||||||
LIBASYNC_THROW(std::bad_alloc());
|
return ptr;
|
||||||
return ptr;
|
|
||||||
#else
|
#else
|
||||||
void* result;
|
void *result;
|
||||||
if (posix_memalign(&result, align, size))
|
if (posix_memalign(&result, align, size))
|
||||||
LIBASYNC_THROW(std::bad_alloc());
|
LIBASYNC_THROW(std::bad_alloc());
|
||||||
else
|
else
|
||||||
return result;
|
return result;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void aligned_free(void* addr) LIBASYNC_NOEXCEPT
|
void
|
||||||
|
aligned_free(void *addr) LIBASYNC_NOEXCEPT
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
_aligned_free(addr);
|
_aligned_free(addr);
|
||||||
#else
|
#else
|
||||||
free(addr);
|
free(addr);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for a task to complete (for threads outside thread pool)
|
// Wait for a task to complete (for threads outside thread pool)
|
||||||
static void generic_wait_handler(task_wait_handle wait_task)
|
static void
|
||||||
|
generic_wait_handler(task_wait_handle wait_task)
|
||||||
{
|
{
|
||||||
// Create an event to wait on
|
// Create an event to wait on
|
||||||
task_wait_event event;
|
task_wait_event event;
|
||||||
event.init();
|
event.init();
|
||||||
|
|
||||||
// Create a continuation for the task we are waiting for
|
// Create a continuation for the task we are waiting for
|
||||||
wait_task.on_finish([&event] {
|
wait_task.on_finish([&event] {
|
||||||
// Just signal the thread event
|
// Just signal the thread event
|
||||||
event.signal(wait_type::task_finished);
|
event.signal(wait_type::task_finished);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for the event to be set
|
// Wait for the event to be set
|
||||||
event.wait();
|
event.wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(EMULATE_PTHREAD_THREAD_LOCAL)
|
#if defined(EMULATE_PTHREAD_THREAD_LOCAL)
|
||||||
// Wait handler function, per-thread, defaults to generic version
|
// Wait handler function, per-thread, defaults to generic version
|
||||||
struct pthread_emulation_thread_wait_handler_key_initializer {
|
struct pthread_emulation_thread_wait_handler_key_initializer {
|
||||||
pthread_key_t key;
|
pthread_key_t key;
|
||||||
|
|
||||||
pthread_emulation_thread_wait_handler_key_initializer()
|
pthread_emulation_thread_wait_handler_key_initializer() { pthread_key_create(&key, nullptr); }
|
||||||
{
|
|
||||||
pthread_key_create(&key, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
~pthread_emulation_thread_wait_handler_key_initializer()
|
~pthread_emulation_thread_wait_handler_key_initializer() { pthread_key_delete(key); }
|
||||||
{
|
|
||||||
pthread_key_delete(key);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static pthread_key_t get_thread_wait_handler_key()
|
static pthread_key_t
|
||||||
|
get_thread_wait_handler_key()
|
||||||
{
|
{
|
||||||
static pthread_emulation_thread_wait_handler_key_initializer initializer;
|
static pthread_emulation_thread_wait_handler_key_initializer initializer;
|
||||||
return initializer.key;
|
return initializer.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
static THREAD_LOCAL wait_handler thread_wait_handler = generic_wait_handler;
|
static THREAD_LOCAL wait_handler thread_wait_handler = generic_wait_handler;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void set_thread_wait_handler(wait_handler handler)
|
static void
|
||||||
|
set_thread_wait_handler(wait_handler handler)
|
||||||
{
|
{
|
||||||
#if defined(EMULATE_PTHREAD_THREAD_LOCAL)
|
#if defined(EMULATE_PTHREAD_THREAD_LOCAL)
|
||||||
// we need to call this here, because the pthread initializer is lazy,
|
// we need to call this here, because the pthread initializer is lazy,
|
||||||
// this means the it could be null and we need to set it before trying to
|
// this means the it could be null and we need to set it before trying to
|
||||||
// get or set it
|
// get or set it
|
||||||
pthread_setspecific(get_thread_wait_handler_key(), reinterpret_cast<void*>(handler));
|
pthread_setspecific(get_thread_wait_handler_key(), reinterpret_cast<void *>(handler));
|
||||||
#else
|
#else
|
||||||
thread_wait_handler = handler;
|
thread_wait_handler = handler;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static wait_handler get_thread_wait_handler()
|
static wait_handler
|
||||||
|
get_thread_wait_handler()
|
||||||
{
|
{
|
||||||
#if defined(EMULATE_PTHREAD_THREAD_LOCAL)
|
#if defined(EMULATE_PTHREAD_THREAD_LOCAL)
|
||||||
// we need to call this here, because the pthread initializer is lazy,
|
// we need to call this here, because the pthread initializer is lazy,
|
||||||
// this means the it could be null and we need to set it before trying to
|
// this means the it could be null and we need to set it before trying to
|
||||||
// get or set it
|
// get or set it
|
||||||
wait_handler handler = (wait_handler) pthread_getspecific(get_thread_wait_handler_key());
|
wait_handler handler = (wait_handler) pthread_getspecific(get_thread_wait_handler_key());
|
||||||
if(handler == nullptr) {
|
if (handler == nullptr) { return generic_wait_handler; }
|
||||||
return generic_wait_handler;
|
return handler;
|
||||||
}
|
|
||||||
return handler;
|
|
||||||
#else
|
#else
|
||||||
return thread_wait_handler;
|
return thread_wait_handler;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for a task to complete
|
// Wait for a task to complete
|
||||||
void wait_for_task(task_base* wait_task)
|
void
|
||||||
|
generic_wait_for_task(task_base *wait_task)
|
||||||
{
|
{
|
||||||
// Dispatch to the current thread's wait handler
|
// Dispatch to the current thread's wait handler
|
||||||
wait_handler thread_wait_handler = get_thread_wait_handler();
|
wait_handler thread_wait_handler = get_thread_wait_handler();
|
||||||
thread_wait_handler(task_wait_handle(wait_task));
|
thread_wait_handler(task_wait_handle(wait_task));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The default scheduler is just a thread pool which can be configured
|
// The default scheduler is just a thread pool which can be configured
|
||||||
// using environment variables.
|
// using environment variables.
|
||||||
class default_scheduler_impl: public threadpool_scheduler {
|
class default_scheduler_impl : public threadpool_scheduler {
|
||||||
static std::size_t get_num_threads()
|
static std::size_t get_num_threads()
|
||||||
{
|
{
|
||||||
// Get the requested number of threads from the environment
|
// Get the requested number of threads from the environment
|
||||||
// If that fails, use the number of CPUs in the system.
|
// If that fails, use the number of CPUs in the system.
|
||||||
std::size_t num_threads;
|
std::size_t num_threads;
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
char* s;
|
char *s;
|
||||||
# ifdef __cplusplus_winrt
|
#ifdef __cplusplus_winrt
|
||||||
// Windows store applications do not support environment variables
|
// Windows store applications do not support environment variables
|
||||||
s = nullptr;
|
s = nullptr;
|
||||||
# else
|
|
||||||
// MSVC gives an error when trying to use getenv, work around this
|
|
||||||
// by using _dupenv_s instead.
|
|
||||||
_dupenv_s(&s, nullptr, "LIBASYNC_NUM_THREADS");
|
|
||||||
# endif
|
|
||||||
#else
|
#else
|
||||||
const char *s = std::getenv("LIBASYNC_NUM_THREADS");
|
// MSVC gives an error when trying to use getenv, work around this
|
||||||
|
// by using _dupenv_s instead.
|
||||||
|
_dupenv_s(&s, nullptr, "LIBASYNC_NUM_THREADS");
|
||||||
#endif
|
#endif
|
||||||
if (s)
|
#else
|
||||||
num_threads = std::strtoul(s, nullptr, 10);
|
const char *s = std::getenv("LIBASYNC_NUM_THREADS");
|
||||||
else
|
#endif
|
||||||
num_threads = hardware_concurrency();
|
if (s)
|
||||||
|
num_threads = std::strtoul(s, nullptr, 10);
|
||||||
|
else
|
||||||
|
num_threads = hardware_concurrency();
|
||||||
|
|
||||||
#if defined(_MSC_VER) && !defined(__cplusplus_winrt)
|
#if defined(_MSC_VER) && !defined(__cplusplus_winrt)
|
||||||
// Free the string allocated by _dupenv_s
|
// Free the string allocated by _dupenv_s
|
||||||
free(s);
|
free(s);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Make sure the thread count is reasonable
|
// Make sure the thread count is reasonable
|
||||||
if (num_threads < 1)
|
if (num_threads < 1) num_threads = 1;
|
||||||
num_threads = 1;
|
return num_threads;
|
||||||
return num_threads;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
default_scheduler_impl()
|
default_scheduler_impl() : threadpool_scheduler(get_num_threads()) {}
|
||||||
: threadpool_scheduler(get_num_threads()) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Thread scheduler implementation
|
// Thread scheduler implementation
|
||||||
void thread_scheduler_impl::schedule(task_run_handle t)
|
void
|
||||||
|
thread_scheduler_impl::schedule(task_run_handle t)
|
||||||
{
|
{
|
||||||
// A shared_ptr is used here because not all implementations of
|
// A shared_ptr is used here because not all implementations of
|
||||||
// std::thread support move-only objects.
|
// std::thread support move-only objects.
|
||||||
std::thread([](const std::shared_ptr<task_run_handle>& t) {
|
std::thread([](const std::shared_ptr<task_run_handle> &t) { t->run(); },
|
||||||
t->run();
|
std::make_shared<task_run_handle>(std::move(t)))
|
||||||
}, std::make_shared<task_run_handle>(std::move(t))).detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace detail
|
}// namespace detail
|
||||||
|
|
||||||
threadpool_scheduler& default_threadpool_scheduler()
|
threadpool_scheduler &
|
||||||
|
default_threadpool_scheduler()
|
||||||
{
|
{
|
||||||
return detail::singleton<detail::default_scheduler_impl>::get_instance();
|
return detail::singleton<detail::default_scheduler_impl>::get_instance();
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIFO scheduler implementation
|
// FIFO scheduler implementation
|
||||||
struct fifo_scheduler::internal_data {
|
struct fifo_scheduler::internal_data {
|
||||||
detail::fifo_queue queue;
|
detail::fifo_queue queue;
|
||||||
std::mutex lock;
|
std::mutex lock;
|
||||||
};
|
};
|
||||||
fifo_scheduler::fifo_scheduler()
|
|
||||||
: impl(new internal_data) {}
|
fifo_scheduler::fifo_scheduler() : impl(new internal_data) {}
|
||||||
|
|
||||||
fifo_scheduler::~fifo_scheduler() {}
|
fifo_scheduler::~fifo_scheduler() {}
|
||||||
void fifo_scheduler::schedule(task_run_handle t)
|
|
||||||
|
void
|
||||||
|
fifo_scheduler::schedule(task_run_handle t)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> locked(impl->lock);
|
std::lock_guard<std::mutex> locked(impl->lock);
|
||||||
impl->queue.push(std::move(t));
|
impl->queue.push(std::move(t));
|
||||||
}
|
|
||||||
bool fifo_scheduler::try_run_one_task()
|
|
||||||
{
|
|
||||||
task_run_handle t;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> locked(impl->lock);
|
|
||||||
t = impl->queue.pop();
|
|
||||||
}
|
|
||||||
if (t) {
|
|
||||||
t.run();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
void fifo_scheduler::run_all_tasks()
|
|
||||||
{
|
|
||||||
while (try_run_one_task()) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t hardware_concurrency() LIBASYNC_NOEXCEPT
|
bool
|
||||||
|
fifo_scheduler::try_run_one_task()
|
||||||
{
|
{
|
||||||
// Cache the value because calculating it may be expensive
|
task_run_handle t;
|
||||||
static std::size_t value = std::thread::hardware_concurrency();
|
{
|
||||||
|
std::lock_guard<std::mutex> locked(impl->lock);
|
||||||
// Always return at least 1 core
|
t = impl->queue.pop();
|
||||||
return value == 0 ? 1 : value;
|
}
|
||||||
|
if (t) {
|
||||||
|
t.run();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
wait_handler set_thread_wait_handler(wait_handler handler) LIBASYNC_NOEXCEPT
|
void
|
||||||
|
fifo_scheduler::run_all_tasks()
|
||||||
{
|
{
|
||||||
wait_handler old = detail::get_thread_wait_handler();
|
while (try_run_one_task()) {}
|
||||||
detail::set_thread_wait_handler(handler);
|
|
||||||
return old;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace async
|
std::size_t
|
||||||
|
hardware_concurrency() LIBASYNC_NOEXCEPT
|
||||||
|
{
|
||||||
|
// Cache the value because calculating it may be expensive
|
||||||
|
static std::size_t value = std::thread::hardware_concurrency();
|
||||||
|
|
||||||
|
// Always return at least 1 core
|
||||||
|
return value == 0 ? 1 : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_handler
|
||||||
|
set_thread_wait_handler(wait_handler handler) LIBASYNC_NOEXCEPT
|
||||||
|
{
|
||||||
|
wait_handler old = detail::get_thread_wait_handler();
|
||||||
|
detail::set_thread_wait_handler(handler);
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace async
|
||||||
|
|
||||||
#ifndef LIBASYNC_STATIC
|
#ifndef LIBASYNC_STATIC
|
||||||
#if defined(__GNUC__) && !defined(_WIN32)
|
#if defined(__GNUC__) && !defined(_WIN32)
|
||||||
# pragma GCC visibility pop
|
#pragma GCC visibility pop
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
@ -8,14 +8,14 @@ class FiberScheduler;
|
|||||||
|
|
||||||
namespace async {
|
namespace async {
|
||||||
sled::FiberScheduler &default_scheduler();
|
sled::FiberScheduler &default_scheduler();
|
||||||
class task_base;
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
class task_base;
|
||||||
void wait_for_task(task_base *wait_task);
|
void wait_for_task(task_base *wait_task);
|
||||||
}
|
}// namespace detail
|
||||||
}// namespace async
|
}// namespace async
|
||||||
|
|
||||||
#define LIBASYNC_CUSTON_EVENT
|
#define LIBASYNC_CUSTOM_WAIT_FOR_TASK
|
||||||
#define LIBASYNC_CUSTOM_DEFAULT_SCHEDULER
|
#define LIBASYNC_CUSTOM_DEFAULT_SCHEDULER
|
||||||
#include <async++.h>
|
#include <async++.h>
|
||||||
|
|
||||||
|
@ -532,7 +532,7 @@ URI::href() const
|
|||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
if (!scheme().empty()) { ss << scheme() << ":"; }
|
if (!scheme().empty()) { ss << scheme() << ":"; }
|
||||||
if (!user_info.empty()) { ss << user_info() << "@"; }
|
if (!user_info().empty()) { ss << user_info() << "@"; }
|
||||||
if (!authority().empty()) { ss << authority(); }
|
if (!authority().empty()) { ss << authority(); }
|
||||||
ss << path();
|
ss << path();
|
||||||
ss << "?" << query_string();
|
ss << "?" << query_string();
|
||||||
|
@ -7,7 +7,7 @@ TEST(URI, Absolute)
|
|||||||
EXPECT_EQ(uri.scheme(), "http");
|
EXPECT_EQ(uri.scheme(), "http");
|
||||||
EXPECT_EQ(uri.host(), "example.com");
|
EXPECT_EQ(uri.host(), "example.com");
|
||||||
EXPECT_EQ(uri.port(), 1234);
|
EXPECT_EQ(uri.port(), 1234);
|
||||||
EXPECT_EQ(uri.path(), "dir1/dir2/file");
|
EXPECT_EQ(uri.path(), "/dir1/dir2/file");
|
||||||
EXPECT_EQ(uri.query().size(), 1);
|
EXPECT_EQ(uri.query().size(), 1);
|
||||||
EXPECT_EQ(uri.query()["a"], "1");
|
EXPECT_EQ(uri.query()["a"], "1");
|
||||||
EXPECT_EQ(uri.anchor(), "anchor");
|
EXPECT_EQ(uri.anchor(), "anchor");
|
||||||
|
Loading…
Reference in New Issue
Block a user