From 4d1845b5c3da819da36d064a98887ad57df85ba7 Mon Sep 17 00:00:00 2001 From: Lewis Baker Date: Sat, 20 Jan 2018 15:12:45 +1030 Subject: [PATCH 01/34] Turn off warnings about std::result_of_t usage on latest msvc compiler. I will move over to using std::invoke_result_t once I have confirmed that it is supported by clang/libc++ as well. --- config.cake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config.cake b/config.cake index 2332813f..a9a0925b 100644 --- a/config.cake +++ b/config.cake @@ -117,6 +117,8 @@ if cake.system.isWindows() or cake.system.isCygwin(): # Enable C++17 features like std::optional<> compiler.addCppFlag('/std:c++latest') + compiler.addDefine('_SILENCE_CXX17_RESULT_OF_DEPRECATION_WARNING') + compiler.addProgramFlag('/nodefaultlib') compiler.addModuleFlag('/nodefaultlib') From c549df842f159bcbebd8ea3a5b7392344729070a Mon Sep 17 00:00:00 2001 From: Anirudh Badam Date: Sat, 27 Jan 2018 18:10:12 +0000 Subject: [PATCH 02/34] support for linux scheduler and timer --- config.cake | 4 +- include/cppcoro/detail/linux.hpp | 59 +++++ include/cppcoro/io_service.hpp | 19 +- lib/build.cake | 10 +- lib/io_service.cpp | 352 ++++++++++++++++++++++++----- lib/linux.cpp | 193 ++++++++++++++++ test/build.cake | 4 +- test/scheduling_operator_tests.cpp | 85 ++++--- 8 files changed, 629 insertions(+), 97 deletions(-) create mode 100644 include/cppcoro/detail/linux.hpp create mode 100644 lib/linux.cpp diff --git a/config.cake b/config.cake index 77b71038..74874f95 100644 --- a/config.cake +++ b/config.cake @@ -221,7 +221,9 @@ elif cake.system.isLinux(): compiler.addLibrary('c++') compiler.addLibrary('c') compiler.addLibrary('pthread') - + compiler.addLibrary('rt') + compiler.addLibrary('uuid') + #compiler.addProgramFlag('-Wl,--trace') #compiler.addProgramFlag('-Wl,-v') diff --git a/include/cppcoro/detail/linux.hpp b/include/cppcoro/detail/linux.hpp new file mode 100644 index 00000000..6739a25d --- /dev/null +++ b/include/cppcoro/detail/linux.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cppcoro +{ + namespace detail + { + namespace linux + { + enum message_type + { + CALLBACK_TYPE, + RESUME_TYPE + }; + + struct message + { + enum message_type m_type; + void* m_ptr; + }; + + struct io_state : linux::message + { + using callback_type = void(io_state* state); + callback_type* m_callback; + }; + + class message_queue + { + private: + mqd_t m_mqdt; + char m_qname[NAME_MAX]; + int m_epollfd; + struct epoll_event m_ev; + message_queue(); + public: + message_queue(size_t queue_length); + ~message_queue(); + bool enqueue_message(void* message, message_type type); + bool dequeue_message(void*& message, message_type& type, bool wait); + }; + + int create_event_fd(); + int create_timer_fd(); + int create_epoll_fd(); + } + } +} diff --git a/include/cppcoro/io_service.hpp b/include/cppcoro/io_service.hpp index 27867c58..99ab7168 100644 --- a/include/cppcoro/io_service.hpp +++ b/include/cppcoro/io_service.hpp @@ -13,6 +13,10 @@ # include #endif +#if CPPCORO_OS_LINUX +#include +#endif + #include #include #include @@ -42,8 +46,12 @@ namespace cppcoro /// actively processing events. /// Note that the number of active threads may temporarily go /// above this number. - io_service(std::uint32_t concurrencyHint); - +#if CPPCORO_OS_WINNT + io_service(std::uint32_t concurrencyHint); +#endif +#if CPPCORO_OS_LINUX + io_service(size_t queue_length); +#endif ~io_service(); io_service(io_service&& other) = delete; @@ -147,6 +155,10 @@ namespace cppcoro void try_reschedule_overflow_operations() noexcept; + void queue_overflow_operation_to_head(schedule_operation* operation) noexcept; + + void queue_overflow_operation_to_tail(schedule_operation* operation) noexcept; + bool try_enter_event_loop() noexcept; void exit_event_loop() noexcept; @@ -169,6 +181,9 @@ namespace cppcoro detail::win32::safe_handle m_iocpHandle; #endif +#if CPPCORO_OS_LINUX + detail::linux::message_queue* m_mq; +#endif // Head of a linked-list of schedule operations that are // ready to run but that failed to be queued to the I/O // completion port (eg. due to low memory). diff --git a/lib/build.cake b/lib/build.cake index 9e6f5c55..4abd7ab6 100644 --- a/lib/build.cake +++ b/lib/build.cake @@ -65,6 +65,7 @@ sources = script.cwd([ 'cancellation_source.cpp', 'cancellation_registration.cpp', 'lightweight_manual_reset_event.cpp', + 'io_service.cpp', ]) extras = script.cwd([ @@ -72,13 +73,20 @@ extras = script.cwd([ 'use.cake', ]) +if variant.platform == "linux": + detailIncludes.extend(cake.path.join(env.expand('${CPPCORO}'), 'include', 'cppcoro', 'detail', [ + 'linux.hpp', + ])) + sources.extend(script.cwd([ + 'linux.cpp', + ])) + if variant.platform == "windows": detailIncludes.extend(cake.path.join(env.expand('${CPPCORO}'), 'include', 'cppcoro', 'detail', [ 'win32.hpp', ])) sources.extend(script.cwd([ 'win32.cpp', - 'io_service.cpp', 'file.cpp', 'readable_file.cpp', 'writable_file.cpp', diff --git a/lib/io_service.cpp b/lib/io_service.cpp index 8031775e..f87eb009 100644 --- a/lib/io_service.cpp +++ b/lib/io_service.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #if CPPCORO_OS_WINNT # define WIN32_LEAN_AND_MEAN @@ -20,7 +21,7 @@ namespace { #if CPPCORO_OS_WINNT - cppcoro::detail::win32::safe_handle create_io_completion_port(std::uint32_t concurrencyHint) + cppcoro::detail::win32::safe_handle create_io_completion_port(std::uint32_t concurrencyHint) { HANDLE handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, concurrencyHint); if (handle == NULL) @@ -298,15 +299,21 @@ class cppcoro::io_service::timer_thread_state void request_timer_cancellation() noexcept; - void run() noexcept; + void run(); - void wake_up_timer_thread() noexcept; + void wake_up_timer_thread(); #if CPPCORO_OS_WINNT detail::win32::safe_handle m_wakeUpEvent; detail::win32::safe_handle m_waitableTimerEvent; #endif +#if CPPCORO_OS_LINUX + int m_wakeupfd; + int m_timerfd; + int m_epollfd; +#endif + std::atomic m_newlyQueuedTimers; std::atomic m_timerCancellationRequested; std::atomic m_shutDownRequested; @@ -314,18 +321,31 @@ class cppcoro::io_service::timer_thread_state std::thread m_thread; }; - - cppcoro::io_service::io_service() +#if CPPCORO_OS_WINNT : io_service(0) +#endif +#if CPPCORO_OS_LINUX + : io_service(1) +#endif { } -cppcoro::io_service::io_service(std::uint32_t concurrencyHint) +cppcoro::io_service::io_service( +#if CPPCORO_OS_WINNT + std::uint32_t concurrencyHint +#endif +#if CPPCORO_OS_LINUX + size_t queue_length +#endif + ) : m_threadState(0) , m_workCount(0) #if CPPCORO_OS_WINNT , m_iocpHandle(create_io_completion_port(concurrencyHint)) +#endif +#if CPPCORO_OS_LINUX + , m_mq(new detail::linux::message_queue(queue_length)) #endif , m_scheduleOperations(nullptr) , m_timerState(nullptr) @@ -338,6 +358,10 @@ cppcoro::io_service::~io_service() assert(m_threadState.load(std::memory_order_relaxed) < active_thread_count_increment); delete m_timerState.load(std::memory_order_relaxed); + +#if CPPCORO_OS_LINUX + delete m_mq; +#endif } cppcoro::io_service::schedule_operation cppcoro::io_service::schedule() noexcept @@ -453,13 +477,69 @@ void cppcoro::io_service::notify_work_finished() noexcept } } +#if CPPCORO_OS_WINNT cppcoro::detail::win32::handle_t cppcoro::io_service::native_iocp_handle() noexcept { return m_iocpHandle.handle(); } +#endif + +void cppcoro::io_service::queue_overflow_operation_to_tail(schedule_operation* operation) noexcept +{ + // Still unable to queue these operations. + // Put them back on the list of overflow operations. + auto* tail = operation; + while (tail->m_next != nullptr) + { + tail = tail->m_next; + } + + schedule_operation* head = nullptr; + while (!m_scheduleOperations.compare_exchange_weak( + head, + operation, + std::memory_order_release, + std::memory_order_relaxed)) + { + tail->m_next = head; + } + + return; +} + +void cppcoro::io_service::queue_overflow_operation_to_head(schedule_operation* operation) noexcept +{ + // Failed to post to the I/O completion port. + // + // This is most-likely because the queue is currently full. + // + // We'll queue up the operation to a linked-list using a lock-free + // push and defer the dispatch to the completion port until some I/O + // thread next enters its event loop. + auto* head = m_scheduleOperations.load(std::memory_order_acquire); + do + { + operation->m_next = head; + } while (!m_scheduleOperations.compare_exchange_weak( + head, + operation, + std::memory_order_release, + std::memory_order_acquire)); +} void cppcoro::io_service::schedule_impl(schedule_operation* operation) noexcept { +#if CPPCORO_OS_LINUX + bool status = m_mq->enqueue_message(reinterpret_cast(operation->m_awaiter.address()), detail::linux::RESUME_TYPE); + + if(!status) + { + assert(errno == EAGAIN); + queue_overflow_operation_to_head(operation); + } + + return; +#endif #if CPPCORO_OS_WINNT const BOOL ok = ::PostQueuedCompletionStatus( m_iocpHandle.handle(), @@ -468,33 +548,18 @@ void cppcoro::io_service::schedule_impl(schedule_operation* operation) noexcept nullptr); if (!ok) { - // Failed to post to the I/O completion port. - // - // This is most-likely because the queue is currently full. - // - // We'll queue up the operation to a linked-list using a lock-free - // push and defer the dispatch to the completion port until some I/O - // thread next enters its event loop. - auto* head = m_scheduleOperations.load(std::memory_order_acquire); - do - { - operation->m_next = head; - } while (!m_scheduleOperations.compare_exchange_weak( - head, - operation, - std::memory_order_release, - std::memory_order_acquire)); + queue_overflow_operation_to_head(operation); } #endif } void cppcoro::io_service::try_reschedule_overflow_operations() noexcept { -#if CPPCORO_OS_WINNT auto* operation = m_scheduleOperations.exchange(nullptr, std::memory_order_acquire); while (operation != nullptr) { auto* next = operation->m_next; +#if CPPCORO_OS_WINNT BOOL ok = ::PostQueuedCompletionStatus( m_iocpHandle.handle(), 0, @@ -502,30 +567,20 @@ void cppcoro::io_service::try_reschedule_overflow_operations() noexcept nullptr); if (!ok) { - // Still unable to queue these operations. - // Put them back on the list of overflow operations. - auto* tail = operation; - while (tail->m_next != nullptr) - { - tail = tail->m_next; - } - - schedule_operation* head = nullptr; - while (!m_scheduleOperations.compare_exchange_weak( - head, - operation, - std::memory_order_release, - std::memory_order_relaxed)) - { - tail->m_next = head; - } - - return; + queue_overflow_operation_to_tail(operation); } - +#endif +#if CPPCORO_OS_LINUX + bool status = m_mq->enqueue_message(reinterpret_cast(operation->m_awaiter.address()), detail::linux::RESUME_TYPE); + + if(!status) + { + assert(errno == EAGAIN); + queue_overflow_operation_to_tail(operation); + } +#endif operation = next; } -#endif } bool cppcoro::io_service::try_enter_event_loop() noexcept @@ -552,12 +607,52 @@ void cppcoro::io_service::exit_event_loop() noexcept bool cppcoro::io_service::try_process_one_event(bool waitForEvent) { -#if CPPCORO_OS_WINNT - if (is_stop_requested()) + if(is_stop_requested()) + { + return false; + } + +#if CPPCORO_OS_LINUX + while(true) + { + try_reschedule_overflow_operations(); + void* message = NULL; + detail::linux::message_type type = detail::linux::RESUME_TYPE; + + bool status = m_mq->dequeue_message(message, type, waitForEvent); + + if(!status) { - return false; + return false; } - + + if(type == detail::linux::CALLBACK_TYPE) + { + auto* state = + static_cast(reinterpret_cast(message)); + + state->m_callback(state); + + return true; + } + else + { + if((unsigned long long)message != 0) + { + std::experimental::coroutine_handle<>::from_address(reinterpret_cast(message)).resume(); + + return true; + } + + if (is_stop_requested()) + { + return false; + } + } + } +#endif + +#if CPPCORO_OS_WINNT const DWORD timeout = waitForEvent ? INFINITE : 0; while (true) @@ -640,6 +735,10 @@ void cppcoro::io_service::post_wake_up_event() noexcept // in the queue next time they check anyway and thus wake-up. (void)::PostQueuedCompletionStatus(m_iocpHandle.handle(), 0, 0, nullptr); #endif + +#if CPPCORO_OS_LINUX + (void)m_mq->enqueue_message(NULL, detail::linux::RESUME_TYPE); +#endif } cppcoro::io_service::timer_thread_state* @@ -665,16 +764,50 @@ cppcoro::io_service::ensure_timer_thread_started() return timerState; } -cppcoro::io_service::timer_thread_state::timer_thread_state() +cppcoro::io_service::timer_thread_state::timer_thread_state() : #if CPPCORO_OS_WINNT - : m_wakeUpEvent(create_auto_reset_event()) - , m_waitableTimerEvent(create_waitable_timer_event()) + m_wakeUpEvent(create_auto_reset_event()) + , m_waitableTimerEvent(create_waitable_timer_event()), +#endif +#if CPPCORO_OS_LINUX + m_wakeupfd(detail::linux::create_event_fd()) + , m_timerfd(detail::linux::create_timer_fd()) + , m_epollfd(detail::linux::create_epoll_fd()), #endif - , m_newlyQueuedTimers(nullptr) + m_newlyQueuedTimers(nullptr) , m_timerCancellationRequested(false) , m_shutDownRequested(false) , m_thread([this] { this->run(); }) { +#if CPPCORO_OS_LINUX + struct epoll_event wake_ev = {0}; + wake_ev.events = EPOLLIN; + wake_ev.data.fd = m_wakeupfd; + + if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_wakeupfd, &wake_ev) == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: epoll ctl wake ev" + }; + } + + struct epoll_event timer_ev = {0}; + timer_ev.events = EPOLLIN; + timer_ev.data.fd = m_timerfd; + + if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_timerfd, &timer_ev) == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: epoll ctl timer ev" + }; + } +#endif } cppcoro::io_service::timer_thread_state::~timer_thread_state() @@ -682,6 +815,11 @@ cppcoro::io_service::timer_thread_state::~timer_thread_state() m_shutDownRequested.store(true, std::memory_order_release); wake_up_timer_thread(); m_thread.join(); +#if CPPCORO_OS_LINUX + close(m_wakeupfd); + close(m_timerfd); + close(m_epollfd); +#endif } void cppcoro::io_service::timer_thread_state::request_timer_cancellation() noexcept @@ -694,36 +832,77 @@ void cppcoro::io_service::timer_thread_state::request_timer_cancellation() noexc } } -void cppcoro::io_service::timer_thread_state::run() noexcept +void cppcoro::io_service::timer_thread_state::run() { -#if CPPCORO_OS_WINNT using clock = std::chrono::high_resolution_clock; using time_point = clock::time_point; timer_queue timerQueue; +#if CPPCORO_OS_WINNT const DWORD waitHandleCount = 2; const HANDLE waitHandles[waitHandleCount] = { m_wakeUpEvent.handle(), m_waitableTimerEvent.handle() }; + DWORD timeout = INFINITE; +#endif time_point lastSetWaitEventTime = time_point::max(); timed_schedule_operation* timersReadyToResume = nullptr; - DWORD timeout = INFINITE; while (!m_shutDownRequested.load(std::memory_order_relaxed)) { +#if CPPCORO_OS_WINNNT const DWORD waitResult = ::WaitForMultipleObjectsEx( waitHandleCount, waitHandles, FALSE, // waitAll timeout, FALSE); // alertable +#endif + +#if CPPCORO_OS_LINUX + struct epoll_event ev; + const int status = epoll_wait(m_epollfd, &ev, 1, -1); + + if(status == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in timer thread: epoll wait" + }; + } +#endif + + +#if CPPCORO_OS_WINNT if (waitResult == WAIT_OBJECT_0 || waitResult == WAIT_FAILED) { +#endif +#if CPPCORO_OS_LINUX + if(status == 0) + { + continue; + } + + if (status == 1 && ev.data.fd == m_wakeupfd) + { + uint64_t count; + if(read(m_wakeupfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in timer thread: eventfd read" + }; + } +#endif // Wake-up event (WAIT_OBJECT_0) // // We are only woken up for: @@ -759,9 +938,29 @@ void cppcoro::io_service::timer_thread_state::run() noexcept } } } - else if (waitResult == (WAIT_OBJECT_0 + 1)) + else if ( +#if CPPCORO_OS_WINNT + waitResult == (WAIT_OBJECT_0 + 1) +#endif +#if CPPCORO_OS_LINUX + ev.data.fd == m_timerfd +#endif + ) { lastSetWaitEventTime = time_point::max(); + +#if CPPCORO_OS_LINUX + uint64_t count; + if(read(m_timerfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in timer thread: timerfd read" + }; + } +#endif } if (!timerQueue.is_empty()) @@ -780,11 +979,12 @@ void cppcoro::io_service::timer_thread_state::run() noexcept // amount of time to wait until the next timer is ready. if (earliestDueTime != lastSetWaitEventTime) { - using ticks = std::chrono::duration>; + using ticks = std::chrono::duration>; auto timeUntilNextDueTime = earliestDueTime - currentTime; // Negative value indicates relative time. +#if CPPCORO_OS_WINNT LARGE_INTEGER dueTime; dueTime.QuadPart = -std::chrono::duration_cast(timeUntilNextDueTime).count(); @@ -831,6 +1031,29 @@ void cppcoro::io_service::timer_thread_state::run() noexcept timeout = 1; } } +#endif +#if CPPCORO_OS_LINUX + struct itimerspec alarm_time = {0}; + alarm_time.it_value.tv_sec = std::chrono::duration_cast(timeUntilNextDueTime).count(); + alarm_time.it_value.tv_nsec = (std::chrono::duration_cast(timeUntilNextDueTime).count() % 10000000) * 100; + if(alarm_time.it_value.tv_sec == 0 && alarm_time.it_value.tv_nsec == 0) + { + //linux timer of 0 time will not generate events so let's set it to 1 nsec + alarm_time.it_value.tv_nsec = 1; + } + + if(timerfd_settime(m_timerfd, 0, &alarm_time, NULL) == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in timer thread: timerfd_settime" + }; + } + + lastSetWaitEventTime = earliestDueTime; +#endif } } } @@ -854,14 +1077,25 @@ void cppcoro::io_service::timer_thread_state::run() noexcept timersReadyToResume = nextTimer; } } -#endif } -void cppcoro::io_service::timer_thread_state::wake_up_timer_thread() noexcept +void cppcoro::io_service::timer_thread_state::wake_up_timer_thread() { #if CPPCORO_OS_WINNT (void)::SetEvent(m_wakeUpEvent.handle()); #endif +#if CPPCORO_OS_LINUX + uint64_t count = 1; + if(write(m_wakeupfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in waking up: writing to wake up eventfd" + }; + } +#endif } void cppcoro::io_service::schedule_operation::await_suspend( diff --git a/lib/linux.cpp b/lib/linux.cpp new file mode 100644 index 00000000..56d18daa --- /dev/null +++ b/lib/linux.cpp @@ -0,0 +1,193 @@ +#include +#include +#include + +#define UUID_STRING_SIZE 36 + +namespace cppcoro +{ + namespace detail + { + namespace linux + { + message_queue::message_queue(size_t queue_length) + { + m_mqdt = -1; + uuid_t unique_name; + const char* cppcoro_qname_prefix = "/cppcoro-"; + + if(NAME_MAX < UUID_STRING_SIZE + sizeof(cppcoro_qname_prefix) + 1) + { + throw std::system_error + { + static_cast(EINVAL), + std::system_category(), + "Error creating message queue: system name max length too small" + }; + } + + strncpy(m_qname, cppcoro_qname_prefix, NAME_MAX); + + for(;;) + { + uuid_generate(unique_name); + uuid_unparse(unique_name, m_qname + sizeof(cppcoro_qname_prefix)); + + struct mq_attr attr; + attr.mq_flags = O_NONBLOCK; + attr.mq_maxmsg = queue_length; + attr.mq_msgsize = sizeof(cppcoro::detail::linux::message); + attr.mq_curmsgs = 0; + + m_mqdt = mq_open((const char*)m_qname, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, S_IRWXU, &attr); + + if( m_mqdt == -1 && errno == EEXIST) + { + continue; + } + + if( m_mqdt == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: message queue open" + }; + } + + break; + } + + m_epollfd = create_epoll_fd(); + m_ev.data.fd = m_mqdt; + m_ev.events = EPOLLIN; + + if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_mqdt, &m_ev) == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: epoll ctl mqdt" + }; + } + } + + message_queue::~message_queue() + { + close(m_epollfd); + assert(mq_close(m_mqdt) == 0); + assert(mq_unlink(m_qname) == 0); + } + + bool message_queue::enqueue_message(void* msg, message_type type) + { + message qmsg; + qmsg.m_type = type; + qmsg.m_ptr = msg; + int status = mq_send(m_mqdt, (const char*)&qmsg, sizeof(message), 0); + return status==-1?false:true; + } + + bool message_queue::dequeue_message(void*& msg, message_type& type, bool wait) + { + struct epoll_event ev = {0}; + int nfds = epoll_wait(m_epollfd, &ev, 1, wait?-1:0); + + if(nfds == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in epoll_wait run loop" + }; + } + + if(nfds == 0 && !wait) + { + return false; + } + + if(nfds == 0 && wait) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in epoll_wait run loop" + }; + } + + message qmsg; + ssize_t status = mq_receive(m_mqdt, (char*)&qmsg, sizeof(message), NULL); + + if(status == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error retrieving message from message queue: mq_receive" + }; + } + + msg = qmsg.m_ptr; + type = qmsg.m_type; + return true; + } + + int create_event_fd() + { + int fd = eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK | EFD_CLOEXEC); + + if(fd == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: event fd create" + }; + } + + return fd; + } + + int create_timer_fd() + { + int fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); + + if(fd == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: timer fd create" + }; + } + + return fd; + } + + int create_epoll_fd() + { + int fd = epoll_create1(EPOLL_CLOEXEC); + + if(fd == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating timer thread: epoll create" + }; + } + + return fd; + } + } + } +} diff --git a/test/build.cake b/test/build.cake index 0681f209..a9545cce 100644 --- a/test/build.cake +++ b/test/build.cake @@ -31,12 +31,12 @@ sources = script.cwd([ 'sync_wait_tests.cpp', 'when_all_tests.cpp', 'when_all_ready_tests.cpp', + 'scheduling_operator_tests.cpp', + 'io_service_tests.cpp', ]) if variant.platform == 'windows': sources += script.cwd([ - 'scheduling_operator_tests.cpp', - 'io_service_tests.cpp', 'file_tests.cpp', ]) diff --git a/test/scheduling_operator_tests.cpp b/test/scheduling_operator_tests.cpp index 50def1b0..39e32265 100644 --- a/test/scheduling_operator_tests.cpp +++ b/test/scheduling_operator_tests.cpp @@ -17,51 +17,72 @@ TEST_SUITE_BEGIN("schedule/resume_on"); +#if CPPCORO_OS_WINNT +#define THREAD_ID std::thread::id +#define GET_THIS_THREAD_ID std::this_thread::get_id() +#endif + +#if CPPCORO_OS_LINUX +#define THREAD_ID unsigned long long +#define GET_THIS_THREAD_ID get_thread_id() + +#include + +static unsigned long long get_thread_id() +{ + unsigned long long id; + std::stringstream ss; + ss << std::this_thread::get_id(); + id = std::stoull(ss.str()); + return id; +} +#endif + TEST_CASE_FIXTURE(io_service_fixture, "schedule_on task<> function") { - auto mainThreadId = std::this_thread::get_id(); + auto mainThreadId = GET_THIS_THREAD_ID; - std::thread::id ioThreadId; + THREAD_ID ioThreadId; auto start = [&]() -> cppcoro::task<> { - ioThreadId = std::this_thread::get_id(); + ioThreadId = GET_THIS_THREAD_ID; CHECK(ioThreadId != mainThreadId); co_return; }; cppcoro::sync_wait([&]() -> cppcoro::task<> { - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); co_await schedule_on(io_service(), start()); - CHECK(std::this_thread::get_id() == ioThreadId); + CHECK(GET_THIS_THREAD_ID == ioThreadId); }()); } TEST_CASE_FIXTURE(io_service_fixture, "schedule_on async_generator<> function") { - auto mainThreadId = std::this_thread::get_id(); + auto mainThreadId = GET_THIS_THREAD_ID; - std::thread::id ioThreadId; + THREAD_ID ioThreadId; auto makeSequence = [&]() -> cppcoro::async_generator { - ioThreadId = std::this_thread::get_id(); + ioThreadId = GET_THIS_THREAD_ID; CHECK(ioThreadId != mainThreadId); co_yield 1; - CHECK(std::this_thread::get_id() == ioThreadId); + CHECK(GET_THIS_THREAD_ID == ioThreadId); co_yield 2; - CHECK(std::this_thread::get_id() == ioThreadId); + CHECK(GET_THIS_THREAD_ID == ioThreadId); co_yield 3; - CHECK(std::this_thread::get_id() == ioThreadId); + CHECK(GET_THIS_THREAD_ID == ioThreadId); co_return; }; @@ -71,7 +92,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on async_generator<> function") cppcoro::sync_wait(cppcoro::when_all_ready( [&]() -> cppcoro::task<> { - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); auto seq = schedule_on(io_service(), makeSequence()); @@ -97,35 +118,35 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on async_generator<> function") TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> function") { - auto mainThreadId = std::this_thread::get_id(); + auto mainThreadId = GET_THIS_THREAD_ID; auto start = [&]() -> cppcoro::task<> { - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); co_return; }; cppcoro::sync_wait([&]() -> cppcoro::task<> { - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); co_await resume_on(io_service(), start()); - CHECK(std::this_thread::get_id() != mainThreadId); + CHECK(GET_THIS_THREAD_ID != mainThreadId); }()); } TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function") { - auto mainThreadId = std::this_thread::get_id(); + auto mainThreadId = GET_THIS_THREAD_ID; - std::thread::id ioThreadId; + THREAD_ID ioThreadId; auto makeSequence = [&]() -> cppcoro::async_generator { co_await io_service().schedule(); - ioThreadId = std::this_thread::get_id(); + ioThreadId = GET_THIS_THREAD_ID; CHECK(ioThreadId != mainThreadId); @@ -149,7 +170,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function") { auto stopOnExit = cppcoro::on_scope_exit([&] { otherIoService.stop(); }); - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); auto seq = resume_on(otherIoService, makeSequence()); @@ -158,7 +179,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function") { // Every time we receive a value it should be on our requested // scheduler (ie. main thread) - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); CHECK(value == expected++); // Occasionally transfer execution to a different thread before @@ -180,17 +201,17 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function") TEST_CASE_FIXTURE(io_service_fixture, "schedule_on task<> pipe syntax") { - auto mainThreadId = std::this_thread::get_id(); + auto mainThreadId = GET_THIS_THREAD_ID; auto makeTask = [&]() -> cppcoro::task { - CHECK(std::this_thread::get_id() != mainThreadId); + CHECK(GET_THIS_THREAD_ID != mainThreadId); co_return 123; }; auto triple = [&](int x) { - CHECK(std::this_thread::get_id() != mainThreadId); + CHECK(GET_THIS_THREAD_ID != mainThreadId); return x * 3; }; @@ -204,11 +225,11 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on task<> pipe syntax") TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax") { - auto mainThreadId = std::this_thread::get_id(); + auto mainThreadId = GET_THIS_THREAD_ID; auto makeTask = [&]() -> cppcoro::task { - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); co_return 123; }; @@ -216,23 +237,23 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax") { cppcoro::task t = makeTask() | cppcoro::resume_on(io_service()); CHECK(co_await t == 123); - CHECK(std::this_thread::get_id() != mainThreadId); + CHECK(GET_THIS_THREAD_ID != mainThreadId); }()); } TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax multiple uses") { - auto mainThreadId = std::this_thread::get_id(); + auto mainThreadId = GET_THIS_THREAD_ID; auto makeTask = [&]() -> cppcoro::task { - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); co_return 123; }; auto triple = [&](int x) { - CHECK(std::this_thread::get_id() != mainThreadId); + CHECK(GET_THIS_THREAD_ID != mainThreadId); return x * 3; }; @@ -243,7 +264,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax multiple use { auto stopOnExit = cppcoro::on_scope_exit([&] { otherIoService.stop(); }); - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); cppcoro::task t = makeTask() @@ -253,7 +274,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax multiple use CHECK(co_await t == 369); - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); }(), [&]() -> cppcoro::task<> { From af44da4f5ec6fe077932d82340854ee9294b9c95 Mon Sep 17 00:00:00 2001 From: Anirudh Badam Date: Wed, 7 Feb 2018 23:41:38 +0000 Subject: [PATCH 03/34] restructuring the code to reduce ifdefs --- include/cppcoro/detail/linux.hpp | 80 ++-- lib/io_service.cpp | 625 +++++++++++++++---------------- 2 files changed, 352 insertions(+), 353 deletions(-) diff --git a/include/cppcoro/detail/linux.hpp b/include/cppcoro/detail/linux.hpp index 6739a25d..a8f14499 100644 --- a/include/cppcoro/detail/linux.hpp +++ b/include/cppcoro/detail/linux.hpp @@ -12,48 +12,52 @@ #include #include +typedef int DWORD; +#define INFINITE (DWORD)-1 + namespace cppcoro { - namespace detail - { - namespace linux - { - enum message_type + namespace detail { - CALLBACK_TYPE, - RESUME_TYPE - }; - - struct message - { - enum message_type m_type; - void* m_ptr; - }; + namespace linux + { + enum message_type + { + CALLBACK_TYPE, + RESUME_TYPE + }; - struct io_state : linux::message - { - using callback_type = void(io_state* state); - callback_type* m_callback; - }; + struct message + { + enum message_type m_type; + void* m_ptr; + }; - class message_queue - { - private: - mqd_t m_mqdt; - char m_qname[NAME_MAX]; - int m_epollfd; - struct epoll_event m_ev; - message_queue(); - public: - message_queue(size_t queue_length); - ~message_queue(); - bool enqueue_message(void* message, message_type type); - bool dequeue_message(void*& message, message_type& type, bool wait); - }; + struct io_state : linux::message + { + using callback_type = void(io_state* state); + callback_type* m_callback; + }; - int create_event_fd(); - int create_timer_fd(); - int create_epoll_fd(); - } - } + class message_queue + { + private: + mqd_t m_mqdt; + char m_qname[NAME_MAX]; + int m_epollfd; + struct epoll_event m_ev; + message_queue(); + public: + message_queue(size_t queue_length); + ~message_queue(); + bool enqueue_message(void* message, message_type type); + bool dequeue_message(void*& message, message_type& type, bool wait); + }; + + int create_event_fd(); + int create_timer_fd(); + int create_epoll_fd(); + + } + } } diff --git a/lib/io_service.cpp b/lib/io_service.cpp index f87eb009..2f27ecde 100644 --- a/lib/io_service.cpp +++ b/lib/io_service.cpp @@ -21,7 +21,7 @@ namespace { #if CPPCORO_OS_WINNT - cppcoro::detail::win32::safe_handle create_io_completion_port(std::uint32_t concurrencyHint) + cppcoro::detail::win32::safe_handle create_io_completion_port(std::uint32_t concurrencyHint) { HANDLE handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, concurrencyHint); if (handle == NULL) @@ -30,9 +30,9 @@ namespace throw std::system_error { static_cast(errorCode), - std::system_category(), - "Error creating io_service: CreateIoCompletionPort" - }; + std::system_category(), + "Error creating io_service: CreateIoCompletionPort" + }; } return cppcoro::detail::win32::safe_handle{ handle }; @@ -47,9 +47,9 @@ namespace throw std::system_error { static_cast(errorCode), - std::system_category(), - "Error creating manual reset event: CreateEventW" - }; + std::system_category(), + "Error creating manual reset event: CreateEventW" + }; } return cppcoro::detail::win32::safe_handle{ eventHandle }; @@ -65,8 +65,8 @@ namespace throw std::system_error { static_cast(errorCode), - std::system_category() - }; + std::system_category() + }; } return cppcoro::detail::win32::safe_handle{ handle }; @@ -102,11 +102,11 @@ class cppcoro::io_service::timer_queue void enqueue_timer(cppcoro::io_service::timed_schedule_operation* timer) noexcept; void dequeue_due_timers( - time_point currentTime, - cppcoro::io_service::timed_schedule_operation*& timerList) noexcept; + time_point currentTime, + cppcoro::io_service::timed_schedule_operation*& timerList) noexcept; void remove_cancelled_timers( - cppcoro::io_service::timed_schedule_operation*& timerList) noexcept; + cppcoro::io_service::timed_schedule_operation*& timerList) noexcept; private: @@ -161,8 +161,8 @@ cppcoro::io_service::timer_queue::earliest_due_time() const noexcept if (m_overflowTimers != nullptr) { return std::min( - m_timerEntries.front().m_dueTime, - m_overflowTimers->m_resumeTime); + m_timerEntries.front().m_dueTime, + m_overflowTimers->m_resumeTime); } return m_timerEntries.front().m_dueTime; @@ -176,7 +176,7 @@ cppcoro::io_service::timer_queue::earliest_due_time() const noexcept } void cppcoro::io_service::timer_queue::enqueue_timer( - cppcoro::io_service::timed_schedule_operation* timer) noexcept + cppcoro::io_service::timed_schedule_operation* timer) noexcept { try { @@ -199,8 +199,8 @@ void cppcoro::io_service::timer_queue::enqueue_timer( } void cppcoro::io_service::timer_queue::dequeue_due_timers( - time_point currentTime, - cppcoro::io_service::timed_schedule_operation*& timerList) noexcept + time_point currentTime, + cppcoro::io_service::timed_schedule_operation*& timerList) noexcept { while (!m_timerEntries.empty() && m_timerEntries.front().m_dueTime <= currentTime) { @@ -222,34 +222,34 @@ void cppcoro::io_service::timer_queue::dequeue_due_timers( } void cppcoro::io_service::timer_queue::remove_cancelled_timers( - cppcoro::io_service::timed_schedule_operation*& timerList) noexcept + cppcoro::io_service::timed_schedule_operation*& timerList) noexcept { // Perform a linear scan of all timers looking for any that have // had cancellation requested. const auto addTimerToList = [&](timed_schedule_operation* timer) - { - timer->m_next = timerList; - timerList = timer; - }; + { + timer->m_next = timerList; + timerList = timer; + }; const auto isTimerCancelled = [](const timer_entry& entry) - { - return entry.m_timer->m_cancellationToken.is_cancellation_requested(); - }; + { + return entry.m_timer->m_cancellationToken.is_cancellation_requested(); + }; auto firstCancelledEntry = std::find_if( - m_timerEntries.begin(), - m_timerEntries.end(), - isTimerCancelled); + m_timerEntries.begin(), + m_timerEntries.end(), + isTimerCancelled); if (firstCancelledEntry != m_timerEntries.end()) { auto nonCancelledEnd = firstCancelledEntry; addTimerToList(nonCancelledEnd->m_timer); for (auto iter = firstCancelledEntry + 1; - iter != m_timerEntries.end(); - ++iter) + iter != m_timerEntries.end(); + ++iter) { if (isTimerCancelled(*iter)) { @@ -264,9 +264,9 @@ void cppcoro::io_service::timer_queue::remove_cancelled_timers( m_timerEntries.erase(nonCancelledEnd, m_timerEntries.end()); std::make_heap( - m_timerEntries.begin(), - m_timerEntries.end(), - compare_entries); + m_timerEntries.begin(), + m_timerEntries.end(), + compare_entries); } { @@ -309,9 +309,9 @@ class cppcoro::io_service::timer_thread_state #endif #if CPPCORO_OS_LINUX - int m_wakeupfd; - int m_timerfd; - int m_epollfd; + int m_wakeupfd; + int m_timerfd; + int m_epollfd; #endif std::atomic m_newlyQueuedTimers; @@ -326,7 +326,7 @@ cppcoro::io_service::io_service() : io_service(0) #endif #if CPPCORO_OS_LINUX - : io_service(1) + : io_service(1) //queue size of message queue must be at least 1 #endif { } @@ -443,8 +443,8 @@ void cppcoro::io_service::stop() noexcept if ((oldState & stop_requested_flag) == 0) { for (auto activeThreadCount = oldState / active_thread_count_increment; - activeThreadCount > 0; - --activeThreadCount) + activeThreadCount > 0; + --activeThreadCount) { post_wake_up_event(); } @@ -486,71 +486,63 @@ cppcoro::detail::win32::handle_t cppcoro::io_service::native_iocp_handle() noexc void cppcoro::io_service::queue_overflow_operation_to_tail(schedule_operation* operation) noexcept { - // Still unable to queue these operations. - // Put them back on the list of overflow operations. - auto* tail = operation; - while (tail->m_next != nullptr) - { - tail = tail->m_next; - } + // Still unable to queue these operations. + // Put them back on the list of overflow operations. + auto* tail = operation; + while (tail->m_next != nullptr) + { + tail = tail->m_next; + } - schedule_operation* head = nullptr; - while (!m_scheduleOperations.compare_exchange_weak( - head, - operation, - std::memory_order_release, - std::memory_order_relaxed)) - { - tail->m_next = head; - } + schedule_operation* head = nullptr; + while (!m_scheduleOperations.compare_exchange_weak( + head, + operation, + std::memory_order_release, + std::memory_order_relaxed)) + { + tail->m_next = head; + } - return; + return; } void cppcoro::io_service::queue_overflow_operation_to_head(schedule_operation* operation) noexcept { - // Failed to post to the I/O completion port. - // - // This is most-likely because the queue is currently full. - // - // We'll queue up the operation to a linked-list using a lock-free - // push and defer the dispatch to the completion port until some I/O - // thread next enters its event loop. - auto* head = m_scheduleOperations.load(std::memory_order_acquire); - do - { - operation->m_next = head; - } while (!m_scheduleOperations.compare_exchange_weak( - head, - operation, - std::memory_order_release, - std::memory_order_acquire)); + // Failed to post to the I/O completion port. + // + // This is most-likely because the queue is currently full. + // + // We'll queue up the operation to a linked-list using a lock-free + // push and defer the dispatch to the completion port until some I/O + // thread next enters its event loop. + auto* head = m_scheduleOperations.load(std::memory_order_acquire); + do + { + operation->m_next = head; + } while (!m_scheduleOperations.compare_exchange_weak( + head, + operation, + std::memory_order_release, + std::memory_order_acquire)); } void cppcoro::io_service::schedule_impl(schedule_operation* operation) noexcept { #if CPPCORO_OS_LINUX - bool status = m_mq->enqueue_message(reinterpret_cast(operation->m_awaiter.address()), detail::linux::RESUME_TYPE); - - if(!status) - { - assert(errno == EAGAIN); - queue_overflow_operation_to_head(operation); - } - - return; -#endif -#if CPPCORO_OS_WINNT + const bool ok = m_mq->enqueue_message(reinterpret_cast(operation->m_awaiter.address()), + detail::linux::RESUME_TYPE); +#elif CPPCORO_OS_WINNT const BOOL ok = ::PostQueuedCompletionStatus( - m_iocpHandle.handle(), - 0, - reinterpret_cast(operation->m_awaiter.address()), - nullptr); + m_iocpHandle.handle(), + 0, + reinterpret_cast(operation->m_awaiter.address()), + nullptr); +#endif if (!ok) { - queue_overflow_operation_to_head(operation); + queue_overflow_operation_to_head(operation); } -#endif } void cppcoro::io_service::try_reschedule_overflow_operations() noexcept @@ -561,24 +553,20 @@ void cppcoro::io_service::try_reschedule_overflow_operations() noexcept auto* next = operation->m_next; #if CPPCORO_OS_WINNT BOOL ok = ::PostQueuedCompletionStatus( - m_iocpHandle.handle(), - 0, - reinterpret_cast(operation->m_awaiter.address()), - nullptr); - if (!ok) + m_iocpHandle.handle(), + 0, + reinterpret_cast(operation->m_awaiter.address()), + nullptr); +#elif CPPCORO_OS_LINUX + bool ok = m_mq->enqueue_message(reinterpret_cast(operation->m_awaiter.address()), + detail::linux::RESUME_TYPE); +#endif + + if(!ok) { - queue_overflow_operation_to_tail(operation); + queue_overflow_operation_to_tail(operation); } -#endif -#if CPPCORO_OS_LINUX - bool status = m_mq->enqueue_message(reinterpret_cast(operation->m_awaiter.address()), detail::linux::RESUME_TYPE); - if(!status) - { - assert(errno == EAGAIN); - queue_overflow_operation_to_tail(operation); - } -#endif operation = next; } } @@ -593,9 +581,9 @@ bool cppcoro::io_service::try_enter_event_loop() noexcept return false; } } while (!m_threadState.compare_exchange_weak( - currentState, - currentState + active_thread_count_increment, - std::memory_order_relaxed)); + currentState, + currentState + active_thread_count_increment, + std::memory_order_relaxed)); return true; } @@ -607,51 +595,52 @@ void cppcoro::io_service::exit_event_loop() noexcept bool cppcoro::io_service::try_process_one_event(bool waitForEvent) { - if(is_stop_requested()) - { - return false; - } + if(is_stop_requested()) + { + return false; + } #if CPPCORO_OS_LINUX - while(true) - { - try_reschedule_overflow_operations(); - void* message = NULL; - detail::linux::message_type type = detail::linux::RESUME_TYPE; + while(true) + { + try_reschedule_overflow_operations(); + void* message = NULL; + detail::linux::message_type type = detail::linux::RESUME_TYPE; - bool status = m_mq->dequeue_message(message, type, waitForEvent); + bool status = m_mq->dequeue_message(message, type, waitForEvent); - if(!status) - { - return false; - } - - if(type == detail::linux::CALLBACK_TYPE) - { - auto* state = - static_cast(reinterpret_cast(message)); - - state->m_callback(state); - - return true; - } - else - { - if((unsigned long long)message != 0) - { - std::experimental::coroutine_handle<>::from_address(reinterpret_cast(message)).resume(); - - return true; - } - - if (is_stop_requested()) - { - return false; - } + if(!status) + { + return false; + } + + if(type == detail::linux::CALLBACK_TYPE) + { + auto* state = + static_cast(reinterpret_cast(message)); + + state->m_callback(state); + + return true; + } + else + { + if((unsigned long long)message != 0) + { + std::experimental + ::coroutine_handle<>::from_address(reinterpret_cast(message)).resume(); + return true; + } + + if (is_stop_requested()) + { + return false; + } + } } - } #endif - + #if CPPCORO_OS_WINNT const DWORD timeout = waitForEvent ? INFINITE : 0; @@ -665,23 +654,24 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) ULONG_PTR completionKey = 0; LPOVERLAPPED overlapped = nullptr; BOOL ok = ::GetQueuedCompletionStatus( - m_iocpHandle.handle(), - &numberOfBytesTransferred, - &completionKey, - &overlapped, - timeout); + m_iocpHandle.handle(), + &numberOfBytesTransferred, + &completionKey, + &overlapped, + timeout); if (overlapped != nullptr) { DWORD errorCode = ok ? ERROR_SUCCESS : ::GetLastError(); - auto* state = static_cast( - reinterpret_cast(overlapped)); + auto* state = static_cast(reinterpret_cast(overlapped)); state->m_callback( - state, - errorCode, - numberOfBytesTransferred, - completionKey); + state, + errorCode, + numberOfBytesTransferred, + completionKey); return true; } @@ -691,8 +681,9 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) { // This was a coroutine scheduled via a call to // io_service::schedule(). - std::experimental::coroutine_handle<>::from_address( - reinterpret_cast(completionKey)).resume(); + std::experimental + ::coroutine_handle<> + ::from_address(reinterpret_cast(completionKey)).resume(); return true; } @@ -717,9 +708,9 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) throw std::system_error { static_cast(errorCode), - std::system_category(), - "Error retrieving item from io_service queue: GetQueuedCompletionStatus" - }; + std::system_category(), + "Error retrieving item from io_service queue: GetQueuedCompletionStatus" + }; } } #endif @@ -749,10 +740,10 @@ cppcoro::io_service::ensure_timer_thread_started() { auto newTimerState = std::make_unique(); if (m_timerState.compare_exchange_strong( - timerState, - newTimerState.get(), - std::memory_order_release, - std::memory_order_acquire)) + timerState, + newTimerState.get(), + std::memory_order_release, + std::memory_order_acquire)) { // We managed to install our timer_thread_state before some // other thread did, don't free it here - it will be freed in @@ -785,28 +776,28 @@ cppcoro::io_service::timer_thread_state::timer_thread_state() : wake_ev.data.fd = m_wakeupfd; if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_wakeupfd, &wake_ev) == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error creating io_service: epoll ctl wake ev" - }; - } + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: epoll ctl wake ev" + }; + } struct epoll_event timer_ev = {0}; timer_ev.events = EPOLLIN; timer_ev.data.fd = m_timerfd; if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_timerfd, &timer_ev) == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error creating io_service: epoll ctl timer ev" - }; - } + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: epoll ctl timer ev" + }; + } #endif } @@ -842,12 +833,12 @@ void cppcoro::io_service::timer_thread_state::run() #if CPPCORO_OS_WINNT const DWORD waitHandleCount = 2; const HANDLE waitHandles[waitHandleCount] = - { - m_wakeUpEvent.handle(), - m_waitableTimerEvent.handle() - }; - DWORD timeout = INFINITE; + { + m_wakeUpEvent.handle(), + m_waitableTimerEvent.handle() + }; #endif + DWORD timeout = INFINITE; time_point lastSetWaitEventTime = time_point::max(); @@ -855,54 +846,18 @@ void cppcoro::io_service::timer_thread_state::run() while (!m_shutDownRequested.load(std::memory_order_relaxed)) { + bool waitEvent = false; + bool timerEvent = false; #if CPPCORO_OS_WINNNT const DWORD waitResult = ::WaitForMultipleObjectsEx( - waitHandleCount, - waitHandles, - FALSE, // waitAll - timeout, - FALSE); // alertable -#endif - -#if CPPCORO_OS_LINUX - struct epoll_event ev; - const int status = epoll_wait(m_epollfd, &ev, 1, -1); + waitHandleCount, + waitHandles, + FALSE, // waitAll + timeout, + FALSE); // alertable - if(status == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error in timer thread: epoll wait" - }; - } -#endif - - -#if CPPCORO_OS_WINNT - if (waitResult == WAIT_OBJECT_0 || waitResult == WAIT_FAILED) + if(waitResult == WAIT_OBJECT_0 || waitResult == WAIT_FAILED) { -#endif -#if CPPCORO_OS_LINUX - if(status == 0) - { - continue; - } - - if (status == 1 && ev.data.fd == m_wakeupfd) - { - uint64_t count; - if(read(m_wakeupfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error in timer thread: eventfd read" - }; - } -#endif // Wake-up event (WAIT_OBJECT_0) // // We are only woken up for: @@ -913,8 +868,61 @@ void cppcoro::io_service::timer_thread_state::run() // We also handle WAIT_FAILED here so that we remain responsive // to new timers and cancellation even if the OS fails to perform // the wait operation for some reason. - // Handle cancelled timers + + waitEvent = true; + } + else if(waitResult == WAIT_OBJECT_0 + 1) + { + timerEvent = true; + } +#elif CPPCORO_OS_LINUX + struct epoll_event ev; + const int status = epoll_wait(m_epollfd, &ev, 1, timeout); + + if(status == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in timer thread: epoll wait" + }; + } + + if(status == 0 || (status == 1 && ev.data.fd == m_wakeupfd)) + { + uint64_t count; + if(read(m_wakeupfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in timer thread: eventfd read" + }; + } + + waitEvent = true; + } else if (status == 1 && ev.data.fd == m_timerfd) + { + uint64_t count; + if(read(m_timerfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in timer thread: timerfd read" + }; + } + + timerEvent = true; + } +#endif + + if(waitEvent) + { if (m_timerCancellationRequested.exchange(false, std::memory_order_acquire)) { timerQueue.remove_cancelled_timers(timersReadyToResume); @@ -937,30 +945,9 @@ void cppcoro::io_service::timer_thread_state::run() timerQueue.enqueue_timer(timer); } } - } - else if ( -#if CPPCORO_OS_WINNT - waitResult == (WAIT_OBJECT_0 + 1) -#endif -#if CPPCORO_OS_LINUX - ev.data.fd == m_timerfd -#endif - ) + } else if (timerEvent) { lastSetWaitEventTime = time_point::max(); - -#if CPPCORO_OS_LINUX - uint64_t count; - if(read(m_timerfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error in timer thread: timerfd read" - }; - } -#endif } if (!timerQueue.is_empty()) @@ -979,14 +966,17 @@ void cppcoro::io_service::timer_thread_state::run() // amount of time to wait until the next timer is ready. if (earliestDueTime != lastSetWaitEventTime) { - using ticks = std::chrono::duration>; + using ticks = std::chrono::duration>; auto timeUntilNextDueTime = earliestDueTime - currentTime; + bool ok = false; // Negative value indicates relative time. #if CPPCORO_OS_WINNT LARGE_INTEGER dueTime; - dueTime.QuadPart = -std::chrono::duration_cast(timeUntilNextDueTime).count(); + dueTime.QuadPart = -std::chrono::duration_cast + (timeUntilNextDueTime).count(); // Period of 0 indicates no repeat on the timer. const LONG period = 0; @@ -995,13 +985,41 @@ void cppcoro::io_service::timer_thread_state::run() // raise the timer event. const BOOL resumeFromSuspend = FALSE; - const BOOL ok = ::SetWaitableTimer( - m_waitableTimerEvent.handle(), - &dueTime, - period, - nullptr, - nullptr, - resumeFromSuspend); + BOOL ok = ::SetWaitableTimer( + m_waitableTimerEvent.handle(), + &dueTime, + period, + nullptr, + nullptr, + resumeFromSuspend); +#elif CPPCORO_OS_LINUX + struct itimerspec alarm_time = {0}; + alarm_time.it_value.tv_sec = + std::chrono:: + duration_cast(timeUntilNextDueTime).count(); + alarm_time.it_value.tv_nsec = + (std::chrono:: + duration_cast(timeUntilNextDueTime).count() + % 10000000) * 100; + if(alarm_time.it_value.tv_sec == 0 && alarm_time.it_value.tv_nsec == 0) + { + //linux timer of 0 time will not generate events + //so let's set it to 1 nsec + alarm_time.it_value.tv_nsec = 1; + } + + if(timerfd_settime(m_timerfd, 0, &alarm_time, NULL) == -1) + { + ok = false; + } + else + { + ok = true; + } + + lastSetWaitEventTime = earliestDueTime; +#endif if (ok) { lastSetWaitEventTime = earliestDueTime; @@ -1009,9 +1027,9 @@ void cppcoro::io_service::timer_thread_state::run() } else { - // Not sure what could cause the call to SetWaitableTimer() + // Not sure what could cause the call to waitable timer // to fail here but we'll just try falling back to using - // the timeout parameter of the WaitForMultipleObjects() call. + // the timeout parameter of the WaitFor or epoll call. // // wake-up at least once every second and retry setting // the timer at that point. @@ -1022,38 +1040,16 @@ void cppcoro::io_service::timer_thread_state::run() } else if (timeUntilNextDueTime > 1ms) { - timeout = static_cast( - std::chrono::duration_cast( - timeUntilNextDueTime).count()); + timeout = static_cast + (std::chrono:: + duration_cast(timeUntilNextDueTime).count()); } else { timeout = 1; } } -#endif -#if CPPCORO_OS_LINUX - struct itimerspec alarm_time = {0}; - alarm_time.it_value.tv_sec = std::chrono::duration_cast(timeUntilNextDueTime).count(); - alarm_time.it_value.tv_nsec = (std::chrono::duration_cast(timeUntilNextDueTime).count() % 10000000) * 100; - if(alarm_time.it_value.tv_sec == 0 && alarm_time.it_value.tv_nsec == 0) - { - //linux timer of 0 time will not generate events so let's set it to 1 nsec - alarm_time.it_value.tv_nsec = 1; - } - - if(timerfd_settime(m_timerfd, 0, &alarm_time, NULL) == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error in timer thread: timerfd_settime" - }; - } - - lastSetWaitEventTime = earliestDueTime; -#endif } } } @@ -1071,7 +1067,7 @@ void cppcoro::io_service::timer_thread_state::run() if (timer->m_refCount.fetch_sub(1, std::memory_order_release) == 1) { timer->m_scheduleOperation.m_service.schedule_impl( - &timer->m_scheduleOperation); + &timer->m_scheduleOperation); } timersReadyToResume = nextTimer; @@ -1083,32 +1079,31 @@ void cppcoro::io_service::timer_thread_state::wake_up_timer_thread() { #if CPPCORO_OS_WINNT (void)::SetEvent(m_wakeUpEvent.handle()); -#endif -#if CPPCORO_OS_LINUX +#elif CPPCORO_OS_LINUX uint64_t count = 1; if(write(m_wakeupfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error in waking up: writing to wake up eventfd" - }; - } + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in waking up: writing to wake up eventfd" + }; + } #endif } void cppcoro::io_service::schedule_operation::await_suspend( - std::experimental::coroutine_handle<> awaiter) noexcept + std::experimental::coroutine_handle<> awaiter) noexcept { m_awaiter = awaiter; m_service.schedule_impl(this); } cppcoro::io_service::timed_schedule_operation::timed_schedule_operation( - io_service& service, - std::chrono::high_resolution_clock::time_point resumeTime, - cppcoro::cancellation_token cancellationToken) noexcept + io_service& service, + std::chrono::high_resolution_clock::time_point resumeTime, + cppcoro::cancellation_token cancellationToken) noexcept : m_scheduleOperation(service) , m_resumeTime(resumeTime) , m_cancellationToken(std::move(cancellationToken)) @@ -1117,7 +1112,7 @@ cppcoro::io_service::timed_schedule_operation::timed_schedule_operation( } cppcoro::io_service::timed_schedule_operation::timed_schedule_operation( - timed_schedule_operation&& other) noexcept + timed_schedule_operation&& other) noexcept : m_scheduleOperation(std::move(other.m_scheduleOperation)) , m_resumeTime(std::move(other.m_resumeTime)) , m_cancellationToken(std::move(other.m_cancellationToken)) @@ -1135,7 +1130,7 @@ bool cppcoro::io_service::timed_schedule_operation::await_ready() const noexcept } void cppcoro::io_service::timed_schedule_operation::await_suspend( - std::experimental::coroutine_handle<> awaiter) + std::experimental::coroutine_handle<> awaiter) { m_scheduleOperation.m_awaiter = awaiter; @@ -1147,9 +1142,9 @@ void cppcoro::io_service::timed_schedule_operation::await_suspend( if (m_cancellationToken.can_be_cancelled()) { m_cancellationRegistration.emplace(m_cancellationToken, [timerState] - { - timerState->request_timer_cancellation(); - }); + { + timerState->request_timer_cancellation(); + }); } // Queue the timer schedule to the queue of incoming new timers. @@ -1179,10 +1174,10 @@ void cppcoro::io_service::timed_schedule_operation::await_suspend( { m_next = prev; } while (!timerState->m_newlyQueuedTimers.compare_exchange_weak( - prev, - this, - std::memory_order_release, - std::memory_order_acquire)); + prev, + this, + std::memory_order_release, + std::memory_order_acquire)); if (prev == nullptr) { From 26e6c723c8c6fc3590b05c0a4cc4374b4d0f6401 Mon Sep 17 00:00:00 2001 From: Anirudh Badam Date: Mon, 12 Feb 2018 20:20:31 +0000 Subject: [PATCH 04/34] conforming to the white space and indentation rules. --- include/cppcoro/detail/linux.hpp | 2 +- include/cppcoro/io_service.hpp | 32 +-- lib/linux.cpp | 366 ++++++++++++++--------------- test/scheduling_operator_tests.cpp | 294 +++++++++++------------ 4 files changed, 347 insertions(+), 347 deletions(-) diff --git a/include/cppcoro/detail/linux.hpp b/include/cppcoro/detail/linux.hpp index a8f14499..b40da11f 100644 --- a/include/cppcoro/detail/linux.hpp +++ b/include/cppcoro/detail/linux.hpp @@ -14,7 +14,7 @@ typedef int DWORD; #define INFINITE (DWORD)-1 - + namespace cppcoro { namespace detail diff --git a/include/cppcoro/io_service.hpp b/include/cppcoro/io_service.hpp index 99ab7168..316e264f 100644 --- a/include/cppcoro/io_service.hpp +++ b/include/cppcoro/io_service.hpp @@ -47,10 +47,10 @@ namespace cppcoro /// Note that the number of active threads may temporarily go /// above this number. #if CPPCORO_OS_WINNT - io_service(std::uint32_t concurrencyHint); + io_service(std::uint32_t concurrencyHint); #endif #if CPPCORO_OS_LINUX - io_service(size_t queue_length); + io_service(size_t queue_length); #endif ~io_service(); @@ -84,8 +84,8 @@ namespace cppcoro template [[nodiscard]] timed_schedule_operation schedule_after( - const std::chrono::duration& delay, - cancellation_token cancellationToken = {}) noexcept; + const std::chrono::duration& delay, + cancellation_token cancellationToken = {}) noexcept; /// Process events until the io_service is stopped. /// @@ -155,10 +155,10 @@ namespace cppcoro void try_reschedule_overflow_operations() noexcept; - void queue_overflow_operation_to_head(schedule_operation* operation) noexcept; + void queue_overflow_operation_to_head(schedule_operation* operation) noexcept; + + void queue_overflow_operation_to_tail(schedule_operation* operation) noexcept; - void queue_overflow_operation_to_tail(schedule_operation* operation) noexcept; - bool try_enter_event_loop() noexcept; void exit_event_loop() noexcept; @@ -182,7 +182,7 @@ namespace cppcoro #endif #if CPPCORO_OS_LINUX - detail::linux::message_queue* m_mq; + detail::linux::message_queue* m_mq; #endif // Head of a linked-list of schedule operations that are // ready to run but that failed to be queued to the I/O @@ -221,9 +221,9 @@ namespace cppcoro public: timed_schedule_operation( - io_service& service, - std::chrono::high_resolution_clock::time_point resumeTime, - cppcoro::cancellation_token cancellationToken) noexcept; + io_service& service, + std::chrono::high_resolution_clock::time_point resumeTime, + cppcoro::cancellation_token cancellationToken) noexcept; timed_schedule_operation(timed_schedule_operation&& other) noexcept; @@ -318,14 +318,14 @@ namespace cppcoro template cppcoro::io_service::timed_schedule_operation cppcoro::io_service::schedule_after( - const std::chrono::duration& duration, - cppcoro::cancellation_token cancellationToken) noexcept + const std::chrono::duration& duration, + cppcoro::cancellation_token cancellationToken) noexcept { return timed_schedule_operation{ *this, - std::chrono::high_resolution_clock::now() + duration, - std::move(cancellationToken) - }; + std::chrono::high_resolution_clock::now() + duration, + std::move(cancellationToken) + }; } #endif diff --git a/lib/linux.cpp b/lib/linux.cpp index 56d18daa..5bfdee67 100644 --- a/lib/linux.cpp +++ b/lib/linux.cpp @@ -6,188 +6,188 @@ namespace cppcoro { - namespace detail - { - namespace linux - { - message_queue::message_queue(size_t queue_length) - { - m_mqdt = -1; - uuid_t unique_name; - const char* cppcoro_qname_prefix = "/cppcoro-"; - - if(NAME_MAX < UUID_STRING_SIZE + sizeof(cppcoro_qname_prefix) + 1) - { - throw std::system_error - { - static_cast(EINVAL), - std::system_category(), - "Error creating message queue: system name max length too small" - }; - } - - strncpy(m_qname, cppcoro_qname_prefix, NAME_MAX); - - for(;;) - { - uuid_generate(unique_name); - uuid_unparse(unique_name, m_qname + sizeof(cppcoro_qname_prefix)); - - struct mq_attr attr; - attr.mq_flags = O_NONBLOCK; - attr.mq_maxmsg = queue_length; - attr.mq_msgsize = sizeof(cppcoro::detail::linux::message); - attr.mq_curmsgs = 0; - - m_mqdt = mq_open((const char*)m_qname, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, S_IRWXU, &attr); - - if( m_mqdt == -1 && errno == EEXIST) - { - continue; - } - - if( m_mqdt == -1) - { - throw std::system_error + namespace detail + { + namespace linux { - static_cast(errno), - std::system_category(), - "Error creating io_service: message queue open" - }; - } - - break; - } - - m_epollfd = create_epoll_fd(); - m_ev.data.fd = m_mqdt; - m_ev.events = EPOLLIN; - - if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_mqdt, &m_ev) == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error creating io_service: epoll ctl mqdt" - }; - } - } - - message_queue::~message_queue() - { - close(m_epollfd); - assert(mq_close(m_mqdt) == 0); - assert(mq_unlink(m_qname) == 0); - } - - bool message_queue::enqueue_message(void* msg, message_type type) - { - message qmsg; - qmsg.m_type = type; - qmsg.m_ptr = msg; - int status = mq_send(m_mqdt, (const char*)&qmsg, sizeof(message), 0); - return status==-1?false:true; - } - - bool message_queue::dequeue_message(void*& msg, message_type& type, bool wait) - { - struct epoll_event ev = {0}; - int nfds = epoll_wait(m_epollfd, &ev, 1, wait?-1:0); - - if(nfds == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error in epoll_wait run loop" - }; - } - - if(nfds == 0 && !wait) - { - return false; - } - - if(nfds == 0 && wait) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error in epoll_wait run loop" - }; - } - - message qmsg; - ssize_t status = mq_receive(m_mqdt, (char*)&qmsg, sizeof(message), NULL); - - if(status == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error retrieving message from message queue: mq_receive" - }; - } - - msg = qmsg.m_ptr; - type = qmsg.m_type; - return true; - } - - int create_event_fd() - { - int fd = eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK | EFD_CLOEXEC); - - if(fd == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error creating io_service: event fd create" - }; - } - - return fd; - } - - int create_timer_fd() - { - int fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); - - if(fd == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error creating io_service: timer fd create" - }; - } - - return fd; - } - - int create_epoll_fd() - { - int fd = epoll_create1(EPOLL_CLOEXEC); - - if(fd == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error creating timer thread: epoll create" - }; - } - - return fd; - } - } - } + message_queue::message_queue(size_t queue_length) + { + m_mqdt = -1; + uuid_t unique_name; + const char* cppcoro_qname_prefix = "/cppcoro-"; + + if(NAME_MAX < UUID_STRING_SIZE + sizeof(cppcoro_qname_prefix) + 1) + { + throw std::system_error + { + static_cast(EINVAL), + std::system_category(), + "Error creating message queue: system name max length too small" + }; + } + + strncpy(m_qname, cppcoro_qname_prefix, NAME_MAX); + + for(;;) + { + uuid_generate(unique_name); + uuid_unparse(unique_name, m_qname + sizeof(cppcoro_qname_prefix)); + + struct mq_attr attr; + attr.mq_flags = O_NONBLOCK; + attr.mq_maxmsg = queue_length; + attr.mq_msgsize = sizeof(cppcoro::detail::linux::message); + attr.mq_curmsgs = 0; + + m_mqdt = mq_open((const char*)m_qname, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, S_IRWXU, &attr); + + if( m_mqdt == -1 && errno == EEXIST) + { + continue; + } + + if( m_mqdt == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: message queue open" + }; + } + + break; + } + + m_epollfd = create_epoll_fd(); + m_ev.data.fd = m_mqdt; + m_ev.events = EPOLLIN; + + if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_mqdt, &m_ev) == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: epoll ctl mqdt" + }; + } + } + + message_queue::~message_queue() + { + close(m_epollfd); + assert(mq_close(m_mqdt) == 0); + assert(mq_unlink(m_qname) == 0); + } + + bool message_queue::enqueue_message(void* msg, message_type type) + { + message qmsg; + qmsg.m_type = type; + qmsg.m_ptr = msg; + int status = mq_send(m_mqdt, (const char*)&qmsg, sizeof(message), 0); + return status==-1?false:true; + } + + bool message_queue::dequeue_message(void*& msg, message_type& type, bool wait) + { + struct epoll_event ev = {0}; + int nfds = epoll_wait(m_epollfd, &ev, 1, wait?-1:0); + + if(nfds == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in epoll_wait run loop" + }; + } + + if(nfds == 0 && !wait) + { + return false; + } + + if(nfds == 0 && wait) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in epoll_wait run loop" + }; + } + + message qmsg; + ssize_t status = mq_receive(m_mqdt, (char*)&qmsg, sizeof(message), NULL); + + if(status == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error retrieving message from message queue: mq_receive" + }; + } + + msg = qmsg.m_ptr; + type = qmsg.m_type; + return true; + } + + int create_event_fd() + { + int fd = eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK | EFD_CLOEXEC); + + if(fd == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: event fd create" + }; + } + + return fd; + } + + int create_timer_fd() + { + int fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); + + if(fd == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: timer fd create" + }; + } + + return fd; + } + + int create_epoll_fd() + { + int fd = epoll_create1(EPOLL_CLOEXEC); + + if(fd == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating timer thread: epoll create" + }; + } + + return fd; + } + } + } } diff --git a/test/scheduling_operator_tests.cpp b/test/scheduling_operator_tests.cpp index 39e32265..a3a1e9a1 100644 --- a/test/scheduling_operator_tests.cpp +++ b/test/scheduling_operator_tests.cpp @@ -30,11 +30,11 @@ TEST_SUITE_BEGIN("schedule/resume_on"); static unsigned long long get_thread_id() { - unsigned long long id; - std::stringstream ss; - ss << std::this_thread::get_id(); - id = std::stoull(ss.str()); - return id; + unsigned long long id; + std::stringstream ss; + ss << std::this_thread::get_id(); + id = std::stoull(ss.str()); + return id; } #endif @@ -45,20 +45,20 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on task<> function") THREAD_ID ioThreadId; auto start = [&]() -> cppcoro::task<> - { - ioThreadId = GET_THIS_THREAD_ID; - CHECK(ioThreadId != mainThreadId); - co_return; - }; + { + ioThreadId = GET_THIS_THREAD_ID; + CHECK(ioThreadId != mainThreadId); + co_return; + }; cppcoro::sync_wait([&]() -> cppcoro::task<> - { - CHECK(GET_THIS_THREAD_ID == mainThreadId); + { + CHECK(GET_THIS_THREAD_ID == mainThreadId); - co_await schedule_on(io_service(), start()); + co_await schedule_on(io_service(), start()); - CHECK(GET_THIS_THREAD_ID == ioThreadId); - }()); + CHECK(GET_THIS_THREAD_ID == ioThreadId); + }()); } TEST_CASE_FIXTURE(io_service_fixture, "schedule_on async_generator<> function") @@ -68,52 +68,52 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on async_generator<> function") THREAD_ID ioThreadId; auto makeSequence = [&]() -> cppcoro::async_generator - { - ioThreadId = GET_THIS_THREAD_ID; - CHECK(ioThreadId != mainThreadId); + { + ioThreadId = GET_THIS_THREAD_ID; + CHECK(ioThreadId != mainThreadId); - co_yield 1; + co_yield 1; - CHECK(GET_THIS_THREAD_ID == ioThreadId); + CHECK(GET_THIS_THREAD_ID == ioThreadId); - co_yield 2; + co_yield 2; - CHECK(GET_THIS_THREAD_ID == ioThreadId); + CHECK(GET_THIS_THREAD_ID == ioThreadId); - co_yield 3; + co_yield 3; - CHECK(GET_THIS_THREAD_ID == ioThreadId); + CHECK(GET_THIS_THREAD_ID == ioThreadId); - co_return; - }; + co_return; + }; cppcoro::io_service otherIoService; cppcoro::sync_wait(cppcoro::when_all_ready( - [&]() -> cppcoro::task<> - { - CHECK(GET_THIS_THREAD_ID == mainThreadId); - - auto seq = schedule_on(io_service(), makeSequence()); - - int expected = 1; - for co_await(int value : seq) - { - CHECK(value == expected++); - - // Transfer exection back to main thread before - // awaiting next item in the loop to chck that - // the generator is resumed on io_service() thread. - co_await otherIoService.schedule(); - } - - otherIoService.stop(); - }(), - [&]() -> cppcoro::task<> - { - otherIoService.process_events(); - co_return; - }())); + [&]() -> cppcoro::task<> + { + CHECK(GET_THIS_THREAD_ID == mainThreadId); + + auto seq = schedule_on(io_service(), makeSequence()); + + int expected = 1; + for co_await(int value : seq) + { + CHECK(value == expected++); + + // Transfer exection back to main thread before + // awaiting next item in the loop to chck that + // the generator is resumed on io_service() thread. + co_await otherIoService.schedule(); + } + + otherIoService.stop(); + }(), + [&]() -> cppcoro::task<> + { + otherIoService.process_events(); + co_return; + }())); } TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> function") @@ -121,19 +121,19 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> function") auto mainThreadId = GET_THIS_THREAD_ID; auto start = [&]() -> cppcoro::task<> - { - CHECK(GET_THIS_THREAD_ID == mainThreadId); - co_return; - }; + { + CHECK(GET_THIS_THREAD_ID == mainThreadId); + co_return; + }; cppcoro::sync_wait([&]() -> cppcoro::task<> - { - CHECK(GET_THIS_THREAD_ID == mainThreadId); + { + CHECK(GET_THIS_THREAD_ID == mainThreadId); - co_await resume_on(io_service(), start()); + co_await resume_on(io_service(), start()); - CHECK(GET_THIS_THREAD_ID != mainThreadId); - }()); + CHECK(GET_THIS_THREAD_ID != mainThreadId); + }()); } TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function") @@ -143,60 +143,60 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function") THREAD_ID ioThreadId; auto makeSequence = [&]() -> cppcoro::async_generator - { - co_await io_service().schedule(); + { + co_await io_service().schedule(); - ioThreadId = GET_THIS_THREAD_ID; + ioThreadId = GET_THIS_THREAD_ID; - CHECK(ioThreadId != mainThreadId); + CHECK(ioThreadId != mainThreadId); - co_yield 1; + co_yield 1; - co_yield 2; + co_yield 2; - co_await io_service().schedule(); + co_await io_service().schedule(); - co_yield 3; + co_yield 3; - co_await io_service().schedule(); + co_await io_service().schedule(); - co_return; - }; + co_return; + }; cppcoro::io_service otherIoService; cppcoro::sync_wait(cppcoro::when_all_ready( - [&]() -> cppcoro::task<> - { - auto stopOnExit = cppcoro::on_scope_exit([&] { otherIoService.stop(); }); - - CHECK(GET_THIS_THREAD_ID == mainThreadId); - - auto seq = resume_on(otherIoService, makeSequence()); - - int expected = 1; - for co_await(int value : seq) - { - // Every time we receive a value it should be on our requested - // scheduler (ie. main thread) - CHECK(GET_THIS_THREAD_ID == mainThreadId); - CHECK(value == expected++); - - // Occasionally transfer execution to a different thread before - // awaiting next element. - if (value == 2) - { - co_await io_service().schedule(); - } - } - - otherIoService.stop(); - }(), - [&]() -> cppcoro::task<> - { - otherIoService.process_events(); - co_return; - }())); + [&]() -> cppcoro::task<> + { + auto stopOnExit = cppcoro::on_scope_exit([&] { otherIoService.stop(); }); + + CHECK(GET_THIS_THREAD_ID == mainThreadId); + + auto seq = resume_on(otherIoService, makeSequence()); + + int expected = 1; + for co_await(int value : seq) + { + // Every time we receive a value it should be on our requested + // scheduler (ie. main thread) + CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(value == expected++); + + // Occasionally transfer execution to a different thread before + // awaiting next element. + if (value == 2) + { + co_await io_service().schedule(); + } + } + + otherIoService.stop(); + }(), + [&]() -> cppcoro::task<> + { + otherIoService.process_events(); + co_return; + }())); } TEST_CASE_FIXTURE(io_service_fixture, "schedule_on task<> pipe syntax") @@ -204,16 +204,16 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on task<> pipe syntax") auto mainThreadId = GET_THIS_THREAD_ID; auto makeTask = [&]() -> cppcoro::task - { - CHECK(GET_THIS_THREAD_ID != mainThreadId); - co_return 123; - }; + { + CHECK(GET_THIS_THREAD_ID != mainThreadId); + co_return 123; + }; auto triple = [&](int x) - { - CHECK(GET_THIS_THREAD_ID != mainThreadId); - return x * 3; - }; + { + CHECK(GET_THIS_THREAD_ID != mainThreadId); + return x * 3; + }; CHECK(cppcoro::sync_wait(makeTask() | schedule_on(io_service())) == 123); @@ -228,17 +228,17 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax") auto mainThreadId = GET_THIS_THREAD_ID; auto makeTask = [&]() -> cppcoro::task - { - CHECK(GET_THIS_THREAD_ID == mainThreadId); - co_return 123; - }; + { + CHECK(GET_THIS_THREAD_ID == mainThreadId); + co_return 123; + }; cppcoro::sync_wait([&]() -> cppcoro::task<> - { - cppcoro::task t = makeTask() | cppcoro::resume_on(io_service()); - CHECK(co_await t == 123); - CHECK(GET_THIS_THREAD_ID != mainThreadId); - }()); + { + cppcoro::task t = makeTask() | cppcoro::resume_on(io_service()); + CHECK(co_await t == 123); + CHECK(GET_THIS_THREAD_ID != mainThreadId); + }()); } TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax multiple uses") @@ -246,41 +246,41 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax multiple use auto mainThreadId = GET_THIS_THREAD_ID; auto makeTask = [&]() -> cppcoro::task - { - CHECK(GET_THIS_THREAD_ID == mainThreadId); - co_return 123; - }; + { + CHECK(GET_THIS_THREAD_ID == mainThreadId); + co_return 123; + }; auto triple = [&](int x) - { - CHECK(GET_THIS_THREAD_ID != mainThreadId); - return x * 3; - }; + { + CHECK(GET_THIS_THREAD_ID != mainThreadId); + return x * 3; + }; cppcoro::io_service otherIoService; cppcoro::sync_wait(cppcoro::when_all_ready( - [&]() -> cppcoro::task<> - { - auto stopOnExit = cppcoro::on_scope_exit([&] { otherIoService.stop(); }); - - CHECK(GET_THIS_THREAD_ID == mainThreadId); - - cppcoro::task t = - makeTask() - | cppcoro::resume_on(io_service()) - | cppcoro::fmap(triple) - | cppcoro::resume_on(otherIoService); - - CHECK(co_await t == 369); - - CHECK(GET_THIS_THREAD_ID == mainThreadId); - }(), - [&]() -> cppcoro::task<> - { - otherIoService.process_events(); - co_return; - }())); + [&]() -> cppcoro::task<> + { + auto stopOnExit = cppcoro::on_scope_exit([&] { otherIoService.stop(); }); + + CHECK(GET_THIS_THREAD_ID == mainThreadId); + + cppcoro::task t = + makeTask() + | cppcoro::resume_on(io_service()) + | cppcoro::fmap(triple) + | cppcoro::resume_on(otherIoService); + + CHECK(co_await t == 369); + + CHECK(GET_THIS_THREAD_ID == mainThreadId); + }(), + [&]() -> cppcoro::task<> + { + otherIoService.process_events(); + co_return; + }())); } TEST_SUITE_END(); From 3ecdf4a1a44801cdf14cf6eaf6fd74047add95f2 Mon Sep 17 00:00:00 2001 From: Lewis Baker Date: Fri, 16 Feb 2018 11:53:50 +1030 Subject: [PATCH 05/34] Update .travis.yml to refer to clang-7 The clang mainline has now been branched for clang-6 and mainline now uses clang-7 version. --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4cc705a3..dccabc4b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,8 @@ addons: - llvm-toolchain-trusty packages: - python2.7 - - clang-6.0 - - lld-6.0 + - clang-7 + - lld-7 - ninja-build cache: @@ -42,7 +42,7 @@ matrix: allow_failures: # Clang 6.0~svn320382 has a bug that causes optimised builds to fail. # See https://bugs.llvm.org/show_bug.cgi?id=34897 - - env: RELEASE=optimised ARCH=x64 CLANG_VERSION=6.0 + - env: RELEASE=optimised ARCH=x64 CLANG_VERSION=7.0 before_install: - export CC="$CC-$CLANG_VERSION" From 377deb81c068c7d3b13693ebf0895e182e6c1c39 Mon Sep 17 00:00:00 2001 From: Lewis Baker Date: Sun, 18 Feb 2018 08:29:17 +1030 Subject: [PATCH 06/34] Try clang-7.0 instead of clang-7 package name --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index dccabc4b..dbaf73ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,8 @@ addons: - llvm-toolchain-trusty packages: - python2.7 - - clang-7 - - lld-7 + - clang-7.0 + - lld-7.0 - ninja-build cache: @@ -37,8 +37,8 @@ env: - CMAKE_VERSION="3.9.1" matrix: include: - - env: RELEASE=debug ARCH=x64 CLANG_VERSION=6.0 - - env: RELEASE=optimised ARCH=x64 CLANG_VERSION=6.0 + - env: RELEASE=debug ARCH=x64 CLANG_VERSION=7.0 + - env: RELEASE=optimised ARCH=x64 CLANG_VERSION=7.0 allow_failures: # Clang 6.0~svn320382 has a bug that causes optimised builds to fail. # See https://bugs.llvm.org/show_bug.cgi?id=34897 From 48e1ef57549e40558414cb2a0d66e4a4b8d844a4 Mon Sep 17 00:00:00 2001 From: Lewis Baker Date: Sun, 25 Feb 2018 09:10:00 +1030 Subject: [PATCH 07/34] Fix clang-7 apt package names again --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index dbaf73ac..a7c2bd84 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,8 @@ addons: - llvm-toolchain-trusty packages: - python2.7 - - clang-7.0 - - lld-7.0 + - clang-7 + - lld-7 - ninja-build cache: From 7d0f2dfad17d9a473a06827dcaeb7deda07ffe8d Mon Sep 17 00:00:00 2001 From: Lewis Baker Date: Sun, 25 Feb 2018 09:15:51 +1030 Subject: [PATCH 08/34] Try setting CLANG_VERSION=7 instead of 7.0 in Travis config --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index a7c2bd84..bf1f6c3e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,12 +37,12 @@ env: - CMAKE_VERSION="3.9.1" matrix: include: - - env: RELEASE=debug ARCH=x64 CLANG_VERSION=7.0 - - env: RELEASE=optimised ARCH=x64 CLANG_VERSION=7.0 + - env: RELEASE=debug ARCH=x64 CLANG_VERSION=7 + - env: RELEASE=optimised ARCH=x64 CLANG_VERSION=7 allow_failures: # Clang 6.0~svn320382 has a bug that causes optimised builds to fail. # See https://bugs.llvm.org/show_bug.cgi?id=34897 - - env: RELEASE=optimised ARCH=x64 CLANG_VERSION=7.0 + - env: RELEASE=optimised ARCH=x64 CLANG_VERSION=7 before_install: - export CC="$CC-$CLANG_VERSION" From 862e99d8173ecb62a39abfd2b543de66bad98adc Mon Sep 17 00:00:00 2001 From: Brian Gesiak Date: Mon, 26 Feb 2018 21:33:03 -0500 Subject: [PATCH 09/34] [README] Add C++ highlighting for 'schedule_on' (#67) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 435f764d..b4fa93b1 100644 --- a/README.md +++ b/README.md @@ -1854,7 +1854,7 @@ task<> example() ``` API Summary: -``` +```c++ // namespace cppcoro { From 936e6eeb1026552e1dd0acc526f0ca2b79d909b4 Mon Sep 17 00:00:00 2001 From: Attila Date: Fri, 9 Mar 2018 17:32:43 +0100 Subject: [PATCH 10/34] Remove possible warnings with redefinitions If the library is used as part of a bigger project some definitions can be provided via external build files. --- lib/file.cpp | 4 +++- lib/file_read_operation.cpp | 4 +++- lib/file_write_operation.cpp | 4 +++- lib/io_service.cpp | 8 ++++++-- lib/lightweight_manual_reset_event.cpp | 4 +++- lib/read_only_file.cpp | 4 +++- lib/read_write_file.cpp | 4 +++- lib/win32.cpp | 4 +++- lib/writable_file.cpp | 4 +++- lib/write_only_file.cpp | 4 +++- 10 files changed, 33 insertions(+), 11 deletions(-) diff --git a/lib/file.cpp b/lib/file.cpp index c53a89f6..3583217f 100644 --- a/lib/file.cpp +++ b/lib/file.cpp @@ -10,7 +10,9 @@ #include #if CPPCORO_OS_WINNT -# define WIN32_LEAN_AND_MEAN +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif # include #endif diff --git a/lib/file_read_operation.cpp b/lib/file_read_operation.cpp index 399d914e..fb9e32cc 100644 --- a/lib/file_read_operation.cpp +++ b/lib/file_read_operation.cpp @@ -6,7 +6,9 @@ #include #if CPPCORO_OS_WINNT -# define WIN32_LEAN_AND_MEAN +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif # include bool cppcoro::file_read_operation::try_start() noexcept diff --git a/lib/file_write_operation.cpp b/lib/file_write_operation.cpp index 92e93660..fd9b465e 100644 --- a/lib/file_write_operation.cpp +++ b/lib/file_write_operation.cpp @@ -6,7 +6,9 @@ #include #if CPPCORO_OS_WINNT -# define WIN32_LEAN_AND_MEAN +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif # include bool cppcoro::file_write_operation::try_start() noexcept diff --git a/lib/io_service.cpp b/lib/io_service.cpp index 8031775e..cc7facef 100644 --- a/lib/io_service.cpp +++ b/lib/io_service.cpp @@ -12,8 +12,12 @@ #include #if CPPCORO_OS_WINNT -# define WIN32_LEAN_AND_MEAN -# define NOMINMAX +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# ifndef NOMINMAX +# define NOMINMAX +# endif # include #endif diff --git a/lib/lightweight_manual_reset_event.cpp b/lib/lightweight_manual_reset_event.cpp index 0d356005..1b6d0d9c 100644 --- a/lib/lightweight_manual_reset_event.cpp +++ b/lib/lightweight_manual_reset_event.cpp @@ -8,7 +8,9 @@ #include #if CPPCORO_OS_WINNT -# define WIN32_LEAN_AND_MEAN +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif # include # if CPPCORO_OS_WINNT >= 0x0602 diff --git a/lib/read_only_file.cpp b/lib/read_only_file.cpp index 058e024c..614aa48a 100644 --- a/lib/read_only_file.cpp +++ b/lib/read_only_file.cpp @@ -6,7 +6,9 @@ #include #if CPPCORO_OS_WINNT -# define WIN32_LEAN_AND_MEAN +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif # include cppcoro::read_only_file cppcoro::read_only_file::open( diff --git a/lib/read_write_file.cpp b/lib/read_write_file.cpp index 4c3815e1..231e6790 100644 --- a/lib/read_write_file.cpp +++ b/lib/read_write_file.cpp @@ -6,7 +6,9 @@ #include #if CPPCORO_OS_WINNT -# define WIN32_LEAN_AND_MEAN +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif # include cppcoro::read_write_file cppcoro::read_write_file::open( diff --git a/lib/win32.cpp b/lib/win32.cpp index 4a3d183d..b7b497bb 100644 --- a/lib/win32.cpp +++ b/lib/win32.cpp @@ -5,7 +5,9 @@ #include -#define WIN32_LEAN_AND_MEAN +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif #include void cppcoro::detail::win32::safe_handle::close() noexcept diff --git a/lib/writable_file.cpp b/lib/writable_file.cpp index b1bd2b8e..3ba63e7c 100644 --- a/lib/writable_file.cpp +++ b/lib/writable_file.cpp @@ -8,7 +8,9 @@ #include #if CPPCORO_OS_WINNT -# define WIN32_LEAN_AND_MEAN +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif # include void cppcoro::writable_file::set_size( diff --git a/lib/write_only_file.cpp b/lib/write_only_file.cpp index c30f3920..4c91bddf 100644 --- a/lib/write_only_file.cpp +++ b/lib/write_only_file.cpp @@ -6,7 +6,9 @@ #include #if CPPCORO_OS_WINNT -# define WIN32_LEAN_AND_MEAN +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif # include cppcoro::write_only_file cppcoro::write_only_file::open( From 4963e3342277722e993b4bf931053a62ff99de06 Mon Sep 17 00:00:00 2001 From: Lewis Baker Date: Tue, 27 Mar 2018 11:47:42 +1030 Subject: [PATCH 11/34] Update appveyor.yml to ignore x86 optimised failures Several tests are currently failing due to what seems to be compiler bugs. Ignore tests for now and revisit once MSVC 15.7 is released. --- appveyor.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 4e2a5001..b9a27d65 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,3 @@ - version: 1.0.{build} image: @@ -13,6 +12,13 @@ configuration: - debug - optimised +matrix: + # Allow failures under MSVC x86 optimised since there are some known compiler + # bugs causing failures here. + allow_failures: + - platform: x86 + configuration: optimised + clone_script: - ps: git clone -q $("--branch=" + $env:APPVEYOR_REPO_BRANCH) $("https://github.com/" + $env:APPVEYOR_REPO_NAME + ".git") $env:APPVEYOR_BUILD_FOLDER - ps: if (!$env:APPVEYOR_PULL_REQUEST_NUMBER) {$("git checkout -qf " + $env:APPVEYOR_REPO_COMMIT)} From a4d2c7b0034844199a7f3e3518cfe2a9b0f42d5b Mon Sep 17 00:00:00 2001 From: Attila Date: Thu, 29 Mar 2018 15:48:22 +0200 Subject: [PATCH 12/34] Correct file_write_operation's friend class --- include/cppcoro/file_write_operation.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/cppcoro/file_write_operation.hpp b/include/cppcoro/file_write_operation.hpp index 2b6a5a0a..49b23883 100644 --- a/include/cppcoro/file_write_operation.hpp +++ b/include/cppcoro/file_write_operation.hpp @@ -66,7 +66,7 @@ namespace cppcoro private: - friend class cppcoro::detail::win32_overlapped_operation; + friend class cppcoro::detail::win32_overlapped_operation_cancellable; bool try_start() noexcept; void cancel() noexcept; From b0e5f765ba2a5d671eb11b1dbf8a4a2a6b5832ac Mon Sep 17 00:00:00 2001 From: Attila Date: Thu, 8 Mar 2018 13:53:59 +0100 Subject: [PATCH 13/34] Fix typo in async_generator state description --- include/cppcoro/async_generator.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/cppcoro/async_generator.hpp b/include/cppcoro/async_generator.hpp index 49cd05ce..f188d195 100644 --- a/include/cppcoro/async_generator.hpp +++ b/include/cppcoro/async_generator.hpp @@ -117,8 +117,8 @@ namespace cppcoro // State transition diagram // VNRCA - value_not_ready_consumer_active // VNRCS - value_not_ready_consumer_suspended - // VRPA - value_ready_consumer_active - // VRPS - value_ready_consumer_suspended + // VRPA - value_ready_producer_active + // VRPS - value_ready_producer_suspended // // A +--- VNRCA --[C]--> VNRCS yield_value() // | | | A | A | . From 14ad4bd5dd7e1e665f59c31bbab8cd1bb11ccca0 Mon Sep 17 00:00:00 2001 From: grishavanika Date: Thu, 5 Apr 2018 09:44:57 +0300 Subject: [PATCH 14/34] Fix io_service_fixture_with_threads: create given threads count (#75) --- test/io_service_fixture.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/io_service_fixture.hpp b/test/io_service_fixture.hpp index 05f7fb0b..9552ebeb 100644 --- a/test/io_service_fixture.hpp +++ b/test/io_service_fixture.hpp @@ -25,7 +25,10 @@ struct io_service_fixture m_ioThreads.reserve(threadCount); try { - m_ioThreads.emplace_back([this] { m_ioService.process_events(); }); + for (std::uint32_t i = 0; i < threadCount; ++i) + { + m_ioThreads.emplace_back([this] { m_ioService.process_events(); }); + } } catch (...) { From bce2a728179c7a0a892adaf3432968b8725e71aa Mon Sep 17 00:00:00 2001 From: Lewis Baker Date: Fri, 6 Apr 2018 15:25:51 +0930 Subject: [PATCH 15/34] Replace with in doctest.h to try and fix linker errors under Linux (#78) --- test/doctest/doctest.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/doctest/doctest.h b/test/doctest/doctest.h index 7ab30067..e70d3f10 100644 --- a/test/doctest/doctest.h +++ b/test/doctest/doctest.h @@ -418,7 +418,8 @@ extern "C" __declspec(dllimport) void __stdcall DebugBreak(); #ifdef _LIBCPP_VERSION // not forward declaring ostream for libc++ because I had some problems (inline namespaces vs c++98) // so the header is used - also it is very light and doesn't drag a ton of stuff -#include +//#include +#include #else // _LIBCPP_VERSION #ifndef DOCTEST_CONFIG_USE_IOSFWD namespace std From 5aa1cd1ff925c07cc21a2968784c0239e4feb139 Mon Sep 17 00:00:00 2001 From: Lewis Baker Date: Fri, 6 Apr 2018 15:37:00 +0930 Subject: [PATCH 16/34] [AppVeyor] Reenable VS 2017 Preview build variants The AppVeyor 'Visual Studio 2017 Preview' image has now been updated with VS 2017.7 Preview 2 --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index b9a27d65..4ad544d7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ version: 1.0.{build} image: - Visual Studio 2017 -#- Visual Studio 2017 Preview +- Visual Studio 2017 Preview platform: - x64 From 2492c0782911cdf7fe803642040efff3faa3c28a Mon Sep 17 00:00:00 2001 From: Lewis Baker Date: Mon, 9 Apr 2018 11:55:53 +0930 Subject: [PATCH 17/34] Link against vcruntime[d].lib instead of msvcurt[d].lib The msvcurt[d].lib is actually intended for C++/CLI usage and seems to no longer being bundled with the default VS install in more recent Visual Studio 2017 installs. Should fix #73 --- config.cake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.cake b/config.cake index a9a0925b..58c31558 100644 --- a/config.cake +++ b/config.cake @@ -139,7 +139,7 @@ if cake.system.isWindows() or cake.system.isCygwin(): compiler.runtimeLibraries = 'debug-dll' compiler.addLibrary('msvcrtd') compiler.addLibrary('msvcprtd') - compiler.addLibrary('msvcurtd') + compiler.addLibrary('vcruntimed') compiler.addLibrary('ucrtd') compiler.addLibrary('oldnames') @@ -161,7 +161,7 @@ if cake.system.isWindows() or cake.system.isCygwin(): compiler.runtimeLibraries = 'release-dll' compiler.addLibrary('msvcrt') compiler.addLibrary('msvcprt') - compiler.addLibrary('msvcurt') + compiler.addLibrary('vcruntime') compiler.addLibrary('ucrt') compiler.addLibrary('oldnames') From 115aa391f0fb6b275f6126d47e37bb6d79ca6900 Mon Sep 17 00:00:00 2001 From: Anirudh Badam Date: Wed, 11 Apr 2018 21:17:07 +0000 Subject: [PATCH 18/34] fixed the minor comments from lewis. --- include/cppcoro/detail/linux.hpp | 82 +++++++++++++++++++++++++++--- lib/io_service.cpp | 64 ++++++++++------------- lib/linux.cpp | 32 +++++++----- test/scheduling_operator_tests.cpp | 72 +++++++++++++------------- 4 files changed, 158 insertions(+), 92 deletions(-) diff --git a/include/cppcoro/detail/linux.hpp b/include/cppcoro/detail/linux.hpp index b40da11f..caba438e 100644 --- a/include/cppcoro/detail/linux.hpp +++ b/include/cppcoro/detail/linux.hpp @@ -11,9 +11,7 @@ #include #include #include - -typedef int DWORD; -#define INFINITE (DWORD)-1 +#include namespace cppcoro { @@ -21,12 +19,81 @@ namespace cppcoro { namespace linux { + using fd_t = int; + enum message_type { CALLBACK_TYPE, RESUME_TYPE }; + class safe_fd + { + public: + + safe_fd() + : m_fd(-1) + {} + + explicit safe_fd(fd_t fd) + : m_fd(fd) + {} + + safe_fd(const safe_fd& other) = delete; + + safe_fd(safe_fd&& other) noexcept + : m_fd(other.m_fd) + { + other.m_fd = -1; + } + + ~safe_fd() + { + close(); + } + + safe_fd& operator=(safe_fd fd) noexcept + { + swap(fd); + return *this; + } + + constexpr fd_t fd() const { return m_fd; } + + /// Calls close() and sets the fd to -1. + void close() noexcept; + + void swap(safe_fd& other) noexcept + { + std::swap(m_fd, other.m_fd); + } + + bool operator==(const safe_fd& other) const + { + return m_fd == other.m_fd; + } + + bool operator!=(const safe_fd& other) const + { + return m_fd != other.m_fd; + } + + bool operator==(fd_t fd) const + { + return m_fd == fd; + } + + bool operator!=(fd_t fd) const + { + return m_fd != fd; + } + + private: + + fd_t m_fd; + + }; + struct message { enum message_type m_type; @@ -44,7 +111,7 @@ namespace cppcoro private: mqd_t m_mqdt; char m_qname[NAME_MAX]; - int m_epollfd; + safe_fd m_epollfd; struct epoll_event m_ev; message_queue(); public: @@ -54,10 +121,9 @@ namespace cppcoro bool dequeue_message(void*& message, message_type& type, bool wait); }; - int create_event_fd(); - int create_timer_fd(); - int create_epoll_fd(); - + safe_fd create_event_fd(); + safe_fd create_timer_fd(); + safe_fd create_epoll_fd(); } } } diff --git a/lib/io_service.cpp b/lib/io_service.cpp index 2f27ecde..89fb9402 100644 --- a/lib/io_service.cpp +++ b/lib/io_service.cpp @@ -18,6 +18,11 @@ # include #endif +#if CPPCORO_OS_LINUX +typedef int DWORD; +#define INFINITE (DWORD)-1 //needed for timeout values in io_service::timer_thread_state::run() +#endif + namespace { #if CPPCORO_OS_WINNT @@ -301,7 +306,7 @@ class cppcoro::io_service::timer_thread_state void run(); - void wake_up_timer_thread(); + void wake_up_timer_thread() noexcept; #if CPPCORO_OS_WINNT detail::win32::safe_handle m_wakeUpEvent; @@ -309,9 +314,9 @@ class cppcoro::io_service::timer_thread_state #endif #if CPPCORO_OS_LINUX - int m_wakeupfd; - int m_timerfd; - int m_epollfd; + detail::linux::safe_fd m_wakeupfd; + detail::linux::safe_fd m_timerfd; + detail::linux::safe_fd m_epollfd; #endif std::atomic m_newlyQueuedTimers; @@ -326,7 +331,7 @@ cppcoro::io_service::io_service() : io_service(0) #endif #if CPPCORO_OS_LINUX - : io_service(1) //queue size of message queue must be at least 1 + : io_service(10) //queue size of message queue must be at least 10 #endif { } @@ -771,11 +776,11 @@ cppcoro::io_service::timer_thread_state::timer_thread_state() : , m_thread([this] { this->run(); }) { #if CPPCORO_OS_LINUX - struct epoll_event wake_ev = {0}; + epoll_event wake_ev = {0}; wake_ev.events = EPOLLIN; - wake_ev.data.fd = m_wakeupfd; + wake_ev.data.fd = m_wakeupfd.fd(); - if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_wakeupfd, &wake_ev) == -1) + if(epoll_ctl(m_epollfd.fd(), EPOLL_CTL_ADD, m_wakeupfd.fd(), &wake_ev) == -1) { throw std::system_error { @@ -785,11 +790,11 @@ cppcoro::io_service::timer_thread_state::timer_thread_state() : }; } - struct epoll_event timer_ev = {0}; + epoll_event timer_ev = {0}; timer_ev.events = EPOLLIN; - timer_ev.data.fd = m_timerfd; + timer_ev.data.fd = m_timerfd.fd(); - if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_timerfd, &timer_ev) == -1) + if(epoll_ctl(m_epollfd.fd(), EPOLL_CTL_ADD, m_timerfd.fd(), &timer_ev) == -1) { throw std::system_error { @@ -806,11 +811,6 @@ cppcoro::io_service::timer_thread_state::~timer_thread_state() m_shutDownRequested.store(true, std::memory_order_release); wake_up_timer_thread(); m_thread.join(); -#if CPPCORO_OS_LINUX - close(m_wakeupfd); - close(m_timerfd); - close(m_epollfd); -#endif } void cppcoro::io_service::timer_thread_state::request_timer_cancellation() noexcept @@ -877,8 +877,8 @@ void cppcoro::io_service::timer_thread_state::run() timerEvent = true; } #elif CPPCORO_OS_LINUX - struct epoll_event ev; - const int status = epoll_wait(m_epollfd, &ev, 1, timeout); + epoll_event ev; + const int status = epoll_wait(m_epollfd.fd(), &ev, 1, timeout); if(status == -1) { @@ -890,10 +890,10 @@ void cppcoro::io_service::timer_thread_state::run() }; } - if(status == 0 || (status == 1 && ev.data.fd == m_wakeupfd)) + if(status == 0 || (status == 1 && ev.data.fd == m_wakeupfd.fd())) { uint64_t count; - if(read(m_wakeupfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) + if(read(m_wakeupfd.fd(), &count, sizeof(uint64_t)) != sizeof(uint64_t)) { throw std::system_error { @@ -904,10 +904,10 @@ void cppcoro::io_service::timer_thread_state::run() } waitEvent = true; - } else if (status == 1 && ev.data.fd == m_timerfd) + } else if (status == 1 && ev.data.fd == m_timerfd.fd()) { uint64_t count; - if(read(m_timerfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) + if(read(m_timerfd.fd(), &count, sizeof(uint64_t)) != sizeof(uint64_t)) { throw std::system_error { @@ -993,15 +993,15 @@ void cppcoro::io_service::timer_thread_state::run() nullptr, resumeFromSuspend); #elif CPPCORO_OS_LINUX - struct itimerspec alarm_time = {0}; + itimerspec alarm_time = {0}; alarm_time.it_value.tv_sec = std::chrono:: duration_cast(timeUntilNextDueTime).count(); alarm_time.it_value.tv_nsec = (std::chrono:: - duration_cast(timeUntilNextDueTime).count() - % 10000000) * 100; + duration_cast(timeUntilNextDueTime).count() % 10000000); if(alarm_time.it_value.tv_sec == 0 && alarm_time.it_value.tv_nsec == 0) { //linux timer of 0 time will not generate events @@ -1009,7 +1009,7 @@ void cppcoro::io_service::timer_thread_state::run() alarm_time.it_value.tv_nsec = 1; } - if(timerfd_settime(m_timerfd, 0, &alarm_time, NULL) == -1) + if(timerfd_settime(m_timerfd.fd(), 0, &alarm_time, NULL) == -1) { ok = false; } @@ -1075,21 +1075,13 @@ void cppcoro::io_service::timer_thread_state::run() } } -void cppcoro::io_service::timer_thread_state::wake_up_timer_thread() +void cppcoro::io_service::timer_thread_state::wake_up_timer_thread() noexcept { #if CPPCORO_OS_WINNT (void)::SetEvent(m_wakeUpEvent.handle()); #elif CPPCORO_OS_LINUX uint64_t count = 1; - if(write(m_wakeupfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error in waking up: writing to wake up eventfd" - }; - } + (void)write(m_wakeupfd.fd(), &count, sizeof(uint64_t)); #endif } diff --git a/lib/linux.cpp b/lib/linux.cpp index 5bfdee67..8912686e 100644 --- a/lib/linux.cpp +++ b/lib/linux.cpp @@ -16,7 +16,7 @@ namespace cppcoro uuid_t unique_name; const char* cppcoro_qname_prefix = "/cppcoro-"; - if(NAME_MAX < UUID_STRING_SIZE + sizeof(cppcoro_qname_prefix) + 1) + if(NAME_MAX < UUID_STRING_SIZE + strlen(cppcoro_qname_prefix) + 1) { throw std::system_error { @@ -39,7 +39,7 @@ namespace cppcoro attr.mq_msgsize = sizeof(cppcoro::detail::linux::message); attr.mq_curmsgs = 0; - m_mqdt = mq_open((const char*)m_qname, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, S_IRWXU, &attr); + m_mqdt = mq_open(m_qname, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, S_IRWXU, &attr); if( m_mqdt == -1 && errno == EEXIST) { @@ -59,11 +59,11 @@ namespace cppcoro break; } - m_epollfd = create_epoll_fd(); + m_epollfd = safe_fd{create_epoll_fd()}; m_ev.data.fd = m_mqdt; m_ev.events = EPOLLIN; - if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_mqdt, &m_ev) == -1) + if(epoll_ctl(m_epollfd.fd(), EPOLL_CTL_ADD, m_mqdt, &m_ev) == -1) { throw std::system_error { @@ -76,7 +76,6 @@ namespace cppcoro message_queue::~message_queue() { - close(m_epollfd); assert(mq_close(m_mqdt) == 0); assert(mq_unlink(m_qname) == 0); } @@ -93,7 +92,7 @@ namespace cppcoro bool message_queue::dequeue_message(void*& msg, message_type& type, bool wait) { struct epoll_event ev = {0}; - int nfds = epoll_wait(m_epollfd, &ev, 1, wait?-1:0); + int nfds = epoll_wait(m_epollfd.fd(), &ev, 1, wait?-1:0); if(nfds == -1) { @@ -138,7 +137,7 @@ namespace cppcoro return true; } - int create_event_fd() + safe_fd create_event_fd() { int fd = eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK | EFD_CLOEXEC); @@ -152,10 +151,10 @@ namespace cppcoro }; } - return fd; + return safe_fd{fd}; } - int create_timer_fd() + safe_fd create_timer_fd() { int fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); @@ -169,10 +168,10 @@ namespace cppcoro }; } - return fd; + return safe_fd{fd}; } - int create_epoll_fd() + safe_fd create_epoll_fd() { int fd = epoll_create1(EPOLL_CLOEXEC); @@ -186,7 +185,16 @@ namespace cppcoro }; } - return fd; + return safe_fd{fd}; + } + + void safe_fd::close() noexcept + { + if(m_fd != -1) + { + ::close(m_fd); + m_fd = -1; + } } } } diff --git a/test/scheduling_operator_tests.cpp b/test/scheduling_operator_tests.cpp index a3a1e9a1..68bc9177 100644 --- a/test/scheduling_operator_tests.cpp +++ b/test/scheduling_operator_tests.cpp @@ -18,13 +18,13 @@ TEST_SUITE_BEGIN("schedule/resume_on"); #if CPPCORO_OS_WINNT -#define THREAD_ID std::thread::id -#define GET_THIS_THREAD_ID std::this_thread::get_id() +typedef thread_id std::thread::id +#define get_thread_id std::this_thread::get_id #endif #if CPPCORO_OS_LINUX -#define THREAD_ID unsigned long long -#define GET_THIS_THREAD_ID get_thread_id() +#define thread_id unsigned long long +#define get_thread_id() get_thread_id() #include @@ -40,49 +40,49 @@ static unsigned long long get_thread_id() TEST_CASE_FIXTURE(io_service_fixture, "schedule_on task<> function") { - auto mainThreadId = GET_THIS_THREAD_ID; + auto mainThreadId = get_thread_id(); - THREAD_ID ioThreadId; + thread_id ioThreadId; auto start = [&]() -> cppcoro::task<> { - ioThreadId = GET_THIS_THREAD_ID; + ioThreadId = get_thread_id(); CHECK(ioThreadId != mainThreadId); co_return; }; cppcoro::sync_wait([&]() -> cppcoro::task<> { - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); co_await schedule_on(io_service(), start()); - CHECK(GET_THIS_THREAD_ID == ioThreadId); + CHECK(get_thread_id() == ioThreadId); }()); } TEST_CASE_FIXTURE(io_service_fixture, "schedule_on async_generator<> function") { - auto mainThreadId = GET_THIS_THREAD_ID; + auto mainThreadId = get_thread_id(); - THREAD_ID ioThreadId; + thread_id ioThreadId; auto makeSequence = [&]() -> cppcoro::async_generator { - ioThreadId = GET_THIS_THREAD_ID; + ioThreadId = get_thread_id(); CHECK(ioThreadId != mainThreadId); co_yield 1; - CHECK(GET_THIS_THREAD_ID == ioThreadId); + CHECK(get_thread_id() == ioThreadId); co_yield 2; - CHECK(GET_THIS_THREAD_ID == ioThreadId); + CHECK(get_thread_id() == ioThreadId); co_yield 3; - CHECK(GET_THIS_THREAD_ID == ioThreadId); + CHECK(get_thread_id() == ioThreadId); co_return; }; @@ -92,7 +92,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on async_generator<> function") cppcoro::sync_wait(cppcoro::when_all_ready( [&]() -> cppcoro::task<> { - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); auto seq = schedule_on(io_service(), makeSequence()); @@ -118,35 +118,35 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on async_generator<> function") TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> function") { - auto mainThreadId = GET_THIS_THREAD_ID; + auto mainThreadId = get_thread_id(); auto start = [&]() -> cppcoro::task<> { - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); co_return; }; cppcoro::sync_wait([&]() -> cppcoro::task<> { - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); co_await resume_on(io_service(), start()); - CHECK(GET_THIS_THREAD_ID != mainThreadId); + CHECK(get_thread_id() != mainThreadId); }()); } TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function") { - auto mainThreadId = GET_THIS_THREAD_ID; + auto mainThreadId = get_thread_id(); - THREAD_ID ioThreadId; + thread_id ioThreadId; auto makeSequence = [&]() -> cppcoro::async_generator { co_await io_service().schedule(); - ioThreadId = GET_THIS_THREAD_ID; + ioThreadId = get_thread_id(); CHECK(ioThreadId != mainThreadId); @@ -170,7 +170,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function") { auto stopOnExit = cppcoro::on_scope_exit([&] { otherIoService.stop(); }); - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); auto seq = resume_on(otherIoService, makeSequence()); @@ -179,7 +179,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function") { // Every time we receive a value it should be on our requested // scheduler (ie. main thread) - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); CHECK(value == expected++); // Occasionally transfer execution to a different thread before @@ -201,17 +201,17 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function") TEST_CASE_FIXTURE(io_service_fixture, "schedule_on task<> pipe syntax") { - auto mainThreadId = GET_THIS_THREAD_ID; + auto mainThreadId = get_thread_id(); auto makeTask = [&]() -> cppcoro::task { - CHECK(GET_THIS_THREAD_ID != mainThreadId); + CHECK(get_thread_id() != mainThreadId); co_return 123; }; auto triple = [&](int x) { - CHECK(GET_THIS_THREAD_ID != mainThreadId); + CHECK(get_thread_id() != mainThreadId); return x * 3; }; @@ -225,11 +225,11 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on task<> pipe syntax") TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax") { - auto mainThreadId = GET_THIS_THREAD_ID; + auto mainThreadId = get_thread_id(); auto makeTask = [&]() -> cppcoro::task { - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); co_return 123; }; @@ -237,23 +237,23 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax") { cppcoro::task t = makeTask() | cppcoro::resume_on(io_service()); CHECK(co_await t == 123); - CHECK(GET_THIS_THREAD_ID != mainThreadId); + CHECK(get_thread_id() != mainThreadId); }()); } TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax multiple uses") { - auto mainThreadId = GET_THIS_THREAD_ID; + auto mainThreadId = get_thread_id(); auto makeTask = [&]() -> cppcoro::task { - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); co_return 123; }; auto triple = [&](int x) { - CHECK(GET_THIS_THREAD_ID != mainThreadId); + CHECK(get_thread_id() != mainThreadId); return x * 3; }; @@ -264,7 +264,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax multiple use { auto stopOnExit = cppcoro::on_scope_exit([&] { otherIoService.stop(); }); - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); cppcoro::task t = makeTask() @@ -274,7 +274,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax multiple use CHECK(co_await t == 369); - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); }(), [&]() -> cppcoro::task<> { From e5dc2fc8f20d41c1c5ca211f4f12e55ec0e34b96 Mon Sep 17 00:00:00 2001 From: Anirudh Badam Date: Thu, 12 Apr 2018 18:23:17 +0000 Subject: [PATCH 19/34] changes for white space based on clang-format.el --- include/cppcoro/detail/linux.hpp | 10 +++++----- lib/io_service.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/cppcoro/detail/linux.hpp b/include/cppcoro/detail/linux.hpp index caba438e..6306465b 100644 --- a/include/cppcoro/detail/linux.hpp +++ b/include/cppcoro/detail/linux.hpp @@ -22,12 +22,12 @@ namespace cppcoro using fd_t = int; enum message_type - { - CALLBACK_TYPE, - RESUME_TYPE - }; + { + CALLBACK_TYPE, + RESUME_TYPE + }; - class safe_fd + class safe_fd { public: diff --git a/lib/io_service.cpp b/lib/io_service.cpp index 89fb9402..98d88ac1 100644 --- a/lib/io_service.cpp +++ b/lib/io_service.cpp @@ -997,7 +997,7 @@ void cppcoro::io_service::timer_thread_state::run() alarm_time.it_value.tv_sec = std::chrono:: duration_cast(timeUntilNextDueTime).count(); + seconds>(timeUntilNextDueTime).count(); alarm_time.it_value.tv_nsec = (std::chrono:: duration_cast Date: Mon, 16 Apr 2018 18:16:20 +0000 Subject: [PATCH 20/34] copyright block --- include/cppcoro/detail/linux.hpp | 4 ++++ lib/linux.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/include/cppcoro/detail/linux.hpp b/include/cppcoro/detail/linux.hpp index 6306465b..136ff4ff 100644 --- a/include/cppcoro/detail/linux.hpp +++ b/include/cppcoro/detail/linux.hpp @@ -1,3 +1,7 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Microsoft +// Licenced under MIT license. See LICENSE.txt for details. +/////////////////////////////////////////////////////////////////////////////// #pragma once #include diff --git a/lib/linux.cpp b/lib/linux.cpp index 8912686e..ab60d8b4 100644 --- a/lib/linux.cpp +++ b/lib/linux.cpp @@ -1,3 +1,7 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Microsoft +// Licenced under MIT license. See LICENSE.txt for details. +/////////////////////////////////////////////////////////////////////////////// #include #include #include From b23f64064671437ee1b6351e6c3ce30a492dc4ba Mon Sep 17 00:00:00 2001 From: Anirudh Badam Date: Sat, 27 Jan 2018 18:10:12 +0000 Subject: [PATCH 21/34] support for linux scheduler and timer --- config.cake | 4 +- include/cppcoro/detail/linux.hpp | 59 +++++ include/cppcoro/io_service.hpp | 19 +- lib/build.cake | 10 +- lib/io_service.cpp | 352 ++++++++++++++++++++++++----- lib/linux.cpp | 193 ++++++++++++++++ test/build.cake | 4 +- test/scheduling_operator_tests.cpp | 85 ++++--- 8 files changed, 629 insertions(+), 97 deletions(-) create mode 100644 include/cppcoro/detail/linux.hpp create mode 100644 lib/linux.cpp diff --git a/config.cake b/config.cake index 58c31558..93c09cc5 100644 --- a/config.cake +++ b/config.cake @@ -295,7 +295,9 @@ elif cake.system.isLinux() or cake.system.isDarwin(): compiler.addLibrary('c++') compiler.addLibrary('c') compiler.addLibrary('pthread') - + compiler.addLibrary('rt') + compiler.addLibrary('uuid') + #compiler.addProgramFlag('-Wl,--trace') #compiler.addProgramFlag('-Wl,-v') diff --git a/include/cppcoro/detail/linux.hpp b/include/cppcoro/detail/linux.hpp new file mode 100644 index 00000000..6739a25d --- /dev/null +++ b/include/cppcoro/detail/linux.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cppcoro +{ + namespace detail + { + namespace linux + { + enum message_type + { + CALLBACK_TYPE, + RESUME_TYPE + }; + + struct message + { + enum message_type m_type; + void* m_ptr; + }; + + struct io_state : linux::message + { + using callback_type = void(io_state* state); + callback_type* m_callback; + }; + + class message_queue + { + private: + mqd_t m_mqdt; + char m_qname[NAME_MAX]; + int m_epollfd; + struct epoll_event m_ev; + message_queue(); + public: + message_queue(size_t queue_length); + ~message_queue(); + bool enqueue_message(void* message, message_type type); + bool dequeue_message(void*& message, message_type& type, bool wait); + }; + + int create_event_fd(); + int create_timer_fd(); + int create_epoll_fd(); + } + } +} diff --git a/include/cppcoro/io_service.hpp b/include/cppcoro/io_service.hpp index 27867c58..99ab7168 100644 --- a/include/cppcoro/io_service.hpp +++ b/include/cppcoro/io_service.hpp @@ -13,6 +13,10 @@ # include #endif +#if CPPCORO_OS_LINUX +#include +#endif + #include #include #include @@ -42,8 +46,12 @@ namespace cppcoro /// actively processing events. /// Note that the number of active threads may temporarily go /// above this number. - io_service(std::uint32_t concurrencyHint); - +#if CPPCORO_OS_WINNT + io_service(std::uint32_t concurrencyHint); +#endif +#if CPPCORO_OS_LINUX + io_service(size_t queue_length); +#endif ~io_service(); io_service(io_service&& other) = delete; @@ -147,6 +155,10 @@ namespace cppcoro void try_reschedule_overflow_operations() noexcept; + void queue_overflow_operation_to_head(schedule_operation* operation) noexcept; + + void queue_overflow_operation_to_tail(schedule_operation* operation) noexcept; + bool try_enter_event_loop() noexcept; void exit_event_loop() noexcept; @@ -169,6 +181,9 @@ namespace cppcoro detail::win32::safe_handle m_iocpHandle; #endif +#if CPPCORO_OS_LINUX + detail::linux::message_queue* m_mq; +#endif // Head of a linked-list of schedule operations that are // ready to run but that failed to be queued to the I/O // completion port (eg. due to low memory). diff --git a/lib/build.cake b/lib/build.cake index b3ed9c03..8faea0db 100644 --- a/lib/build.cake +++ b/lib/build.cake @@ -82,6 +82,7 @@ sources = script.cwd([ 'ipv4_endpoint.cpp', 'ipv6_address.cpp', 'ipv6_endpoint.cpp', + 'io_service.cpp', ]) extras = script.cwd([ @@ -89,6 +90,14 @@ extras = script.cwd([ 'use.cake', ]) +if variant.platform == "linux": + detailIncludes.extend(cake.path.join(env.expand('${CPPCORO}'), 'include', 'cppcoro', 'detail', [ + 'linux.hpp', + ])) + sources.extend(script.cwd([ + 'linux.cpp', + ])) + if variant.platform == "windows": detailIncludes.extend(cake.path.join(env.expand('${CPPCORO}'), 'include', 'cppcoro', 'detail', [ 'win32.hpp', @@ -96,7 +105,6 @@ if variant.platform == "windows": ])) sources.extend(script.cwd([ 'win32.cpp', - 'io_service.cpp', 'file.cpp', 'readable_file.cpp', 'writable_file.cpp', diff --git a/lib/io_service.cpp b/lib/io_service.cpp index cc7facef..a6d326db 100644 --- a/lib/io_service.cpp +++ b/lib/io_service.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #if CPPCORO_OS_WINNT # ifndef WIN32_LEAN_AND_MEAN @@ -24,7 +25,7 @@ namespace { #if CPPCORO_OS_WINNT - cppcoro::detail::win32::safe_handle create_io_completion_port(std::uint32_t concurrencyHint) + cppcoro::detail::win32::safe_handle create_io_completion_port(std::uint32_t concurrencyHint) { HANDLE handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, concurrencyHint); if (handle == NULL) @@ -302,15 +303,21 @@ class cppcoro::io_service::timer_thread_state void request_timer_cancellation() noexcept; - void run() noexcept; + void run(); - void wake_up_timer_thread() noexcept; + void wake_up_timer_thread(); #if CPPCORO_OS_WINNT detail::win32::safe_handle m_wakeUpEvent; detail::win32::safe_handle m_waitableTimerEvent; #endif +#if CPPCORO_OS_LINUX + int m_wakeupfd; + int m_timerfd; + int m_epollfd; +#endif + std::atomic m_newlyQueuedTimers; std::atomic m_timerCancellationRequested; std::atomic m_shutDownRequested; @@ -318,18 +325,31 @@ class cppcoro::io_service::timer_thread_state std::thread m_thread; }; - - cppcoro::io_service::io_service() +#if CPPCORO_OS_WINNT : io_service(0) +#endif +#if CPPCORO_OS_LINUX + : io_service(1) +#endif { } -cppcoro::io_service::io_service(std::uint32_t concurrencyHint) +cppcoro::io_service::io_service( +#if CPPCORO_OS_WINNT + std::uint32_t concurrencyHint +#endif +#if CPPCORO_OS_LINUX + size_t queue_length +#endif + ) : m_threadState(0) , m_workCount(0) #if CPPCORO_OS_WINNT , m_iocpHandle(create_io_completion_port(concurrencyHint)) +#endif +#if CPPCORO_OS_LINUX + , m_mq(new detail::linux::message_queue(queue_length)) #endif , m_scheduleOperations(nullptr) , m_timerState(nullptr) @@ -342,6 +362,10 @@ cppcoro::io_service::~io_service() assert(m_threadState.load(std::memory_order_relaxed) < active_thread_count_increment); delete m_timerState.load(std::memory_order_relaxed); + +#if CPPCORO_OS_LINUX + delete m_mq; +#endif } cppcoro::io_service::schedule_operation cppcoro::io_service::schedule() noexcept @@ -457,13 +481,69 @@ void cppcoro::io_service::notify_work_finished() noexcept } } +#if CPPCORO_OS_WINNT cppcoro::detail::win32::handle_t cppcoro::io_service::native_iocp_handle() noexcept { return m_iocpHandle.handle(); } +#endif + +void cppcoro::io_service::queue_overflow_operation_to_tail(schedule_operation* operation) noexcept +{ + // Still unable to queue these operations. + // Put them back on the list of overflow operations. + auto* tail = operation; + while (tail->m_next != nullptr) + { + tail = tail->m_next; + } + + schedule_operation* head = nullptr; + while (!m_scheduleOperations.compare_exchange_weak( + head, + operation, + std::memory_order_release, + std::memory_order_relaxed)) + { + tail->m_next = head; + } + + return; +} + +void cppcoro::io_service::queue_overflow_operation_to_head(schedule_operation* operation) noexcept +{ + // Failed to post to the I/O completion port. + // + // This is most-likely because the queue is currently full. + // + // We'll queue up the operation to a linked-list using a lock-free + // push and defer the dispatch to the completion port until some I/O + // thread next enters its event loop. + auto* head = m_scheduleOperations.load(std::memory_order_acquire); + do + { + operation->m_next = head; + } while (!m_scheduleOperations.compare_exchange_weak( + head, + operation, + std::memory_order_release, + std::memory_order_acquire)); +} void cppcoro::io_service::schedule_impl(schedule_operation* operation) noexcept { +#if CPPCORO_OS_LINUX + bool status = m_mq->enqueue_message(reinterpret_cast(operation->m_awaiter.address()), detail::linux::RESUME_TYPE); + + if(!status) + { + assert(errno == EAGAIN); + queue_overflow_operation_to_head(operation); + } + + return; +#endif #if CPPCORO_OS_WINNT const BOOL ok = ::PostQueuedCompletionStatus( m_iocpHandle.handle(), @@ -472,33 +552,18 @@ void cppcoro::io_service::schedule_impl(schedule_operation* operation) noexcept nullptr); if (!ok) { - // Failed to post to the I/O completion port. - // - // This is most-likely because the queue is currently full. - // - // We'll queue up the operation to a linked-list using a lock-free - // push and defer the dispatch to the completion port until some I/O - // thread next enters its event loop. - auto* head = m_scheduleOperations.load(std::memory_order_acquire); - do - { - operation->m_next = head; - } while (!m_scheduleOperations.compare_exchange_weak( - head, - operation, - std::memory_order_release, - std::memory_order_acquire)); + queue_overflow_operation_to_head(operation); } #endif } void cppcoro::io_service::try_reschedule_overflow_operations() noexcept { -#if CPPCORO_OS_WINNT auto* operation = m_scheduleOperations.exchange(nullptr, std::memory_order_acquire); while (operation != nullptr) { auto* next = operation->m_next; +#if CPPCORO_OS_WINNT BOOL ok = ::PostQueuedCompletionStatus( m_iocpHandle.handle(), 0, @@ -506,30 +571,20 @@ void cppcoro::io_service::try_reschedule_overflow_operations() noexcept nullptr); if (!ok) { - // Still unable to queue these operations. - // Put them back on the list of overflow operations. - auto* tail = operation; - while (tail->m_next != nullptr) - { - tail = tail->m_next; - } - - schedule_operation* head = nullptr; - while (!m_scheduleOperations.compare_exchange_weak( - head, - operation, - std::memory_order_release, - std::memory_order_relaxed)) - { - tail->m_next = head; - } - - return; + queue_overflow_operation_to_tail(operation); } - +#endif +#if CPPCORO_OS_LINUX + bool status = m_mq->enqueue_message(reinterpret_cast(operation->m_awaiter.address()), detail::linux::RESUME_TYPE); + + if(!status) + { + assert(errno == EAGAIN); + queue_overflow_operation_to_tail(operation); + } +#endif operation = next; } -#endif } bool cppcoro::io_service::try_enter_event_loop() noexcept @@ -556,12 +611,52 @@ void cppcoro::io_service::exit_event_loop() noexcept bool cppcoro::io_service::try_process_one_event(bool waitForEvent) { -#if CPPCORO_OS_WINNT - if (is_stop_requested()) + if(is_stop_requested()) + { + return false; + } + +#if CPPCORO_OS_LINUX + while(true) + { + try_reschedule_overflow_operations(); + void* message = NULL; + detail::linux::message_type type = detail::linux::RESUME_TYPE; + + bool status = m_mq->dequeue_message(message, type, waitForEvent); + + if(!status) { - return false; + return false; } - + + if(type == detail::linux::CALLBACK_TYPE) + { + auto* state = + static_cast(reinterpret_cast(message)); + + state->m_callback(state); + + return true; + } + else + { + if((unsigned long long)message != 0) + { + std::experimental::coroutine_handle<>::from_address(reinterpret_cast(message)).resume(); + + return true; + } + + if (is_stop_requested()) + { + return false; + } + } + } +#endif + +#if CPPCORO_OS_WINNT const DWORD timeout = waitForEvent ? INFINITE : 0; while (true) @@ -644,6 +739,10 @@ void cppcoro::io_service::post_wake_up_event() noexcept // in the queue next time they check anyway and thus wake-up. (void)::PostQueuedCompletionStatus(m_iocpHandle.handle(), 0, 0, nullptr); #endif + +#if CPPCORO_OS_LINUX + (void)m_mq->enqueue_message(NULL, detail::linux::RESUME_TYPE); +#endif } cppcoro::io_service::timer_thread_state* @@ -669,16 +768,50 @@ cppcoro::io_service::ensure_timer_thread_started() return timerState; } -cppcoro::io_service::timer_thread_state::timer_thread_state() +cppcoro::io_service::timer_thread_state::timer_thread_state() : #if CPPCORO_OS_WINNT - : m_wakeUpEvent(create_auto_reset_event()) - , m_waitableTimerEvent(create_waitable_timer_event()) + m_wakeUpEvent(create_auto_reset_event()) + , m_waitableTimerEvent(create_waitable_timer_event()), +#endif +#if CPPCORO_OS_LINUX + m_wakeupfd(detail::linux::create_event_fd()) + , m_timerfd(detail::linux::create_timer_fd()) + , m_epollfd(detail::linux::create_epoll_fd()), #endif - , m_newlyQueuedTimers(nullptr) + m_newlyQueuedTimers(nullptr) , m_timerCancellationRequested(false) , m_shutDownRequested(false) , m_thread([this] { this->run(); }) { +#if CPPCORO_OS_LINUX + struct epoll_event wake_ev = {0}; + wake_ev.events = EPOLLIN; + wake_ev.data.fd = m_wakeupfd; + + if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_wakeupfd, &wake_ev) == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: epoll ctl wake ev" + }; + } + + struct epoll_event timer_ev = {0}; + timer_ev.events = EPOLLIN; + timer_ev.data.fd = m_timerfd; + + if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_timerfd, &timer_ev) == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: epoll ctl timer ev" + }; + } +#endif } cppcoro::io_service::timer_thread_state::~timer_thread_state() @@ -686,6 +819,11 @@ cppcoro::io_service::timer_thread_state::~timer_thread_state() m_shutDownRequested.store(true, std::memory_order_release); wake_up_timer_thread(); m_thread.join(); +#if CPPCORO_OS_LINUX + close(m_wakeupfd); + close(m_timerfd); + close(m_epollfd); +#endif } void cppcoro::io_service::timer_thread_state::request_timer_cancellation() noexcept @@ -698,36 +836,77 @@ void cppcoro::io_service::timer_thread_state::request_timer_cancellation() noexc } } -void cppcoro::io_service::timer_thread_state::run() noexcept +void cppcoro::io_service::timer_thread_state::run() { -#if CPPCORO_OS_WINNT using clock = std::chrono::high_resolution_clock; using time_point = clock::time_point; timer_queue timerQueue; +#if CPPCORO_OS_WINNT const DWORD waitHandleCount = 2; const HANDLE waitHandles[waitHandleCount] = { m_wakeUpEvent.handle(), m_waitableTimerEvent.handle() }; + DWORD timeout = INFINITE; +#endif time_point lastSetWaitEventTime = time_point::max(); timed_schedule_operation* timersReadyToResume = nullptr; - DWORD timeout = INFINITE; while (!m_shutDownRequested.load(std::memory_order_relaxed)) { +#if CPPCORO_OS_WINNNT const DWORD waitResult = ::WaitForMultipleObjectsEx( waitHandleCount, waitHandles, FALSE, // waitAll timeout, FALSE); // alertable +#endif + +#if CPPCORO_OS_LINUX + struct epoll_event ev; + const int status = epoll_wait(m_epollfd, &ev, 1, -1); + + if(status == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in timer thread: epoll wait" + }; + } +#endif + + +#if CPPCORO_OS_WINNT if (waitResult == WAIT_OBJECT_0 || waitResult == WAIT_FAILED) { +#endif +#if CPPCORO_OS_LINUX + if(status == 0) + { + continue; + } + + if (status == 1 && ev.data.fd == m_wakeupfd) + { + uint64_t count; + if(read(m_wakeupfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in timer thread: eventfd read" + }; + } +#endif // Wake-up event (WAIT_OBJECT_0) // // We are only woken up for: @@ -763,9 +942,29 @@ void cppcoro::io_service::timer_thread_state::run() noexcept } } } - else if (waitResult == (WAIT_OBJECT_0 + 1)) + else if ( +#if CPPCORO_OS_WINNT + waitResult == (WAIT_OBJECT_0 + 1) +#endif +#if CPPCORO_OS_LINUX + ev.data.fd == m_timerfd +#endif + ) { lastSetWaitEventTime = time_point::max(); + +#if CPPCORO_OS_LINUX + uint64_t count; + if(read(m_timerfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in timer thread: timerfd read" + }; + } +#endif } if (!timerQueue.is_empty()) @@ -784,11 +983,12 @@ void cppcoro::io_service::timer_thread_state::run() noexcept // amount of time to wait until the next timer is ready. if (earliestDueTime != lastSetWaitEventTime) { - using ticks = std::chrono::duration>; + using ticks = std::chrono::duration>; auto timeUntilNextDueTime = earliestDueTime - currentTime; // Negative value indicates relative time. +#if CPPCORO_OS_WINNT LARGE_INTEGER dueTime; dueTime.QuadPart = -std::chrono::duration_cast(timeUntilNextDueTime).count(); @@ -835,6 +1035,29 @@ void cppcoro::io_service::timer_thread_state::run() noexcept timeout = 1; } } +#endif +#if CPPCORO_OS_LINUX + struct itimerspec alarm_time = {0}; + alarm_time.it_value.tv_sec = std::chrono::duration_cast(timeUntilNextDueTime).count(); + alarm_time.it_value.tv_nsec = (std::chrono::duration_cast(timeUntilNextDueTime).count() % 10000000) * 100; + if(alarm_time.it_value.tv_sec == 0 && alarm_time.it_value.tv_nsec == 0) + { + //linux timer of 0 time will not generate events so let's set it to 1 nsec + alarm_time.it_value.tv_nsec = 1; + } + + if(timerfd_settime(m_timerfd, 0, &alarm_time, NULL) == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in timer thread: timerfd_settime" + }; + } + + lastSetWaitEventTime = earliestDueTime; +#endif } } } @@ -858,14 +1081,25 @@ void cppcoro::io_service::timer_thread_state::run() noexcept timersReadyToResume = nextTimer; } } -#endif } -void cppcoro::io_service::timer_thread_state::wake_up_timer_thread() noexcept +void cppcoro::io_service::timer_thread_state::wake_up_timer_thread() { #if CPPCORO_OS_WINNT (void)::SetEvent(m_wakeUpEvent.handle()); #endif +#if CPPCORO_OS_LINUX + uint64_t count = 1; + if(write(m_wakeupfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in waking up: writing to wake up eventfd" + }; + } +#endif } void cppcoro::io_service::schedule_operation::await_suspend( diff --git a/lib/linux.cpp b/lib/linux.cpp new file mode 100644 index 00000000..56d18daa --- /dev/null +++ b/lib/linux.cpp @@ -0,0 +1,193 @@ +#include +#include +#include + +#define UUID_STRING_SIZE 36 + +namespace cppcoro +{ + namespace detail + { + namespace linux + { + message_queue::message_queue(size_t queue_length) + { + m_mqdt = -1; + uuid_t unique_name; + const char* cppcoro_qname_prefix = "/cppcoro-"; + + if(NAME_MAX < UUID_STRING_SIZE + sizeof(cppcoro_qname_prefix) + 1) + { + throw std::system_error + { + static_cast(EINVAL), + std::system_category(), + "Error creating message queue: system name max length too small" + }; + } + + strncpy(m_qname, cppcoro_qname_prefix, NAME_MAX); + + for(;;) + { + uuid_generate(unique_name); + uuid_unparse(unique_name, m_qname + sizeof(cppcoro_qname_prefix)); + + struct mq_attr attr; + attr.mq_flags = O_NONBLOCK; + attr.mq_maxmsg = queue_length; + attr.mq_msgsize = sizeof(cppcoro::detail::linux::message); + attr.mq_curmsgs = 0; + + m_mqdt = mq_open((const char*)m_qname, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, S_IRWXU, &attr); + + if( m_mqdt == -1 && errno == EEXIST) + { + continue; + } + + if( m_mqdt == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: message queue open" + }; + } + + break; + } + + m_epollfd = create_epoll_fd(); + m_ev.data.fd = m_mqdt; + m_ev.events = EPOLLIN; + + if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_mqdt, &m_ev) == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: epoll ctl mqdt" + }; + } + } + + message_queue::~message_queue() + { + close(m_epollfd); + assert(mq_close(m_mqdt) == 0); + assert(mq_unlink(m_qname) == 0); + } + + bool message_queue::enqueue_message(void* msg, message_type type) + { + message qmsg; + qmsg.m_type = type; + qmsg.m_ptr = msg; + int status = mq_send(m_mqdt, (const char*)&qmsg, sizeof(message), 0); + return status==-1?false:true; + } + + bool message_queue::dequeue_message(void*& msg, message_type& type, bool wait) + { + struct epoll_event ev = {0}; + int nfds = epoll_wait(m_epollfd, &ev, 1, wait?-1:0); + + if(nfds == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in epoll_wait run loop" + }; + } + + if(nfds == 0 && !wait) + { + return false; + } + + if(nfds == 0 && wait) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in epoll_wait run loop" + }; + } + + message qmsg; + ssize_t status = mq_receive(m_mqdt, (char*)&qmsg, sizeof(message), NULL); + + if(status == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error retrieving message from message queue: mq_receive" + }; + } + + msg = qmsg.m_ptr; + type = qmsg.m_type; + return true; + } + + int create_event_fd() + { + int fd = eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK | EFD_CLOEXEC); + + if(fd == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: event fd create" + }; + } + + return fd; + } + + int create_timer_fd() + { + int fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); + + if(fd == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: timer fd create" + }; + } + + return fd; + } + + int create_epoll_fd() + { + int fd = epoll_create1(EPOLL_CLOEXEC); + + if(fd == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating timer thread: epoll create" + }; + } + + return fd; + } + } + } +} diff --git a/test/build.cake b/test/build.cake index 69d0c953..02346b0f 100644 --- a/test/build.cake +++ b/test/build.cake @@ -39,12 +39,12 @@ sources = script.cwd([ 'ipv4_endpoint_tests.cpp', 'ipv6_address_tests.cpp', 'ipv6_endpoint_tests.cpp', + 'scheduling_operator_tests.cpp', + 'io_service_tests.cpp', ]) if variant.platform == 'windows': sources += script.cwd([ - 'scheduling_operator_tests.cpp', - 'io_service_tests.cpp', 'file_tests.cpp', ]) diff --git a/test/scheduling_operator_tests.cpp b/test/scheduling_operator_tests.cpp index a44efef3..6e4a092c 100644 --- a/test/scheduling_operator_tests.cpp +++ b/test/scheduling_operator_tests.cpp @@ -17,51 +17,72 @@ TEST_SUITE_BEGIN("schedule/resume_on"); +#if CPPCORO_OS_WINNT +#define THREAD_ID std::thread::id +#define GET_THIS_THREAD_ID std::this_thread::get_id() +#endif + +#if CPPCORO_OS_LINUX +#define THREAD_ID unsigned long long +#define GET_THIS_THREAD_ID get_thread_id() + +#include + +static unsigned long long get_thread_id() +{ + unsigned long long id; + std::stringstream ss; + ss << std::this_thread::get_id(); + id = std::stoull(ss.str()); + return id; +} +#endif + TEST_CASE_FIXTURE(io_service_fixture, "schedule_on task<> function") { - auto mainThreadId = std::this_thread::get_id(); + auto mainThreadId = GET_THIS_THREAD_ID; - std::thread::id ioThreadId; + THREAD_ID ioThreadId; auto start = [&]() -> cppcoro::task<> { - ioThreadId = std::this_thread::get_id(); + ioThreadId = GET_THIS_THREAD_ID; CHECK(ioThreadId != mainThreadId); co_return; }; cppcoro::sync_wait([&]() -> cppcoro::task<> { - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); co_await schedule_on(io_service(), start()); - CHECK(std::this_thread::get_id() == ioThreadId); + CHECK(GET_THIS_THREAD_ID == ioThreadId); }()); } TEST_CASE_FIXTURE(io_service_fixture, "schedule_on async_generator<> function") { - auto mainThreadId = std::this_thread::get_id(); + auto mainThreadId = GET_THIS_THREAD_ID; - std::thread::id ioThreadId; + THREAD_ID ioThreadId; auto makeSequence = [&]() -> cppcoro::async_generator { - ioThreadId = std::this_thread::get_id(); + ioThreadId = GET_THIS_THREAD_ID; CHECK(ioThreadId != mainThreadId); co_yield 1; - CHECK(std::this_thread::get_id() == ioThreadId); + CHECK(GET_THIS_THREAD_ID == ioThreadId); co_yield 2; - CHECK(std::this_thread::get_id() == ioThreadId); + CHECK(GET_THIS_THREAD_ID == ioThreadId); co_yield 3; - CHECK(std::this_thread::get_id() == ioThreadId); + CHECK(GET_THIS_THREAD_ID == ioThreadId); co_return; }; @@ -71,7 +92,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on async_generator<> function") cppcoro::sync_wait(cppcoro::when_all_ready( [&]() -> cppcoro::task<> { - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); auto seq = schedule_on(io_service(), makeSequence()); @@ -97,21 +118,21 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on async_generator<> function") TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> function") { - auto mainThreadId = std::this_thread::get_id(); + auto mainThreadId = GET_THIS_THREAD_ID; auto start = [&]() -> cppcoro::task<> { - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); co_return; }; cppcoro::sync_wait([&]() -> cppcoro::task<> { - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); co_await resume_on(io_service(), start()); - CHECK(std::this_thread::get_id() != mainThreadId); + CHECK(GET_THIS_THREAD_ID != mainThreadId); }()); } @@ -127,15 +148,15 @@ constexpr bool isMsvc15_4X86Optimised = TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function" * doctest::skip{ isMsvc15_4X86Optimised }) { - auto mainThreadId = std::this_thread::get_id(); + auto mainThreadId = GET_THIS_THREAD_ID; - std::thread::id ioThreadId; + THREAD_ID ioThreadId; auto makeSequence = [&]() -> cppcoro::async_generator { co_await io_service().schedule(); - ioThreadId = std::this_thread::get_id(); + ioThreadId = GET_THIS_THREAD_ID; CHECK(ioThreadId != mainThreadId); @@ -159,7 +180,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function" { auto stopOnExit = cppcoro::on_scope_exit([&] { otherIoService.stop(); }); - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); auto seq = resume_on(otherIoService, makeSequence()); @@ -168,7 +189,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function" { // Every time we receive a value it should be on our requested // scheduler (ie. main thread) - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); CHECK(value == expected++); // Occasionally transfer execution to a different thread before @@ -190,17 +211,17 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function" TEST_CASE_FIXTURE(io_service_fixture, "schedule_on task<> pipe syntax") { - auto mainThreadId = std::this_thread::get_id(); + auto mainThreadId = GET_THIS_THREAD_ID; auto makeTask = [&]() -> cppcoro::task { - CHECK(std::this_thread::get_id() != mainThreadId); + CHECK(GET_THIS_THREAD_ID != mainThreadId); co_return 123; }; auto triple = [&](int x) { - CHECK(std::this_thread::get_id() != mainThreadId); + CHECK(GET_THIS_THREAD_ID != mainThreadId); return x * 3; }; @@ -214,11 +235,11 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on task<> pipe syntax") TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax") { - auto mainThreadId = std::this_thread::get_id(); + auto mainThreadId = GET_THIS_THREAD_ID; auto makeTask = [&]() -> cppcoro::task { - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); co_return 123; }; @@ -226,23 +247,23 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax") { cppcoro::task t = makeTask() | cppcoro::resume_on(io_service()); CHECK(co_await t == 123); - CHECK(std::this_thread::get_id() != mainThreadId); + CHECK(GET_THIS_THREAD_ID != mainThreadId); }()); } TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax multiple uses") { - auto mainThreadId = std::this_thread::get_id(); + auto mainThreadId = GET_THIS_THREAD_ID; auto makeTask = [&]() -> cppcoro::task { - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); co_return 123; }; auto triple = [&](int x) { - CHECK(std::this_thread::get_id() != mainThreadId); + CHECK(GET_THIS_THREAD_ID != mainThreadId); return x * 3; }; @@ -253,7 +274,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax multiple use { auto stopOnExit = cppcoro::on_scope_exit([&] { otherIoService.stop(); }); - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); cppcoro::task t = makeTask() @@ -263,7 +284,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax multiple use CHECK(co_await t == 369); - CHECK(std::this_thread::get_id() == mainThreadId); + CHECK(GET_THIS_THREAD_ID == mainThreadId); }(), [&]() -> cppcoro::task<> { From 723c049efdd2f835976cb4247344519ab936e0b5 Mon Sep 17 00:00:00 2001 From: Anirudh Badam Date: Wed, 7 Feb 2018 23:41:38 +0000 Subject: [PATCH 22/34] restructuring the code to reduce ifdefs --- include/cppcoro/detail/linux.hpp | 80 ++-- lib/io_service.cpp | 625 +++++++++++++++---------------- 2 files changed, 352 insertions(+), 353 deletions(-) diff --git a/include/cppcoro/detail/linux.hpp b/include/cppcoro/detail/linux.hpp index 6739a25d..a8f14499 100644 --- a/include/cppcoro/detail/linux.hpp +++ b/include/cppcoro/detail/linux.hpp @@ -12,48 +12,52 @@ #include #include +typedef int DWORD; +#define INFINITE (DWORD)-1 + namespace cppcoro { - namespace detail - { - namespace linux - { - enum message_type + namespace detail { - CALLBACK_TYPE, - RESUME_TYPE - }; - - struct message - { - enum message_type m_type; - void* m_ptr; - }; + namespace linux + { + enum message_type + { + CALLBACK_TYPE, + RESUME_TYPE + }; - struct io_state : linux::message - { - using callback_type = void(io_state* state); - callback_type* m_callback; - }; + struct message + { + enum message_type m_type; + void* m_ptr; + }; - class message_queue - { - private: - mqd_t m_mqdt; - char m_qname[NAME_MAX]; - int m_epollfd; - struct epoll_event m_ev; - message_queue(); - public: - message_queue(size_t queue_length); - ~message_queue(); - bool enqueue_message(void* message, message_type type); - bool dequeue_message(void*& message, message_type& type, bool wait); - }; + struct io_state : linux::message + { + using callback_type = void(io_state* state); + callback_type* m_callback; + }; - int create_event_fd(); - int create_timer_fd(); - int create_epoll_fd(); - } - } + class message_queue + { + private: + mqd_t m_mqdt; + char m_qname[NAME_MAX]; + int m_epollfd; + struct epoll_event m_ev; + message_queue(); + public: + message_queue(size_t queue_length); + ~message_queue(); + bool enqueue_message(void* message, message_type type); + bool dequeue_message(void*& message, message_type& type, bool wait); + }; + + int create_event_fd(); + int create_timer_fd(); + int create_epoll_fd(); + + } + } } diff --git a/lib/io_service.cpp b/lib/io_service.cpp index a6d326db..0dba7437 100644 --- a/lib/io_service.cpp +++ b/lib/io_service.cpp @@ -25,7 +25,7 @@ namespace { #if CPPCORO_OS_WINNT - cppcoro::detail::win32::safe_handle create_io_completion_port(std::uint32_t concurrencyHint) + cppcoro::detail::win32::safe_handle create_io_completion_port(std::uint32_t concurrencyHint) { HANDLE handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, concurrencyHint); if (handle == NULL) @@ -34,9 +34,9 @@ namespace throw std::system_error { static_cast(errorCode), - std::system_category(), - "Error creating io_service: CreateIoCompletionPort" - }; + std::system_category(), + "Error creating io_service: CreateIoCompletionPort" + }; } return cppcoro::detail::win32::safe_handle{ handle }; @@ -51,9 +51,9 @@ namespace throw std::system_error { static_cast(errorCode), - std::system_category(), - "Error creating manual reset event: CreateEventW" - }; + std::system_category(), + "Error creating manual reset event: CreateEventW" + }; } return cppcoro::detail::win32::safe_handle{ eventHandle }; @@ -69,8 +69,8 @@ namespace throw std::system_error { static_cast(errorCode), - std::system_category() - }; + std::system_category() + }; } return cppcoro::detail::win32::safe_handle{ handle }; @@ -106,11 +106,11 @@ class cppcoro::io_service::timer_queue void enqueue_timer(cppcoro::io_service::timed_schedule_operation* timer) noexcept; void dequeue_due_timers( - time_point currentTime, - cppcoro::io_service::timed_schedule_operation*& timerList) noexcept; + time_point currentTime, + cppcoro::io_service::timed_schedule_operation*& timerList) noexcept; void remove_cancelled_timers( - cppcoro::io_service::timed_schedule_operation*& timerList) noexcept; + cppcoro::io_service::timed_schedule_operation*& timerList) noexcept; private: @@ -165,8 +165,8 @@ cppcoro::io_service::timer_queue::earliest_due_time() const noexcept if (m_overflowTimers != nullptr) { return std::min( - m_timerEntries.front().m_dueTime, - m_overflowTimers->m_resumeTime); + m_timerEntries.front().m_dueTime, + m_overflowTimers->m_resumeTime); } return m_timerEntries.front().m_dueTime; @@ -180,7 +180,7 @@ cppcoro::io_service::timer_queue::earliest_due_time() const noexcept } void cppcoro::io_service::timer_queue::enqueue_timer( - cppcoro::io_service::timed_schedule_operation* timer) noexcept + cppcoro::io_service::timed_schedule_operation* timer) noexcept { try { @@ -203,8 +203,8 @@ void cppcoro::io_service::timer_queue::enqueue_timer( } void cppcoro::io_service::timer_queue::dequeue_due_timers( - time_point currentTime, - cppcoro::io_service::timed_schedule_operation*& timerList) noexcept + time_point currentTime, + cppcoro::io_service::timed_schedule_operation*& timerList) noexcept { while (!m_timerEntries.empty() && m_timerEntries.front().m_dueTime <= currentTime) { @@ -226,34 +226,34 @@ void cppcoro::io_service::timer_queue::dequeue_due_timers( } void cppcoro::io_service::timer_queue::remove_cancelled_timers( - cppcoro::io_service::timed_schedule_operation*& timerList) noexcept + cppcoro::io_service::timed_schedule_operation*& timerList) noexcept { // Perform a linear scan of all timers looking for any that have // had cancellation requested. const auto addTimerToList = [&](timed_schedule_operation* timer) - { - timer->m_next = timerList; - timerList = timer; - }; + { + timer->m_next = timerList; + timerList = timer; + }; const auto isTimerCancelled = [](const timer_entry& entry) - { - return entry.m_timer->m_cancellationToken.is_cancellation_requested(); - }; + { + return entry.m_timer->m_cancellationToken.is_cancellation_requested(); + }; auto firstCancelledEntry = std::find_if( - m_timerEntries.begin(), - m_timerEntries.end(), - isTimerCancelled); + m_timerEntries.begin(), + m_timerEntries.end(), + isTimerCancelled); if (firstCancelledEntry != m_timerEntries.end()) { auto nonCancelledEnd = firstCancelledEntry; addTimerToList(nonCancelledEnd->m_timer); for (auto iter = firstCancelledEntry + 1; - iter != m_timerEntries.end(); - ++iter) + iter != m_timerEntries.end(); + ++iter) { if (isTimerCancelled(*iter)) { @@ -268,9 +268,9 @@ void cppcoro::io_service::timer_queue::remove_cancelled_timers( m_timerEntries.erase(nonCancelledEnd, m_timerEntries.end()); std::make_heap( - m_timerEntries.begin(), - m_timerEntries.end(), - compare_entries); + m_timerEntries.begin(), + m_timerEntries.end(), + compare_entries); } { @@ -313,9 +313,9 @@ class cppcoro::io_service::timer_thread_state #endif #if CPPCORO_OS_LINUX - int m_wakeupfd; - int m_timerfd; - int m_epollfd; + int m_wakeupfd; + int m_timerfd; + int m_epollfd; #endif std::atomic m_newlyQueuedTimers; @@ -330,7 +330,7 @@ cppcoro::io_service::io_service() : io_service(0) #endif #if CPPCORO_OS_LINUX - : io_service(1) + : io_service(1) //queue size of message queue must be at least 1 #endif { } @@ -447,8 +447,8 @@ void cppcoro::io_service::stop() noexcept if ((oldState & stop_requested_flag) == 0) { for (auto activeThreadCount = oldState / active_thread_count_increment; - activeThreadCount > 0; - --activeThreadCount) + activeThreadCount > 0; + --activeThreadCount) { post_wake_up_event(); } @@ -490,71 +490,63 @@ cppcoro::detail::win32::handle_t cppcoro::io_service::native_iocp_handle() noexc void cppcoro::io_service::queue_overflow_operation_to_tail(schedule_operation* operation) noexcept { - // Still unable to queue these operations. - // Put them back on the list of overflow operations. - auto* tail = operation; - while (tail->m_next != nullptr) - { - tail = tail->m_next; - } + // Still unable to queue these operations. + // Put them back on the list of overflow operations. + auto* tail = operation; + while (tail->m_next != nullptr) + { + tail = tail->m_next; + } - schedule_operation* head = nullptr; - while (!m_scheduleOperations.compare_exchange_weak( - head, - operation, - std::memory_order_release, - std::memory_order_relaxed)) - { - tail->m_next = head; - } + schedule_operation* head = nullptr; + while (!m_scheduleOperations.compare_exchange_weak( + head, + operation, + std::memory_order_release, + std::memory_order_relaxed)) + { + tail->m_next = head; + } - return; + return; } void cppcoro::io_service::queue_overflow_operation_to_head(schedule_operation* operation) noexcept { - // Failed to post to the I/O completion port. - // - // This is most-likely because the queue is currently full. - // - // We'll queue up the operation to a linked-list using a lock-free - // push and defer the dispatch to the completion port until some I/O - // thread next enters its event loop. - auto* head = m_scheduleOperations.load(std::memory_order_acquire); - do - { - operation->m_next = head; - } while (!m_scheduleOperations.compare_exchange_weak( - head, - operation, - std::memory_order_release, - std::memory_order_acquire)); + // Failed to post to the I/O completion port. + // + // This is most-likely because the queue is currently full. + // + // We'll queue up the operation to a linked-list using a lock-free + // push and defer the dispatch to the completion port until some I/O + // thread next enters its event loop. + auto* head = m_scheduleOperations.load(std::memory_order_acquire); + do + { + operation->m_next = head; + } while (!m_scheduleOperations.compare_exchange_weak( + head, + operation, + std::memory_order_release, + std::memory_order_acquire)); } void cppcoro::io_service::schedule_impl(schedule_operation* operation) noexcept { #if CPPCORO_OS_LINUX - bool status = m_mq->enqueue_message(reinterpret_cast(operation->m_awaiter.address()), detail::linux::RESUME_TYPE); - - if(!status) - { - assert(errno == EAGAIN); - queue_overflow_operation_to_head(operation); - } - - return; -#endif -#if CPPCORO_OS_WINNT + const bool ok = m_mq->enqueue_message(reinterpret_cast(operation->m_awaiter.address()), + detail::linux::RESUME_TYPE); +#elif CPPCORO_OS_WINNT const BOOL ok = ::PostQueuedCompletionStatus( - m_iocpHandle.handle(), - 0, - reinterpret_cast(operation->m_awaiter.address()), - nullptr); + m_iocpHandle.handle(), + 0, + reinterpret_cast(operation->m_awaiter.address()), + nullptr); +#endif if (!ok) { - queue_overflow_operation_to_head(operation); + queue_overflow_operation_to_head(operation); } -#endif } void cppcoro::io_service::try_reschedule_overflow_operations() noexcept @@ -565,24 +557,20 @@ void cppcoro::io_service::try_reschedule_overflow_operations() noexcept auto* next = operation->m_next; #if CPPCORO_OS_WINNT BOOL ok = ::PostQueuedCompletionStatus( - m_iocpHandle.handle(), - 0, - reinterpret_cast(operation->m_awaiter.address()), - nullptr); - if (!ok) + m_iocpHandle.handle(), + 0, + reinterpret_cast(operation->m_awaiter.address()), + nullptr); +#elif CPPCORO_OS_LINUX + bool ok = m_mq->enqueue_message(reinterpret_cast(operation->m_awaiter.address()), + detail::linux::RESUME_TYPE); +#endif + + if(!ok) { - queue_overflow_operation_to_tail(operation); + queue_overflow_operation_to_tail(operation); } -#endif -#if CPPCORO_OS_LINUX - bool status = m_mq->enqueue_message(reinterpret_cast(operation->m_awaiter.address()), detail::linux::RESUME_TYPE); - if(!status) - { - assert(errno == EAGAIN); - queue_overflow_operation_to_tail(operation); - } -#endif operation = next; } } @@ -597,9 +585,9 @@ bool cppcoro::io_service::try_enter_event_loop() noexcept return false; } } while (!m_threadState.compare_exchange_weak( - currentState, - currentState + active_thread_count_increment, - std::memory_order_relaxed)); + currentState, + currentState + active_thread_count_increment, + std::memory_order_relaxed)); return true; } @@ -611,51 +599,52 @@ void cppcoro::io_service::exit_event_loop() noexcept bool cppcoro::io_service::try_process_one_event(bool waitForEvent) { - if(is_stop_requested()) - { - return false; - } + if(is_stop_requested()) + { + return false; + } #if CPPCORO_OS_LINUX - while(true) - { - try_reschedule_overflow_operations(); - void* message = NULL; - detail::linux::message_type type = detail::linux::RESUME_TYPE; + while(true) + { + try_reschedule_overflow_operations(); + void* message = NULL; + detail::linux::message_type type = detail::linux::RESUME_TYPE; - bool status = m_mq->dequeue_message(message, type, waitForEvent); + bool status = m_mq->dequeue_message(message, type, waitForEvent); - if(!status) - { - return false; - } - - if(type == detail::linux::CALLBACK_TYPE) - { - auto* state = - static_cast(reinterpret_cast(message)); - - state->m_callback(state); - - return true; - } - else - { - if((unsigned long long)message != 0) - { - std::experimental::coroutine_handle<>::from_address(reinterpret_cast(message)).resume(); - - return true; - } - - if (is_stop_requested()) - { - return false; - } + if(!status) + { + return false; + } + + if(type == detail::linux::CALLBACK_TYPE) + { + auto* state = + static_cast(reinterpret_cast(message)); + + state->m_callback(state); + + return true; + } + else + { + if((unsigned long long)message != 0) + { + std::experimental + ::coroutine_handle<>::from_address(reinterpret_cast(message)).resume(); + return true; + } + + if (is_stop_requested()) + { + return false; + } + } } - } #endif - + #if CPPCORO_OS_WINNT const DWORD timeout = waitForEvent ? INFINITE : 0; @@ -669,23 +658,24 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) ULONG_PTR completionKey = 0; LPOVERLAPPED overlapped = nullptr; BOOL ok = ::GetQueuedCompletionStatus( - m_iocpHandle.handle(), - &numberOfBytesTransferred, - &completionKey, - &overlapped, - timeout); + m_iocpHandle.handle(), + &numberOfBytesTransferred, + &completionKey, + &overlapped, + timeout); if (overlapped != nullptr) { DWORD errorCode = ok ? ERROR_SUCCESS : ::GetLastError(); - auto* state = static_cast( - reinterpret_cast(overlapped)); + auto* state = static_cast(reinterpret_cast(overlapped)); state->m_callback( - state, - errorCode, - numberOfBytesTransferred, - completionKey); + state, + errorCode, + numberOfBytesTransferred, + completionKey); return true; } @@ -695,8 +685,9 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) { // This was a coroutine scheduled via a call to // io_service::schedule(). - std::experimental::coroutine_handle<>::from_address( - reinterpret_cast(completionKey)).resume(); + std::experimental + ::coroutine_handle<> + ::from_address(reinterpret_cast(completionKey)).resume(); return true; } @@ -721,9 +712,9 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) throw std::system_error { static_cast(errorCode), - std::system_category(), - "Error retrieving item from io_service queue: GetQueuedCompletionStatus" - }; + std::system_category(), + "Error retrieving item from io_service queue: GetQueuedCompletionStatus" + }; } } #endif @@ -753,10 +744,10 @@ cppcoro::io_service::ensure_timer_thread_started() { auto newTimerState = std::make_unique(); if (m_timerState.compare_exchange_strong( - timerState, - newTimerState.get(), - std::memory_order_release, - std::memory_order_acquire)) + timerState, + newTimerState.get(), + std::memory_order_release, + std::memory_order_acquire)) { // We managed to install our timer_thread_state before some // other thread did, don't free it here - it will be freed in @@ -789,28 +780,28 @@ cppcoro::io_service::timer_thread_state::timer_thread_state() : wake_ev.data.fd = m_wakeupfd; if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_wakeupfd, &wake_ev) == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error creating io_service: epoll ctl wake ev" - }; - } + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: epoll ctl wake ev" + }; + } struct epoll_event timer_ev = {0}; timer_ev.events = EPOLLIN; timer_ev.data.fd = m_timerfd; if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_timerfd, &timer_ev) == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error creating io_service: epoll ctl timer ev" - }; - } + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: epoll ctl timer ev" + }; + } #endif } @@ -846,12 +837,12 @@ void cppcoro::io_service::timer_thread_state::run() #if CPPCORO_OS_WINNT const DWORD waitHandleCount = 2; const HANDLE waitHandles[waitHandleCount] = - { - m_wakeUpEvent.handle(), - m_waitableTimerEvent.handle() - }; - DWORD timeout = INFINITE; + { + m_wakeUpEvent.handle(), + m_waitableTimerEvent.handle() + }; #endif + DWORD timeout = INFINITE; time_point lastSetWaitEventTime = time_point::max(); @@ -859,54 +850,18 @@ void cppcoro::io_service::timer_thread_state::run() while (!m_shutDownRequested.load(std::memory_order_relaxed)) { + bool waitEvent = false; + bool timerEvent = false; #if CPPCORO_OS_WINNNT const DWORD waitResult = ::WaitForMultipleObjectsEx( - waitHandleCount, - waitHandles, - FALSE, // waitAll - timeout, - FALSE); // alertable -#endif - -#if CPPCORO_OS_LINUX - struct epoll_event ev; - const int status = epoll_wait(m_epollfd, &ev, 1, -1); + waitHandleCount, + waitHandles, + FALSE, // waitAll + timeout, + FALSE); // alertable - if(status == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error in timer thread: epoll wait" - }; - } -#endif - - -#if CPPCORO_OS_WINNT - if (waitResult == WAIT_OBJECT_0 || waitResult == WAIT_FAILED) + if(waitResult == WAIT_OBJECT_0 || waitResult == WAIT_FAILED) { -#endif -#if CPPCORO_OS_LINUX - if(status == 0) - { - continue; - } - - if (status == 1 && ev.data.fd == m_wakeupfd) - { - uint64_t count; - if(read(m_wakeupfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error in timer thread: eventfd read" - }; - } -#endif // Wake-up event (WAIT_OBJECT_0) // // We are only woken up for: @@ -917,8 +872,61 @@ void cppcoro::io_service::timer_thread_state::run() // We also handle WAIT_FAILED here so that we remain responsive // to new timers and cancellation even if the OS fails to perform // the wait operation for some reason. - // Handle cancelled timers + + waitEvent = true; + } + else if(waitResult == WAIT_OBJECT_0 + 1) + { + timerEvent = true; + } +#elif CPPCORO_OS_LINUX + struct epoll_event ev; + const int status = epoll_wait(m_epollfd, &ev, 1, timeout); + + if(status == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in timer thread: epoll wait" + }; + } + + if(status == 0 || (status == 1 && ev.data.fd == m_wakeupfd)) + { + uint64_t count; + if(read(m_wakeupfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in timer thread: eventfd read" + }; + } + + waitEvent = true; + } else if (status == 1 && ev.data.fd == m_timerfd) + { + uint64_t count; + if(read(m_timerfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in timer thread: timerfd read" + }; + } + + timerEvent = true; + } +#endif + + if(waitEvent) + { if (m_timerCancellationRequested.exchange(false, std::memory_order_acquire)) { timerQueue.remove_cancelled_timers(timersReadyToResume); @@ -941,30 +949,9 @@ void cppcoro::io_service::timer_thread_state::run() timerQueue.enqueue_timer(timer); } } - } - else if ( -#if CPPCORO_OS_WINNT - waitResult == (WAIT_OBJECT_0 + 1) -#endif -#if CPPCORO_OS_LINUX - ev.data.fd == m_timerfd -#endif - ) + } else if (timerEvent) { lastSetWaitEventTime = time_point::max(); - -#if CPPCORO_OS_LINUX - uint64_t count; - if(read(m_timerfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error in timer thread: timerfd read" - }; - } -#endif } if (!timerQueue.is_empty()) @@ -983,14 +970,17 @@ void cppcoro::io_service::timer_thread_state::run() // amount of time to wait until the next timer is ready. if (earliestDueTime != lastSetWaitEventTime) { - using ticks = std::chrono::duration>; + using ticks = std::chrono::duration>; auto timeUntilNextDueTime = earliestDueTime - currentTime; + bool ok = false; // Negative value indicates relative time. #if CPPCORO_OS_WINNT LARGE_INTEGER dueTime; - dueTime.QuadPart = -std::chrono::duration_cast(timeUntilNextDueTime).count(); + dueTime.QuadPart = -std::chrono::duration_cast + (timeUntilNextDueTime).count(); // Period of 0 indicates no repeat on the timer. const LONG period = 0; @@ -999,13 +989,41 @@ void cppcoro::io_service::timer_thread_state::run() // raise the timer event. const BOOL resumeFromSuspend = FALSE; - const BOOL ok = ::SetWaitableTimer( - m_waitableTimerEvent.handle(), - &dueTime, - period, - nullptr, - nullptr, - resumeFromSuspend); + BOOL ok = ::SetWaitableTimer( + m_waitableTimerEvent.handle(), + &dueTime, + period, + nullptr, + nullptr, + resumeFromSuspend); +#elif CPPCORO_OS_LINUX + struct itimerspec alarm_time = {0}; + alarm_time.it_value.tv_sec = + std::chrono:: + duration_cast(timeUntilNextDueTime).count(); + alarm_time.it_value.tv_nsec = + (std::chrono:: + duration_cast(timeUntilNextDueTime).count() + % 10000000) * 100; + if(alarm_time.it_value.tv_sec == 0 && alarm_time.it_value.tv_nsec == 0) + { + //linux timer of 0 time will not generate events + //so let's set it to 1 nsec + alarm_time.it_value.tv_nsec = 1; + } + + if(timerfd_settime(m_timerfd, 0, &alarm_time, NULL) == -1) + { + ok = false; + } + else + { + ok = true; + } + + lastSetWaitEventTime = earliestDueTime; +#endif if (ok) { lastSetWaitEventTime = earliestDueTime; @@ -1013,9 +1031,9 @@ void cppcoro::io_service::timer_thread_state::run() } else { - // Not sure what could cause the call to SetWaitableTimer() + // Not sure what could cause the call to waitable timer // to fail here but we'll just try falling back to using - // the timeout parameter of the WaitForMultipleObjects() call. + // the timeout parameter of the WaitFor or epoll call. // // wake-up at least once every second and retry setting // the timer at that point. @@ -1026,38 +1044,16 @@ void cppcoro::io_service::timer_thread_state::run() } else if (timeUntilNextDueTime > 1ms) { - timeout = static_cast( - std::chrono::duration_cast( - timeUntilNextDueTime).count()); + timeout = static_cast + (std::chrono:: + duration_cast(timeUntilNextDueTime).count()); } else { timeout = 1; } } -#endif -#if CPPCORO_OS_LINUX - struct itimerspec alarm_time = {0}; - alarm_time.it_value.tv_sec = std::chrono::duration_cast(timeUntilNextDueTime).count(); - alarm_time.it_value.tv_nsec = (std::chrono::duration_cast(timeUntilNextDueTime).count() % 10000000) * 100; - if(alarm_time.it_value.tv_sec == 0 && alarm_time.it_value.tv_nsec == 0) - { - //linux timer of 0 time will not generate events so let's set it to 1 nsec - alarm_time.it_value.tv_nsec = 1; - } - - if(timerfd_settime(m_timerfd, 0, &alarm_time, NULL) == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error in timer thread: timerfd_settime" - }; - } - - lastSetWaitEventTime = earliestDueTime; -#endif } } } @@ -1075,7 +1071,7 @@ void cppcoro::io_service::timer_thread_state::run() if (timer->m_refCount.fetch_sub(1, std::memory_order_release) == 1) { timer->m_scheduleOperation.m_service.schedule_impl( - &timer->m_scheduleOperation); + &timer->m_scheduleOperation); } timersReadyToResume = nextTimer; @@ -1087,32 +1083,31 @@ void cppcoro::io_service::timer_thread_state::wake_up_timer_thread() { #if CPPCORO_OS_WINNT (void)::SetEvent(m_wakeUpEvent.handle()); -#endif -#if CPPCORO_OS_LINUX +#elif CPPCORO_OS_LINUX uint64_t count = 1; if(write(m_wakeupfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error in waking up: writing to wake up eventfd" - }; - } + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in waking up: writing to wake up eventfd" + }; + } #endif } void cppcoro::io_service::schedule_operation::await_suspend( - std::experimental::coroutine_handle<> awaiter) noexcept + std::experimental::coroutine_handle<> awaiter) noexcept { m_awaiter = awaiter; m_service.schedule_impl(this); } cppcoro::io_service::timed_schedule_operation::timed_schedule_operation( - io_service& service, - std::chrono::high_resolution_clock::time_point resumeTime, - cppcoro::cancellation_token cancellationToken) noexcept + io_service& service, + std::chrono::high_resolution_clock::time_point resumeTime, + cppcoro::cancellation_token cancellationToken) noexcept : m_scheduleOperation(service) , m_resumeTime(resumeTime) , m_cancellationToken(std::move(cancellationToken)) @@ -1121,7 +1116,7 @@ cppcoro::io_service::timed_schedule_operation::timed_schedule_operation( } cppcoro::io_service::timed_schedule_operation::timed_schedule_operation( - timed_schedule_operation&& other) noexcept + timed_schedule_operation&& other) noexcept : m_scheduleOperation(std::move(other.m_scheduleOperation)) , m_resumeTime(std::move(other.m_resumeTime)) , m_cancellationToken(std::move(other.m_cancellationToken)) @@ -1139,7 +1134,7 @@ bool cppcoro::io_service::timed_schedule_operation::await_ready() const noexcept } void cppcoro::io_service::timed_schedule_operation::await_suspend( - std::experimental::coroutine_handle<> awaiter) + std::experimental::coroutine_handle<> awaiter) { m_scheduleOperation.m_awaiter = awaiter; @@ -1151,9 +1146,9 @@ void cppcoro::io_service::timed_schedule_operation::await_suspend( if (m_cancellationToken.can_be_cancelled()) { m_cancellationRegistration.emplace(m_cancellationToken, [timerState] - { - timerState->request_timer_cancellation(); - }); + { + timerState->request_timer_cancellation(); + }); } // Queue the timer schedule to the queue of incoming new timers. @@ -1183,10 +1178,10 @@ void cppcoro::io_service::timed_schedule_operation::await_suspend( { m_next = prev; } while (!timerState->m_newlyQueuedTimers.compare_exchange_weak( - prev, - this, - std::memory_order_release, - std::memory_order_acquire)); + prev, + this, + std::memory_order_release, + std::memory_order_acquire)); if (prev == nullptr) { From b8db839a5f635ecfa80b95e1e54773401e4c966a Mon Sep 17 00:00:00 2001 From: Anirudh Badam Date: Mon, 12 Feb 2018 20:20:31 +0000 Subject: [PATCH 23/34] conforming to the white space and indentation rules. --- include/cppcoro/detail/linux.hpp | 2 +- include/cppcoro/io_service.hpp | 32 +-- lib/linux.cpp | 366 ++++++++++++++--------------- test/scheduling_operator_tests.cpp | 294 +++++++++++------------ 4 files changed, 347 insertions(+), 347 deletions(-) diff --git a/include/cppcoro/detail/linux.hpp b/include/cppcoro/detail/linux.hpp index a8f14499..b40da11f 100644 --- a/include/cppcoro/detail/linux.hpp +++ b/include/cppcoro/detail/linux.hpp @@ -14,7 +14,7 @@ typedef int DWORD; #define INFINITE (DWORD)-1 - + namespace cppcoro { namespace detail diff --git a/include/cppcoro/io_service.hpp b/include/cppcoro/io_service.hpp index 99ab7168..316e264f 100644 --- a/include/cppcoro/io_service.hpp +++ b/include/cppcoro/io_service.hpp @@ -47,10 +47,10 @@ namespace cppcoro /// Note that the number of active threads may temporarily go /// above this number. #if CPPCORO_OS_WINNT - io_service(std::uint32_t concurrencyHint); + io_service(std::uint32_t concurrencyHint); #endif #if CPPCORO_OS_LINUX - io_service(size_t queue_length); + io_service(size_t queue_length); #endif ~io_service(); @@ -84,8 +84,8 @@ namespace cppcoro template [[nodiscard]] timed_schedule_operation schedule_after( - const std::chrono::duration& delay, - cancellation_token cancellationToken = {}) noexcept; + const std::chrono::duration& delay, + cancellation_token cancellationToken = {}) noexcept; /// Process events until the io_service is stopped. /// @@ -155,10 +155,10 @@ namespace cppcoro void try_reschedule_overflow_operations() noexcept; - void queue_overflow_operation_to_head(schedule_operation* operation) noexcept; + void queue_overflow_operation_to_head(schedule_operation* operation) noexcept; + + void queue_overflow_operation_to_tail(schedule_operation* operation) noexcept; - void queue_overflow_operation_to_tail(schedule_operation* operation) noexcept; - bool try_enter_event_loop() noexcept; void exit_event_loop() noexcept; @@ -182,7 +182,7 @@ namespace cppcoro #endif #if CPPCORO_OS_LINUX - detail::linux::message_queue* m_mq; + detail::linux::message_queue* m_mq; #endif // Head of a linked-list of schedule operations that are // ready to run but that failed to be queued to the I/O @@ -221,9 +221,9 @@ namespace cppcoro public: timed_schedule_operation( - io_service& service, - std::chrono::high_resolution_clock::time_point resumeTime, - cppcoro::cancellation_token cancellationToken) noexcept; + io_service& service, + std::chrono::high_resolution_clock::time_point resumeTime, + cppcoro::cancellation_token cancellationToken) noexcept; timed_schedule_operation(timed_schedule_operation&& other) noexcept; @@ -318,14 +318,14 @@ namespace cppcoro template cppcoro::io_service::timed_schedule_operation cppcoro::io_service::schedule_after( - const std::chrono::duration& duration, - cppcoro::cancellation_token cancellationToken) noexcept + const std::chrono::duration& duration, + cppcoro::cancellation_token cancellationToken) noexcept { return timed_schedule_operation{ *this, - std::chrono::high_resolution_clock::now() + duration, - std::move(cancellationToken) - }; + std::chrono::high_resolution_clock::now() + duration, + std::move(cancellationToken) + }; } #endif diff --git a/lib/linux.cpp b/lib/linux.cpp index 56d18daa..5bfdee67 100644 --- a/lib/linux.cpp +++ b/lib/linux.cpp @@ -6,188 +6,188 @@ namespace cppcoro { - namespace detail - { - namespace linux - { - message_queue::message_queue(size_t queue_length) - { - m_mqdt = -1; - uuid_t unique_name; - const char* cppcoro_qname_prefix = "/cppcoro-"; - - if(NAME_MAX < UUID_STRING_SIZE + sizeof(cppcoro_qname_prefix) + 1) - { - throw std::system_error - { - static_cast(EINVAL), - std::system_category(), - "Error creating message queue: system name max length too small" - }; - } - - strncpy(m_qname, cppcoro_qname_prefix, NAME_MAX); - - for(;;) - { - uuid_generate(unique_name); - uuid_unparse(unique_name, m_qname + sizeof(cppcoro_qname_prefix)); - - struct mq_attr attr; - attr.mq_flags = O_NONBLOCK; - attr.mq_maxmsg = queue_length; - attr.mq_msgsize = sizeof(cppcoro::detail::linux::message); - attr.mq_curmsgs = 0; - - m_mqdt = mq_open((const char*)m_qname, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, S_IRWXU, &attr); - - if( m_mqdt == -1 && errno == EEXIST) - { - continue; - } - - if( m_mqdt == -1) - { - throw std::system_error + namespace detail + { + namespace linux { - static_cast(errno), - std::system_category(), - "Error creating io_service: message queue open" - }; - } - - break; - } - - m_epollfd = create_epoll_fd(); - m_ev.data.fd = m_mqdt; - m_ev.events = EPOLLIN; - - if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_mqdt, &m_ev) == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error creating io_service: epoll ctl mqdt" - }; - } - } - - message_queue::~message_queue() - { - close(m_epollfd); - assert(mq_close(m_mqdt) == 0); - assert(mq_unlink(m_qname) == 0); - } - - bool message_queue::enqueue_message(void* msg, message_type type) - { - message qmsg; - qmsg.m_type = type; - qmsg.m_ptr = msg; - int status = mq_send(m_mqdt, (const char*)&qmsg, sizeof(message), 0); - return status==-1?false:true; - } - - bool message_queue::dequeue_message(void*& msg, message_type& type, bool wait) - { - struct epoll_event ev = {0}; - int nfds = epoll_wait(m_epollfd, &ev, 1, wait?-1:0); - - if(nfds == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error in epoll_wait run loop" - }; - } - - if(nfds == 0 && !wait) - { - return false; - } - - if(nfds == 0 && wait) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error in epoll_wait run loop" - }; - } - - message qmsg; - ssize_t status = mq_receive(m_mqdt, (char*)&qmsg, sizeof(message), NULL); - - if(status == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error retrieving message from message queue: mq_receive" - }; - } - - msg = qmsg.m_ptr; - type = qmsg.m_type; - return true; - } - - int create_event_fd() - { - int fd = eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK | EFD_CLOEXEC); - - if(fd == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error creating io_service: event fd create" - }; - } - - return fd; - } - - int create_timer_fd() - { - int fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); - - if(fd == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error creating io_service: timer fd create" - }; - } - - return fd; - } - - int create_epoll_fd() - { - int fd = epoll_create1(EPOLL_CLOEXEC); - - if(fd == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error creating timer thread: epoll create" - }; - } - - return fd; - } - } - } + message_queue::message_queue(size_t queue_length) + { + m_mqdt = -1; + uuid_t unique_name; + const char* cppcoro_qname_prefix = "/cppcoro-"; + + if(NAME_MAX < UUID_STRING_SIZE + sizeof(cppcoro_qname_prefix) + 1) + { + throw std::system_error + { + static_cast(EINVAL), + std::system_category(), + "Error creating message queue: system name max length too small" + }; + } + + strncpy(m_qname, cppcoro_qname_prefix, NAME_MAX); + + for(;;) + { + uuid_generate(unique_name); + uuid_unparse(unique_name, m_qname + sizeof(cppcoro_qname_prefix)); + + struct mq_attr attr; + attr.mq_flags = O_NONBLOCK; + attr.mq_maxmsg = queue_length; + attr.mq_msgsize = sizeof(cppcoro::detail::linux::message); + attr.mq_curmsgs = 0; + + m_mqdt = mq_open((const char*)m_qname, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, S_IRWXU, &attr); + + if( m_mqdt == -1 && errno == EEXIST) + { + continue; + } + + if( m_mqdt == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: message queue open" + }; + } + + break; + } + + m_epollfd = create_epoll_fd(); + m_ev.data.fd = m_mqdt; + m_ev.events = EPOLLIN; + + if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_mqdt, &m_ev) == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: epoll ctl mqdt" + }; + } + } + + message_queue::~message_queue() + { + close(m_epollfd); + assert(mq_close(m_mqdt) == 0); + assert(mq_unlink(m_qname) == 0); + } + + bool message_queue::enqueue_message(void* msg, message_type type) + { + message qmsg; + qmsg.m_type = type; + qmsg.m_ptr = msg; + int status = mq_send(m_mqdt, (const char*)&qmsg, sizeof(message), 0); + return status==-1?false:true; + } + + bool message_queue::dequeue_message(void*& msg, message_type& type, bool wait) + { + struct epoll_event ev = {0}; + int nfds = epoll_wait(m_epollfd, &ev, 1, wait?-1:0); + + if(nfds == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in epoll_wait run loop" + }; + } + + if(nfds == 0 && !wait) + { + return false; + } + + if(nfds == 0 && wait) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error in epoll_wait run loop" + }; + } + + message qmsg; + ssize_t status = mq_receive(m_mqdt, (char*)&qmsg, sizeof(message), NULL); + + if(status == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error retrieving message from message queue: mq_receive" + }; + } + + msg = qmsg.m_ptr; + type = qmsg.m_type; + return true; + } + + int create_event_fd() + { + int fd = eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK | EFD_CLOEXEC); + + if(fd == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: event fd create" + }; + } + + return fd; + } + + int create_timer_fd() + { + int fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); + + if(fd == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating io_service: timer fd create" + }; + } + + return fd; + } + + int create_epoll_fd() + { + int fd = epoll_create1(EPOLL_CLOEXEC); + + if(fd == -1) + { + throw std::system_error + { + static_cast(errno), + std::system_category(), + "Error creating timer thread: epoll create" + }; + } + + return fd; + } + } + } } diff --git a/test/scheduling_operator_tests.cpp b/test/scheduling_operator_tests.cpp index 6e4a092c..711522db 100644 --- a/test/scheduling_operator_tests.cpp +++ b/test/scheduling_operator_tests.cpp @@ -30,11 +30,11 @@ TEST_SUITE_BEGIN("schedule/resume_on"); static unsigned long long get_thread_id() { - unsigned long long id; - std::stringstream ss; - ss << std::this_thread::get_id(); - id = std::stoull(ss.str()); - return id; + unsigned long long id; + std::stringstream ss; + ss << std::this_thread::get_id(); + id = std::stoull(ss.str()); + return id; } #endif @@ -45,20 +45,20 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on task<> function") THREAD_ID ioThreadId; auto start = [&]() -> cppcoro::task<> - { - ioThreadId = GET_THIS_THREAD_ID; - CHECK(ioThreadId != mainThreadId); - co_return; - }; + { + ioThreadId = GET_THIS_THREAD_ID; + CHECK(ioThreadId != mainThreadId); + co_return; + }; cppcoro::sync_wait([&]() -> cppcoro::task<> - { - CHECK(GET_THIS_THREAD_ID == mainThreadId); + { + CHECK(GET_THIS_THREAD_ID == mainThreadId); - co_await schedule_on(io_service(), start()); + co_await schedule_on(io_service(), start()); - CHECK(GET_THIS_THREAD_ID == ioThreadId); - }()); + CHECK(GET_THIS_THREAD_ID == ioThreadId); + }()); } TEST_CASE_FIXTURE(io_service_fixture, "schedule_on async_generator<> function") @@ -68,52 +68,52 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on async_generator<> function") THREAD_ID ioThreadId; auto makeSequence = [&]() -> cppcoro::async_generator - { - ioThreadId = GET_THIS_THREAD_ID; - CHECK(ioThreadId != mainThreadId); + { + ioThreadId = GET_THIS_THREAD_ID; + CHECK(ioThreadId != mainThreadId); - co_yield 1; + co_yield 1; - CHECK(GET_THIS_THREAD_ID == ioThreadId); + CHECK(GET_THIS_THREAD_ID == ioThreadId); - co_yield 2; + co_yield 2; - CHECK(GET_THIS_THREAD_ID == ioThreadId); + CHECK(GET_THIS_THREAD_ID == ioThreadId); - co_yield 3; + co_yield 3; - CHECK(GET_THIS_THREAD_ID == ioThreadId); + CHECK(GET_THIS_THREAD_ID == ioThreadId); - co_return; - }; + co_return; + }; cppcoro::io_service otherIoService; cppcoro::sync_wait(cppcoro::when_all_ready( - [&]() -> cppcoro::task<> - { - CHECK(GET_THIS_THREAD_ID == mainThreadId); - - auto seq = schedule_on(io_service(), makeSequence()); - - int expected = 1; - for co_await(int value : seq) - { - CHECK(value == expected++); - - // Transfer exection back to main thread before - // awaiting next item in the loop to chck that - // the generator is resumed on io_service() thread. - co_await otherIoService.schedule(); - } - - otherIoService.stop(); - }(), - [&]() -> cppcoro::task<> - { - otherIoService.process_events(); - co_return; - }())); + [&]() -> cppcoro::task<> + { + CHECK(GET_THIS_THREAD_ID == mainThreadId); + + auto seq = schedule_on(io_service(), makeSequence()); + + int expected = 1; + for co_await(int value : seq) + { + CHECK(value == expected++); + + // Transfer exection back to main thread before + // awaiting next item in the loop to chck that + // the generator is resumed on io_service() thread. + co_await otherIoService.schedule(); + } + + otherIoService.stop(); + }(), + [&]() -> cppcoro::task<> + { + otherIoService.process_events(); + co_return; + }())); } TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> function") @@ -121,19 +121,19 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> function") auto mainThreadId = GET_THIS_THREAD_ID; auto start = [&]() -> cppcoro::task<> - { - CHECK(GET_THIS_THREAD_ID == mainThreadId); - co_return; - }; + { + CHECK(GET_THIS_THREAD_ID == mainThreadId); + co_return; + }; cppcoro::sync_wait([&]() -> cppcoro::task<> - { - CHECK(GET_THIS_THREAD_ID == mainThreadId); + { + CHECK(GET_THIS_THREAD_ID == mainThreadId); - co_await resume_on(io_service(), start()); + co_await resume_on(io_service(), start()); - CHECK(GET_THIS_THREAD_ID != mainThreadId); - }()); + CHECK(GET_THIS_THREAD_ID != mainThreadId); + }()); } constexpr bool isMsvc15_4X86Optimised = @@ -153,60 +153,60 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function" THREAD_ID ioThreadId; auto makeSequence = [&]() -> cppcoro::async_generator - { - co_await io_service().schedule(); + { + co_await io_service().schedule(); - ioThreadId = GET_THIS_THREAD_ID; + ioThreadId = GET_THIS_THREAD_ID; - CHECK(ioThreadId != mainThreadId); + CHECK(ioThreadId != mainThreadId); - co_yield 1; + co_yield 1; - co_yield 2; + co_yield 2; - co_await io_service().schedule(); + co_await io_service().schedule(); - co_yield 3; + co_yield 3; - co_await io_service().schedule(); + co_await io_service().schedule(); - co_return; - }; + co_return; + }; cppcoro::io_service otherIoService; cppcoro::sync_wait(cppcoro::when_all_ready( - [&]() -> cppcoro::task<> - { - auto stopOnExit = cppcoro::on_scope_exit([&] { otherIoService.stop(); }); - - CHECK(GET_THIS_THREAD_ID == mainThreadId); - - auto seq = resume_on(otherIoService, makeSequence()); - - int expected = 1; - for co_await(int value : seq) - { - // Every time we receive a value it should be on our requested - // scheduler (ie. main thread) - CHECK(GET_THIS_THREAD_ID == mainThreadId); - CHECK(value == expected++); - - // Occasionally transfer execution to a different thread before - // awaiting next element. - if (value == 2) - { - co_await io_service().schedule(); - } - } - - otherIoService.stop(); - }(), - [&]() -> cppcoro::task<> - { - otherIoService.process_events(); - co_return; - }())); + [&]() -> cppcoro::task<> + { + auto stopOnExit = cppcoro::on_scope_exit([&] { otherIoService.stop(); }); + + CHECK(GET_THIS_THREAD_ID == mainThreadId); + + auto seq = resume_on(otherIoService, makeSequence()); + + int expected = 1; + for co_await(int value : seq) + { + // Every time we receive a value it should be on our requested + // scheduler (ie. main thread) + CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(value == expected++); + + // Occasionally transfer execution to a different thread before + // awaiting next element. + if (value == 2) + { + co_await io_service().schedule(); + } + } + + otherIoService.stop(); + }(), + [&]() -> cppcoro::task<> + { + otherIoService.process_events(); + co_return; + }())); } TEST_CASE_FIXTURE(io_service_fixture, "schedule_on task<> pipe syntax") @@ -214,16 +214,16 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on task<> pipe syntax") auto mainThreadId = GET_THIS_THREAD_ID; auto makeTask = [&]() -> cppcoro::task - { - CHECK(GET_THIS_THREAD_ID != mainThreadId); - co_return 123; - }; + { + CHECK(GET_THIS_THREAD_ID != mainThreadId); + co_return 123; + }; auto triple = [&](int x) - { - CHECK(GET_THIS_THREAD_ID != mainThreadId); - return x * 3; - }; + { + CHECK(GET_THIS_THREAD_ID != mainThreadId); + return x * 3; + }; CHECK(cppcoro::sync_wait(makeTask() | schedule_on(io_service())) == 123); @@ -238,17 +238,17 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax") auto mainThreadId = GET_THIS_THREAD_ID; auto makeTask = [&]() -> cppcoro::task - { - CHECK(GET_THIS_THREAD_ID == mainThreadId); - co_return 123; - }; + { + CHECK(GET_THIS_THREAD_ID == mainThreadId); + co_return 123; + }; cppcoro::sync_wait([&]() -> cppcoro::task<> - { - cppcoro::task t = makeTask() | cppcoro::resume_on(io_service()); - CHECK(co_await t == 123); - CHECK(GET_THIS_THREAD_ID != mainThreadId); - }()); + { + cppcoro::task t = makeTask() | cppcoro::resume_on(io_service()); + CHECK(co_await t == 123); + CHECK(GET_THIS_THREAD_ID != mainThreadId); + }()); } TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax multiple uses") @@ -256,41 +256,41 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax multiple use auto mainThreadId = GET_THIS_THREAD_ID; auto makeTask = [&]() -> cppcoro::task - { - CHECK(GET_THIS_THREAD_ID == mainThreadId); - co_return 123; - }; + { + CHECK(GET_THIS_THREAD_ID == mainThreadId); + co_return 123; + }; auto triple = [&](int x) - { - CHECK(GET_THIS_THREAD_ID != mainThreadId); - return x * 3; - }; + { + CHECK(GET_THIS_THREAD_ID != mainThreadId); + return x * 3; + }; cppcoro::io_service otherIoService; cppcoro::sync_wait(cppcoro::when_all_ready( - [&]() -> cppcoro::task<> - { - auto stopOnExit = cppcoro::on_scope_exit([&] { otherIoService.stop(); }); - - CHECK(GET_THIS_THREAD_ID == mainThreadId); - - cppcoro::task t = - makeTask() - | cppcoro::resume_on(io_service()) - | cppcoro::fmap(triple) - | cppcoro::resume_on(otherIoService); - - CHECK(co_await t == 369); - - CHECK(GET_THIS_THREAD_ID == mainThreadId); - }(), - [&]() -> cppcoro::task<> - { - otherIoService.process_events(); - co_return; - }())); + [&]() -> cppcoro::task<> + { + auto stopOnExit = cppcoro::on_scope_exit([&] { otherIoService.stop(); }); + + CHECK(GET_THIS_THREAD_ID == mainThreadId); + + cppcoro::task t = + makeTask() + | cppcoro::resume_on(io_service()) + | cppcoro::fmap(triple) + | cppcoro::resume_on(otherIoService); + + CHECK(co_await t == 369); + + CHECK(GET_THIS_THREAD_ID == mainThreadId); + }(), + [&]() -> cppcoro::task<> + { + otherIoService.process_events(); + co_return; + }())); } TEST_SUITE_END(); From cc86c9b3336b39ff4b43d7859dc84b294b8a9cbf Mon Sep 17 00:00:00 2001 From: Anirudh Badam Date: Wed, 11 Apr 2018 21:17:07 +0000 Subject: [PATCH 24/34] fixed the minor comments from lewis. --- include/cppcoro/detail/linux.hpp | 82 +++++++++++++++++++++++++++--- lib/io_service.cpp | 64 ++++++++++------------- lib/linux.cpp | 32 +++++++----- test/scheduling_operator_tests.cpp | 72 +++++++++++++------------- 4 files changed, 158 insertions(+), 92 deletions(-) diff --git a/include/cppcoro/detail/linux.hpp b/include/cppcoro/detail/linux.hpp index b40da11f..caba438e 100644 --- a/include/cppcoro/detail/linux.hpp +++ b/include/cppcoro/detail/linux.hpp @@ -11,9 +11,7 @@ #include #include #include - -typedef int DWORD; -#define INFINITE (DWORD)-1 +#include namespace cppcoro { @@ -21,12 +19,81 @@ namespace cppcoro { namespace linux { + using fd_t = int; + enum message_type { CALLBACK_TYPE, RESUME_TYPE }; + class safe_fd + { + public: + + safe_fd() + : m_fd(-1) + {} + + explicit safe_fd(fd_t fd) + : m_fd(fd) + {} + + safe_fd(const safe_fd& other) = delete; + + safe_fd(safe_fd&& other) noexcept + : m_fd(other.m_fd) + { + other.m_fd = -1; + } + + ~safe_fd() + { + close(); + } + + safe_fd& operator=(safe_fd fd) noexcept + { + swap(fd); + return *this; + } + + constexpr fd_t fd() const { return m_fd; } + + /// Calls close() and sets the fd to -1. + void close() noexcept; + + void swap(safe_fd& other) noexcept + { + std::swap(m_fd, other.m_fd); + } + + bool operator==(const safe_fd& other) const + { + return m_fd == other.m_fd; + } + + bool operator!=(const safe_fd& other) const + { + return m_fd != other.m_fd; + } + + bool operator==(fd_t fd) const + { + return m_fd == fd; + } + + bool operator!=(fd_t fd) const + { + return m_fd != fd; + } + + private: + + fd_t m_fd; + + }; + struct message { enum message_type m_type; @@ -44,7 +111,7 @@ namespace cppcoro private: mqd_t m_mqdt; char m_qname[NAME_MAX]; - int m_epollfd; + safe_fd m_epollfd; struct epoll_event m_ev; message_queue(); public: @@ -54,10 +121,9 @@ namespace cppcoro bool dequeue_message(void*& message, message_type& type, bool wait); }; - int create_event_fd(); - int create_timer_fd(); - int create_epoll_fd(); - + safe_fd create_event_fd(); + safe_fd create_timer_fd(); + safe_fd create_epoll_fd(); } } } diff --git a/lib/io_service.cpp b/lib/io_service.cpp index 0dba7437..bb6acbd3 100644 --- a/lib/io_service.cpp +++ b/lib/io_service.cpp @@ -22,6 +22,11 @@ # include #endif +#if CPPCORO_OS_LINUX +typedef int DWORD; +#define INFINITE (DWORD)-1 //needed for timeout values in io_service::timer_thread_state::run() +#endif + namespace { #if CPPCORO_OS_WINNT @@ -305,7 +310,7 @@ class cppcoro::io_service::timer_thread_state void run(); - void wake_up_timer_thread(); + void wake_up_timer_thread() noexcept; #if CPPCORO_OS_WINNT detail::win32::safe_handle m_wakeUpEvent; @@ -313,9 +318,9 @@ class cppcoro::io_service::timer_thread_state #endif #if CPPCORO_OS_LINUX - int m_wakeupfd; - int m_timerfd; - int m_epollfd; + detail::linux::safe_fd m_wakeupfd; + detail::linux::safe_fd m_timerfd; + detail::linux::safe_fd m_epollfd; #endif std::atomic m_newlyQueuedTimers; @@ -330,7 +335,7 @@ cppcoro::io_service::io_service() : io_service(0) #endif #if CPPCORO_OS_LINUX - : io_service(1) //queue size of message queue must be at least 1 + : io_service(10) //queue size of message queue must be at least 10 #endif { } @@ -775,11 +780,11 @@ cppcoro::io_service::timer_thread_state::timer_thread_state() : , m_thread([this] { this->run(); }) { #if CPPCORO_OS_LINUX - struct epoll_event wake_ev = {0}; + epoll_event wake_ev = {0}; wake_ev.events = EPOLLIN; - wake_ev.data.fd = m_wakeupfd; + wake_ev.data.fd = m_wakeupfd.fd(); - if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_wakeupfd, &wake_ev) == -1) + if(epoll_ctl(m_epollfd.fd(), EPOLL_CTL_ADD, m_wakeupfd.fd(), &wake_ev) == -1) { throw std::system_error { @@ -789,11 +794,11 @@ cppcoro::io_service::timer_thread_state::timer_thread_state() : }; } - struct epoll_event timer_ev = {0}; + epoll_event timer_ev = {0}; timer_ev.events = EPOLLIN; - timer_ev.data.fd = m_timerfd; + timer_ev.data.fd = m_timerfd.fd(); - if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_timerfd, &timer_ev) == -1) + if(epoll_ctl(m_epollfd.fd(), EPOLL_CTL_ADD, m_timerfd.fd(), &timer_ev) == -1) { throw std::system_error { @@ -810,11 +815,6 @@ cppcoro::io_service::timer_thread_state::~timer_thread_state() m_shutDownRequested.store(true, std::memory_order_release); wake_up_timer_thread(); m_thread.join(); -#if CPPCORO_OS_LINUX - close(m_wakeupfd); - close(m_timerfd); - close(m_epollfd); -#endif } void cppcoro::io_service::timer_thread_state::request_timer_cancellation() noexcept @@ -881,8 +881,8 @@ void cppcoro::io_service::timer_thread_state::run() timerEvent = true; } #elif CPPCORO_OS_LINUX - struct epoll_event ev; - const int status = epoll_wait(m_epollfd, &ev, 1, timeout); + epoll_event ev; + const int status = epoll_wait(m_epollfd.fd(), &ev, 1, timeout); if(status == -1) { @@ -894,10 +894,10 @@ void cppcoro::io_service::timer_thread_state::run() }; } - if(status == 0 || (status == 1 && ev.data.fd == m_wakeupfd)) + if(status == 0 || (status == 1 && ev.data.fd == m_wakeupfd.fd())) { uint64_t count; - if(read(m_wakeupfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) + if(read(m_wakeupfd.fd(), &count, sizeof(uint64_t)) != sizeof(uint64_t)) { throw std::system_error { @@ -908,10 +908,10 @@ void cppcoro::io_service::timer_thread_state::run() } waitEvent = true; - } else if (status == 1 && ev.data.fd == m_timerfd) + } else if (status == 1 && ev.data.fd == m_timerfd.fd()) { uint64_t count; - if(read(m_timerfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) + if(read(m_timerfd.fd(), &count, sizeof(uint64_t)) != sizeof(uint64_t)) { throw std::system_error { @@ -997,15 +997,15 @@ void cppcoro::io_service::timer_thread_state::run() nullptr, resumeFromSuspend); #elif CPPCORO_OS_LINUX - struct itimerspec alarm_time = {0}; + itimerspec alarm_time = {0}; alarm_time.it_value.tv_sec = std::chrono:: duration_cast(timeUntilNextDueTime).count(); alarm_time.it_value.tv_nsec = (std::chrono:: - duration_cast(timeUntilNextDueTime).count() - % 10000000) * 100; + duration_cast(timeUntilNextDueTime).count() % 10000000); if(alarm_time.it_value.tv_sec == 0 && alarm_time.it_value.tv_nsec == 0) { //linux timer of 0 time will not generate events @@ -1013,7 +1013,7 @@ void cppcoro::io_service::timer_thread_state::run() alarm_time.it_value.tv_nsec = 1; } - if(timerfd_settime(m_timerfd, 0, &alarm_time, NULL) == -1) + if(timerfd_settime(m_timerfd.fd(), 0, &alarm_time, NULL) == -1) { ok = false; } @@ -1079,21 +1079,13 @@ void cppcoro::io_service::timer_thread_state::run() } } -void cppcoro::io_service::timer_thread_state::wake_up_timer_thread() +void cppcoro::io_service::timer_thread_state::wake_up_timer_thread() noexcept { #if CPPCORO_OS_WINNT (void)::SetEvent(m_wakeUpEvent.handle()); #elif CPPCORO_OS_LINUX uint64_t count = 1; - if(write(m_wakeupfd, &count, sizeof(uint64_t)) != sizeof(uint64_t)) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error in waking up: writing to wake up eventfd" - }; - } + (void)write(m_wakeupfd.fd(), &count, sizeof(uint64_t)); #endif } diff --git a/lib/linux.cpp b/lib/linux.cpp index 5bfdee67..8912686e 100644 --- a/lib/linux.cpp +++ b/lib/linux.cpp @@ -16,7 +16,7 @@ namespace cppcoro uuid_t unique_name; const char* cppcoro_qname_prefix = "/cppcoro-"; - if(NAME_MAX < UUID_STRING_SIZE + sizeof(cppcoro_qname_prefix) + 1) + if(NAME_MAX < UUID_STRING_SIZE + strlen(cppcoro_qname_prefix) + 1) { throw std::system_error { @@ -39,7 +39,7 @@ namespace cppcoro attr.mq_msgsize = sizeof(cppcoro::detail::linux::message); attr.mq_curmsgs = 0; - m_mqdt = mq_open((const char*)m_qname, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, S_IRWXU, &attr); + m_mqdt = mq_open(m_qname, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, S_IRWXU, &attr); if( m_mqdt == -1 && errno == EEXIST) { @@ -59,11 +59,11 @@ namespace cppcoro break; } - m_epollfd = create_epoll_fd(); + m_epollfd = safe_fd{create_epoll_fd()}; m_ev.data.fd = m_mqdt; m_ev.events = EPOLLIN; - if(epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_mqdt, &m_ev) == -1) + if(epoll_ctl(m_epollfd.fd(), EPOLL_CTL_ADD, m_mqdt, &m_ev) == -1) { throw std::system_error { @@ -76,7 +76,6 @@ namespace cppcoro message_queue::~message_queue() { - close(m_epollfd); assert(mq_close(m_mqdt) == 0); assert(mq_unlink(m_qname) == 0); } @@ -93,7 +92,7 @@ namespace cppcoro bool message_queue::dequeue_message(void*& msg, message_type& type, bool wait) { struct epoll_event ev = {0}; - int nfds = epoll_wait(m_epollfd, &ev, 1, wait?-1:0); + int nfds = epoll_wait(m_epollfd.fd(), &ev, 1, wait?-1:0); if(nfds == -1) { @@ -138,7 +137,7 @@ namespace cppcoro return true; } - int create_event_fd() + safe_fd create_event_fd() { int fd = eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK | EFD_CLOEXEC); @@ -152,10 +151,10 @@ namespace cppcoro }; } - return fd; + return safe_fd{fd}; } - int create_timer_fd() + safe_fd create_timer_fd() { int fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); @@ -169,10 +168,10 @@ namespace cppcoro }; } - return fd; + return safe_fd{fd}; } - int create_epoll_fd() + safe_fd create_epoll_fd() { int fd = epoll_create1(EPOLL_CLOEXEC); @@ -186,7 +185,16 @@ namespace cppcoro }; } - return fd; + return safe_fd{fd}; + } + + void safe_fd::close() noexcept + { + if(m_fd != -1) + { + ::close(m_fd); + m_fd = -1; + } } } } diff --git a/test/scheduling_operator_tests.cpp b/test/scheduling_operator_tests.cpp index 711522db..97f47321 100644 --- a/test/scheduling_operator_tests.cpp +++ b/test/scheduling_operator_tests.cpp @@ -18,13 +18,13 @@ TEST_SUITE_BEGIN("schedule/resume_on"); #if CPPCORO_OS_WINNT -#define THREAD_ID std::thread::id -#define GET_THIS_THREAD_ID std::this_thread::get_id() +typedef thread_id std::thread::id +#define get_thread_id std::this_thread::get_id #endif #if CPPCORO_OS_LINUX -#define THREAD_ID unsigned long long -#define GET_THIS_THREAD_ID get_thread_id() +#define thread_id unsigned long long +#define get_thread_id() get_thread_id() #include @@ -40,49 +40,49 @@ static unsigned long long get_thread_id() TEST_CASE_FIXTURE(io_service_fixture, "schedule_on task<> function") { - auto mainThreadId = GET_THIS_THREAD_ID; + auto mainThreadId = get_thread_id(); - THREAD_ID ioThreadId; + thread_id ioThreadId; auto start = [&]() -> cppcoro::task<> { - ioThreadId = GET_THIS_THREAD_ID; + ioThreadId = get_thread_id(); CHECK(ioThreadId != mainThreadId); co_return; }; cppcoro::sync_wait([&]() -> cppcoro::task<> { - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); co_await schedule_on(io_service(), start()); - CHECK(GET_THIS_THREAD_ID == ioThreadId); + CHECK(get_thread_id() == ioThreadId); }()); } TEST_CASE_FIXTURE(io_service_fixture, "schedule_on async_generator<> function") { - auto mainThreadId = GET_THIS_THREAD_ID; + auto mainThreadId = get_thread_id(); - THREAD_ID ioThreadId; + thread_id ioThreadId; auto makeSequence = [&]() -> cppcoro::async_generator { - ioThreadId = GET_THIS_THREAD_ID; + ioThreadId = get_thread_id(); CHECK(ioThreadId != mainThreadId); co_yield 1; - CHECK(GET_THIS_THREAD_ID == ioThreadId); + CHECK(get_thread_id() == ioThreadId); co_yield 2; - CHECK(GET_THIS_THREAD_ID == ioThreadId); + CHECK(get_thread_id() == ioThreadId); co_yield 3; - CHECK(GET_THIS_THREAD_ID == ioThreadId); + CHECK(get_thread_id() == ioThreadId); co_return; }; @@ -92,7 +92,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on async_generator<> function") cppcoro::sync_wait(cppcoro::when_all_ready( [&]() -> cppcoro::task<> { - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); auto seq = schedule_on(io_service(), makeSequence()); @@ -118,21 +118,21 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on async_generator<> function") TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> function") { - auto mainThreadId = GET_THIS_THREAD_ID; + auto mainThreadId = get_thread_id(); auto start = [&]() -> cppcoro::task<> { - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); co_return; }; cppcoro::sync_wait([&]() -> cppcoro::task<> { - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); co_await resume_on(io_service(), start()); - CHECK(GET_THIS_THREAD_ID != mainThreadId); + CHECK(get_thread_id() != mainThreadId); }()); } @@ -148,15 +148,15 @@ constexpr bool isMsvc15_4X86Optimised = TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function" * doctest::skip{ isMsvc15_4X86Optimised }) { - auto mainThreadId = GET_THIS_THREAD_ID; + auto mainThreadId = get_thread_id(); - THREAD_ID ioThreadId; + thread_id ioThreadId; auto makeSequence = [&]() -> cppcoro::async_generator { co_await io_service().schedule(); - ioThreadId = GET_THIS_THREAD_ID; + ioThreadId = get_thread_id(); CHECK(ioThreadId != mainThreadId); @@ -180,7 +180,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function" { auto stopOnExit = cppcoro::on_scope_exit([&] { otherIoService.stop(); }); - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); auto seq = resume_on(otherIoService, makeSequence()); @@ -189,7 +189,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function" { // Every time we receive a value it should be on our requested // scheduler (ie. main thread) - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); CHECK(value == expected++); // Occasionally transfer execution to a different thread before @@ -211,17 +211,17 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on async_generator<> function" TEST_CASE_FIXTURE(io_service_fixture, "schedule_on task<> pipe syntax") { - auto mainThreadId = GET_THIS_THREAD_ID; + auto mainThreadId = get_thread_id(); auto makeTask = [&]() -> cppcoro::task { - CHECK(GET_THIS_THREAD_ID != mainThreadId); + CHECK(get_thread_id() != mainThreadId); co_return 123; }; auto triple = [&](int x) { - CHECK(GET_THIS_THREAD_ID != mainThreadId); + CHECK(get_thread_id() != mainThreadId); return x * 3; }; @@ -235,11 +235,11 @@ TEST_CASE_FIXTURE(io_service_fixture, "schedule_on task<> pipe syntax") TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax") { - auto mainThreadId = GET_THIS_THREAD_ID; + auto mainThreadId = get_thread_id(); auto makeTask = [&]() -> cppcoro::task { - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); co_return 123; }; @@ -247,23 +247,23 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax") { cppcoro::task t = makeTask() | cppcoro::resume_on(io_service()); CHECK(co_await t == 123); - CHECK(GET_THIS_THREAD_ID != mainThreadId); + CHECK(get_thread_id() != mainThreadId); }()); } TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax multiple uses") { - auto mainThreadId = GET_THIS_THREAD_ID; + auto mainThreadId = get_thread_id(); auto makeTask = [&]() -> cppcoro::task { - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); co_return 123; }; auto triple = [&](int x) { - CHECK(GET_THIS_THREAD_ID != mainThreadId); + CHECK(get_thread_id() != mainThreadId); return x * 3; }; @@ -274,7 +274,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax multiple use { auto stopOnExit = cppcoro::on_scope_exit([&] { otherIoService.stop(); }); - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); cppcoro::task t = makeTask() @@ -284,7 +284,7 @@ TEST_CASE_FIXTURE(io_service_fixture, "resume_on task<> pipe syntax multiple use CHECK(co_await t == 369); - CHECK(GET_THIS_THREAD_ID == mainThreadId); + CHECK(get_thread_id() == mainThreadId); }(), [&]() -> cppcoro::task<> { From 343fe0a4e78ca991c7ea6a083185a0efc35c6385 Mon Sep 17 00:00:00 2001 From: Anirudh Badam Date: Thu, 12 Apr 2018 18:23:17 +0000 Subject: [PATCH 25/34] changes for white space based on clang-format.el --- include/cppcoro/detail/linux.hpp | 10 +++++----- lib/io_service.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/cppcoro/detail/linux.hpp b/include/cppcoro/detail/linux.hpp index caba438e..6306465b 100644 --- a/include/cppcoro/detail/linux.hpp +++ b/include/cppcoro/detail/linux.hpp @@ -22,12 +22,12 @@ namespace cppcoro using fd_t = int; enum message_type - { - CALLBACK_TYPE, - RESUME_TYPE - }; + { + CALLBACK_TYPE, + RESUME_TYPE + }; - class safe_fd + class safe_fd { public: diff --git a/lib/io_service.cpp b/lib/io_service.cpp index bb6acbd3..8c692345 100644 --- a/lib/io_service.cpp +++ b/lib/io_service.cpp @@ -1001,7 +1001,7 @@ void cppcoro::io_service::timer_thread_state::run() alarm_time.it_value.tv_sec = std::chrono:: duration_cast(timeUntilNextDueTime).count(); + seconds>(timeUntilNextDueTime).count(); alarm_time.it_value.tv_nsec = (std::chrono:: duration_cast Date: Mon, 16 Apr 2018 18:16:20 +0000 Subject: [PATCH 26/34] copyright block --- include/cppcoro/detail/linux.hpp | 4 ++++ lib/linux.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/include/cppcoro/detail/linux.hpp b/include/cppcoro/detail/linux.hpp index 6306465b..136ff4ff 100644 --- a/include/cppcoro/detail/linux.hpp +++ b/include/cppcoro/detail/linux.hpp @@ -1,3 +1,7 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Microsoft +// Licenced under MIT license. See LICENSE.txt for details. +/////////////////////////////////////////////////////////////////////////////// #pragma once #include diff --git a/lib/linux.cpp b/lib/linux.cpp index 8912686e..ab60d8b4 100644 --- a/lib/linux.cpp +++ b/lib/linux.cpp @@ -1,3 +1,7 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Microsoft +// Licenced under MIT license. See LICENSE.txt for details. +/////////////////////////////////////////////////////////////////////////////// #include #include #include From b006b0479200aec3a316ca37b0f33c8cb16be0d4 Mon Sep 17 00:00:00 2001 From: Anirudh Badam Date: Wed, 20 Jun 2018 13:41:47 -0700 Subject: [PATCH 27/34] rebasing with upstream master and fixing some windows bugs. --- lib/io_service.cpp | 2 +- test/scheduling_operator_tests.cpp | 2 +- tools/cake | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/io_service.cpp b/lib/io_service.cpp index 8c692345..8a10f34c 100644 --- a/lib/io_service.cpp +++ b/lib/io_service.cpp @@ -975,7 +975,6 @@ void cppcoro::io_service::timer_thread_state::run() auto timeUntilNextDueTime = earliestDueTime - currentTime; - bool ok = false; // Negative value indicates relative time. #if CPPCORO_OS_WINNT LARGE_INTEGER dueTime; @@ -997,6 +996,7 @@ void cppcoro::io_service::timer_thread_state::run() nullptr, resumeFromSuspend); #elif CPPCORO_OS_LINUX + bool ok = false; itimerspec alarm_time = {0}; alarm_time.it_value.tv_sec = std::chrono:: diff --git a/test/scheduling_operator_tests.cpp b/test/scheduling_operator_tests.cpp index 97f47321..6d7b368c 100644 --- a/test/scheduling_operator_tests.cpp +++ b/test/scheduling_operator_tests.cpp @@ -18,7 +18,7 @@ TEST_SUITE_BEGIN("schedule/resume_on"); #if CPPCORO_OS_WINNT -typedef thread_id std::thread::id +#define thread_id std::thread::id #define get_thread_id std::this_thread::get_id #endif diff --git a/tools/cake b/tools/cake index db43f336..429a0f9d 160000 --- a/tools/cake +++ b/tools/cake @@ -1 +1 @@ -Subproject commit db43f336b95d367163b967491f4e22d252ae59b4 +Subproject commit 429a0f9d5d13257b29fd445e0d2391268fe97d45 From 87910b861e922cc7eefda0c2cb216a97d7493001 Mon Sep 17 00:00:00 2001 From: Anirudh Badam Date: Wed, 20 Jun 2018 13:46:14 -0700 Subject: [PATCH 28/34] removing repeated files from cake. --- test/build.cake | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/build.cake b/test/build.cake index 9480e280..02346b0f 100644 --- a/test/build.cake +++ b/test/build.cake @@ -41,8 +41,6 @@ sources = script.cwd([ 'ipv6_endpoint_tests.cpp', 'scheduling_operator_tests.cpp', 'io_service_tests.cpp', - 'scheduling_operator_tests.cpp', - 'io_service_tests.cpp', ]) if variant.platform == 'windows': From 8007d4ff622b26c086c1c329e9e600f93d2404aa Mon Sep 17 00:00:00 2001 From: Anirudh Badam Date: Wed, 20 Jun 2018 15:38:58 -0700 Subject: [PATCH 29/34] indentation fixes. --- config.cake | 4 +- include/cppcoro/io_service.hpp | 70 ++++---- lib/io_service.cpp | 302 ++++++++++++++++----------------- 3 files changed, 188 insertions(+), 188 deletions(-) diff --git a/config.cake b/config.cake index 93c09cc5..da31b676 100644 --- a/config.cake +++ b/config.cake @@ -296,8 +296,8 @@ elif cake.system.isLinux() or cake.system.isDarwin(): compiler.addLibrary('c') compiler.addLibrary('pthread') compiler.addLibrary('rt') - compiler.addLibrary('uuid') - + compiler.addLibrary('uuid') + #compiler.addProgramFlag('-Wl,--trace') #compiler.addProgramFlag('-Wl,-v') diff --git a/include/cppcoro/io_service.hpp b/include/cppcoro/io_service.hpp index 316e264f..3702f76b 100644 --- a/include/cppcoro/io_service.hpp +++ b/include/cppcoro/io_service.hpp @@ -84,8 +84,8 @@ namespace cppcoro template [[nodiscard]] timed_schedule_operation schedule_after( - const std::chrono::duration& delay, - cancellation_token cancellationToken = {}) noexcept; + const std::chrono::duration& delay, + cancellation_token cancellationToken = {}) noexcept; /// Process events until the io_service is stopped. /// @@ -199,7 +199,7 @@ namespace cppcoro schedule_operation(io_service& service) noexcept : m_service(service) - {} + {} bool await_ready() const noexcept { return false; } void await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept; @@ -221,9 +221,9 @@ namespace cppcoro public: timed_schedule_operation( - io_service& service, - std::chrono::high_resolution_clock::time_point resumeTime, - cppcoro::cancellation_token cancellationToken) noexcept; + io_service& service, + std::chrono::high_resolution_clock::time_point resumeTime, + cppcoro::cancellation_token cancellationToken) noexcept; timed_schedule_operation(timed_schedule_operation&& other) noexcept; @@ -260,48 +260,48 @@ namespace cppcoro explicit io_work_scope(io_service& service) noexcept : m_service(&service) - { - service.notify_work_started(); - } + { + service.notify_work_started(); + } io_work_scope(const io_work_scope& other) noexcept : m_service(other.m_service) - { - if (m_service != nullptr) { - m_service->notify_work_started(); + if (m_service != nullptr) + { + m_service->notify_work_started(); + } } - } io_work_scope(io_work_scope&& other) noexcept : m_service(other.m_service) - { - other.m_service = nullptr; - } + { + other.m_service = nullptr; + } ~io_work_scope() - { - if (m_service != nullptr) { - m_service->notify_work_finished(); + if (m_service != nullptr) + { + m_service->notify_work_finished(); + } } - } void swap(io_work_scope& other) noexcept - { - std::swap(m_service, other.m_service); - } + { + std::swap(m_service, other.m_service); + } io_work_scope& operator=(io_work_scope other) noexcept - { - swap(other); - return *this; - } + { + swap(other); + return *this; + } io_service& service() noexcept - { - return *m_service; - } + { + return *m_service; + } private: @@ -318,14 +318,14 @@ namespace cppcoro template cppcoro::io_service::timed_schedule_operation cppcoro::io_service::schedule_after( - const std::chrono::duration& duration, - cppcoro::cancellation_token cancellationToken) noexcept + const std::chrono::duration& duration, + cppcoro::cancellation_token cancellationToken) noexcept { return timed_schedule_operation{ *this, - std::chrono::high_resolution_clock::now() + duration, - std::move(cancellationToken) - }; + std::chrono::high_resolution_clock::now() + duration, + std::move(cancellationToken) + }; } #endif diff --git a/lib/io_service.cpp b/lib/io_service.cpp index 8a10f34c..a6380098 100644 --- a/lib/io_service.cpp +++ b/lib/io_service.cpp @@ -39,9 +39,9 @@ namespace throw std::system_error { static_cast(errorCode), - std::system_category(), - "Error creating io_service: CreateIoCompletionPort" - }; + std::system_category(), + "Error creating io_service: CreateIoCompletionPort" + }; } return cppcoro::detail::win32::safe_handle{ handle }; @@ -56,9 +56,9 @@ namespace throw std::system_error { static_cast(errorCode), - std::system_category(), - "Error creating manual reset event: CreateEventW" - }; + std::system_category(), + "Error creating manual reset event: CreateEventW" + }; } return cppcoro::detail::win32::safe_handle{ eventHandle }; @@ -74,8 +74,8 @@ namespace throw std::system_error { static_cast(errorCode), - std::system_category() - }; + std::system_category() + }; } return cppcoro::detail::win32::safe_handle{ handle }; @@ -111,11 +111,11 @@ class cppcoro::io_service::timer_queue void enqueue_timer(cppcoro::io_service::timed_schedule_operation* timer) noexcept; void dequeue_due_timers( - time_point currentTime, - cppcoro::io_service::timed_schedule_operation*& timerList) noexcept; + time_point currentTime, + cppcoro::io_service::timed_schedule_operation*& timerList) noexcept; void remove_cancelled_timers( - cppcoro::io_service::timed_schedule_operation*& timerList) noexcept; + cppcoro::io_service::timed_schedule_operation*& timerList) noexcept; private: @@ -124,16 +124,16 @@ class cppcoro::io_service::timer_queue timer_entry(cppcoro::io_service::timed_schedule_operation* timer) : m_dueTime(timer->m_resumeTime) , m_timer(timer) - {} + {} time_point m_dueTime; cppcoro::io_service::timed_schedule_operation* m_timer; }; static bool compare_entries(const timer_entry& a, const timer_entry& b) noexcept - { - return a.m_dueTime > b.m_dueTime; - } + { + return a.m_dueTime > b.m_dueTime; + } // A heap-sorted list of active timer entries // Earliest due timer is at the front of the queue @@ -148,8 +148,8 @@ class cppcoro::io_service::timer_queue }; cppcoro::io_service::timer_queue::timer_queue() noexcept - : m_timerEntries() - , m_overflowTimers(nullptr) +: m_timerEntries() + , m_overflowTimers(nullptr) {} cppcoro::io_service::timer_queue::~timer_queue() @@ -170,8 +170,8 @@ cppcoro::io_service::timer_queue::earliest_due_time() const noexcept if (m_overflowTimers != nullptr) { return std::min( - m_timerEntries.front().m_dueTime, - m_overflowTimers->m_resumeTime); + m_timerEntries.front().m_dueTime, + m_overflowTimers->m_resumeTime); } return m_timerEntries.front().m_dueTime; @@ -185,7 +185,7 @@ cppcoro::io_service::timer_queue::earliest_due_time() const noexcept } void cppcoro::io_service::timer_queue::enqueue_timer( - cppcoro::io_service::timed_schedule_operation* timer) noexcept + cppcoro::io_service::timed_schedule_operation* timer) noexcept { try { @@ -208,8 +208,8 @@ void cppcoro::io_service::timer_queue::enqueue_timer( } void cppcoro::io_service::timer_queue::dequeue_due_timers( - time_point currentTime, - cppcoro::io_service::timed_schedule_operation*& timerList) noexcept + time_point currentTime, + cppcoro::io_service::timed_schedule_operation*& timerList) noexcept { while (!m_timerEntries.empty() && m_timerEntries.front().m_dueTime <= currentTime) { @@ -231,26 +231,26 @@ void cppcoro::io_service::timer_queue::dequeue_due_timers( } void cppcoro::io_service::timer_queue::remove_cancelled_timers( - cppcoro::io_service::timed_schedule_operation*& timerList) noexcept + cppcoro::io_service::timed_schedule_operation*& timerList) noexcept { // Perform a linear scan of all timers looking for any that have // had cancellation requested. const auto addTimerToList = [&](timed_schedule_operation* timer) - { - timer->m_next = timerList; - timerList = timer; - }; + { + timer->m_next = timerList; + timerList = timer; + }; const auto isTimerCancelled = [](const timer_entry& entry) - { - return entry.m_timer->m_cancellationToken.is_cancellation_requested(); - }; + { + return entry.m_timer->m_cancellationToken.is_cancellation_requested(); + }; auto firstCancelledEntry = std::find_if( - m_timerEntries.begin(), - m_timerEntries.end(), - isTimerCancelled); + m_timerEntries.begin(), + m_timerEntries.end(), + isTimerCancelled); if (firstCancelledEntry != m_timerEntries.end()) { auto nonCancelledEnd = firstCancelledEntry; @@ -273,27 +273,27 @@ void cppcoro::io_service::timer_queue::remove_cancelled_timers( m_timerEntries.erase(nonCancelledEnd, m_timerEntries.end()); std::make_heap( - m_timerEntries.begin(), - m_timerEntries.end(), - compare_entries); + m_timerEntries.begin(), + m_timerEntries.end(), + compare_entries); } - { - timed_schedule_operation** current = &m_overflowTimers; - while ((*current) != nullptr) { - auto* timer = (*current); - if (timer->m_cancellationToken.is_cancellation_requested()) - { - *current = timer->m_next; - addTimerToList(timer); - } - else + timed_schedule_operation** current = &m_overflowTimers; + while ((*current) != nullptr) { - current = &timer->m_next; + auto* timer = (*current); + if (timer->m_cancellationToken.is_cancellation_requested()) + { + *current = timer->m_next; + addTimerToList(timer); + } + else + { + current = &timer->m_next; + } } } - } } class cppcoro::io_service::timer_thread_state @@ -342,10 +342,10 @@ cppcoro::io_service::io_service() cppcoro::io_service::io_service( #if CPPCORO_OS_WINNT - std::uint32_t concurrencyHint + std::uint32_t concurrencyHint #endif #if CPPCORO_OS_LINUX - size_t queue_length + size_t queue_length #endif ) : m_threadState(0) @@ -505,10 +505,10 @@ void cppcoro::io_service::queue_overflow_operation_to_tail(schedule_operation* o schedule_operation* head = nullptr; while (!m_scheduleOperations.compare_exchange_weak( - head, - operation, - std::memory_order_release, - std::memory_order_relaxed)) + head, + operation, + std::memory_order_release, + std::memory_order_relaxed)) { tail->m_next = head; } @@ -530,10 +530,10 @@ void cppcoro::io_service::queue_overflow_operation_to_head(schedule_operation* o { operation->m_next = head; } while (!m_scheduleOperations.compare_exchange_weak( - head, - operation, - std::memory_order_release, - std::memory_order_acquire)); + head, + operation, + std::memory_order_release, + std::memory_order_acquire)); } void cppcoro::io_service::schedule_impl(schedule_operation* operation) noexcept @@ -543,10 +543,10 @@ void cppcoro::io_service::schedule_impl(schedule_operation* operation) noexcept detail::linux::RESUME_TYPE); #elif CPPCORO_OS_WINNT const BOOL ok = ::PostQueuedCompletionStatus( - m_iocpHandle.handle(), - 0, - reinterpret_cast(operation->m_awaiter.address()), - nullptr); + m_iocpHandle.handle(), + 0, + reinterpret_cast(operation->m_awaiter.address()), + nullptr); #endif if (!ok) { @@ -562,10 +562,10 @@ void cppcoro::io_service::try_reschedule_overflow_operations() noexcept auto* next = operation->m_next; #if CPPCORO_OS_WINNT BOOL ok = ::PostQueuedCompletionStatus( - m_iocpHandle.handle(), - 0, - reinterpret_cast(operation->m_awaiter.address()), - nullptr); + m_iocpHandle.handle(), + 0, + reinterpret_cast(operation->m_awaiter.address()), + nullptr); #elif CPPCORO_OS_LINUX bool ok = m_mq->enqueue_message(reinterpret_cast(operation->m_awaiter.address()), detail::linux::RESUME_TYPE); @@ -590,9 +590,9 @@ bool cppcoro::io_service::try_enter_event_loop() noexcept return false; } } while (!m_threadState.compare_exchange_weak( - currentState, - currentState + active_thread_count_increment, - std::memory_order_relaxed)); + currentState, + currentState + active_thread_count_increment, + std::memory_order_relaxed)); return true; } @@ -626,8 +626,8 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) if(type == detail::linux::CALLBACK_TYPE) { auto* state = - static_cast(reinterpret_cast(message)); + static_cast(reinterpret_cast(message)); state->m_callback(state); @@ -638,7 +638,7 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) if((unsigned long long)message != 0) { std::experimental - ::coroutine_handle<>::from_address(reinterpret_cast(message)).resume(); + ::coroutine_handle<>::from_address(reinterpret_cast(message)).resume(); return true; } @@ -663,11 +663,11 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) ULONG_PTR completionKey = 0; LPOVERLAPPED overlapped = nullptr; BOOL ok = ::GetQueuedCompletionStatus( - m_iocpHandle.handle(), - &numberOfBytesTransferred, - &completionKey, - &overlapped, - timeout); + m_iocpHandle.handle(), + &numberOfBytesTransferred, + &completionKey, + &overlapped, + timeout); if (overlapped != nullptr) { DWORD errorCode = ok ? ERROR_SUCCESS : ::GetLastError(); @@ -677,10 +677,10 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) ::overlapped*>(overlapped)); state->m_callback( - state, - errorCode, - numberOfBytesTransferred, - completionKey); + state, + errorCode, + numberOfBytesTransferred, + completionKey); return true; } @@ -691,8 +691,8 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) // This was a coroutine scheduled via a call to // io_service::schedule(). std::experimental - ::coroutine_handle<> - ::from_address(reinterpret_cast(completionKey)).resume(); + ::coroutine_handle<> + ::from_address(reinterpret_cast(completionKey)).resume(); return true; } @@ -717,9 +717,9 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) throw std::system_error { static_cast(errorCode), - std::system_category(), - "Error retrieving item from io_service queue: GetQueuedCompletionStatus" - }; + std::system_category(), + "Error retrieving item from io_service queue: GetQueuedCompletionStatus" + }; } } #endif @@ -749,10 +749,10 @@ cppcoro::io_service::ensure_timer_thread_started() { auto newTimerState = std::make_unique(); if (m_timerState.compare_exchange_strong( - timerState, - newTimerState.get(), - std::memory_order_release, - std::memory_order_acquire)) + timerState, + newTimerState.get(), + std::memory_order_release, + std::memory_order_acquire)) { // We managed to install our timer_thread_state before some // other thread did, don't free it here - it will be freed in @@ -789,11 +789,11 @@ cppcoro::io_service::timer_thread_state::timer_thread_state() : throw std::system_error { static_cast(errno), - std::system_category(), - "Error creating io_service: epoll ctl wake ev" - }; + std::system_category(), + "Error creating io_service: epoll ctl wake ev" + }; } - + epoll_event timer_ev = {0}; timer_ev.events = EPOLLIN; timer_ev.data.fd = m_timerfd.fd(); @@ -803,9 +803,9 @@ cppcoro::io_service::timer_thread_state::timer_thread_state() : throw std::system_error { static_cast(errno), - std::system_category(), - "Error creating io_service: epoll ctl timer ev" - }; + std::system_category(), + "Error creating io_service: epoll ctl timer ev" + }; } #endif } @@ -820,7 +820,7 @@ cppcoro::io_service::timer_thread_state::~timer_thread_state() void cppcoro::io_service::timer_thread_state::request_timer_cancellation() noexcept { const bool wasTimerCancellationAlreadyRequested = - m_timerCancellationRequested.exchange(true, std::memory_order_release); + m_timerCancellationRequested.exchange(true, std::memory_order_release); if (!wasTimerCancellationAlreadyRequested) { wake_up_timer_thread(); @@ -837,10 +837,10 @@ void cppcoro::io_service::timer_thread_state::run() #if CPPCORO_OS_WINNT const DWORD waitHandleCount = 2; const HANDLE waitHandles[waitHandleCount] = - { - m_wakeUpEvent.handle(), - m_waitableTimerEvent.handle() - }; + { + m_wakeUpEvent.handle(), + m_waitableTimerEvent.handle() + }; #endif DWORD timeout = INFINITE; @@ -854,11 +854,11 @@ void cppcoro::io_service::timer_thread_state::run() bool timerEvent = false; #if CPPCORO_OS_WINNNT const DWORD waitResult = ::WaitForMultipleObjectsEx( - waitHandleCount, - waitHandles, - FALSE, // waitAll - timeout, - FALSE); // alertable + waitHandleCount, + waitHandles, + FALSE, // waitAll + timeout, + FALSE); // alertable if(waitResult == WAIT_OBJECT_0 || waitResult == WAIT_FAILED) { @@ -889,9 +889,9 @@ void cppcoro::io_service::timer_thread_state::run() throw std::system_error { static_cast(errno), - std::system_category(), - "Error in timer thread: epoll wait" - }; + std::system_category(), + "Error in timer thread: epoll wait" + }; } if(status == 0 || (status == 1 && ev.data.fd == m_wakeupfd.fd())) @@ -902,9 +902,9 @@ void cppcoro::io_service::timer_thread_state::run() throw std::system_error { static_cast(errno), - std::system_category(), - "Error in timer thread: eventfd read" - }; + std::system_category(), + "Error in timer thread: eventfd read" + }; } waitEvent = true; @@ -916,9 +916,9 @@ void cppcoro::io_service::timer_thread_state::run() throw std::system_error { static_cast(errno), - std::system_category(), - "Error in timer thread: timerfd read" - }; + std::system_category(), + "Error in timer thread: timerfd read" + }; } timerEvent = true; @@ -979,7 +979,7 @@ void cppcoro::io_service::timer_thread_state::run() #if CPPCORO_OS_WINNT LARGE_INTEGER dueTime; dueTime.QuadPart = -std::chrono::duration_cast - (timeUntilNextDueTime).count(); + (timeUntilNextDueTime).count(); // Period of 0 indicates no repeat on the timer. const LONG period = 0; @@ -989,23 +989,23 @@ void cppcoro::io_service::timer_thread_state::run() const BOOL resumeFromSuspend = FALSE; BOOL ok = ::SetWaitableTimer( - m_waitableTimerEvent.handle(), - &dueTime, - period, - nullptr, - nullptr, - resumeFromSuspend); + m_waitableTimerEvent.handle(), + &dueTime, + period, + nullptr, + nullptr, + resumeFromSuspend); #elif CPPCORO_OS_LINUX bool ok = false; itimerspec alarm_time = {0}; alarm_time.it_value.tv_sec = - std::chrono:: - duration_cast(timeUntilNextDueTime).count(); + std::chrono:: + duration_cast(timeUntilNextDueTime).count(); alarm_time.it_value.tv_nsec = - (std::chrono:: - duration_cast(timeUntilNextDueTime).count() % 10000000); + (std::chrono:: + duration_cast(timeUntilNextDueTime).count() % 10000000); if(alarm_time.it_value.tv_sec == 0 && alarm_time.it_value.tv_nsec == 0) { //linux timer of 0 time will not generate events @@ -1045,9 +1045,9 @@ void cppcoro::io_service::timer_thread_state::run() else if (timeUntilNextDueTime > 1ms) { timeout = static_cast - (std::chrono:: - duration_cast(timeUntilNextDueTime).count()); + (std::chrono:: + duration_cast(timeUntilNextDueTime).count()); } else { @@ -1071,7 +1071,7 @@ void cppcoro::io_service::timer_thread_state::run() if (timer->m_refCount.fetch_sub(1, std::memory_order_release) == 1) { timer->m_scheduleOperation.m_service.schedule_impl( - &timer->m_scheduleOperation); + &timer->m_scheduleOperation); } timersReadyToResume = nextTimer; @@ -1090,29 +1090,29 @@ void cppcoro::io_service::timer_thread_state::wake_up_timer_thread() noexcept } void cppcoro::io_service::schedule_operation::await_suspend( - std::experimental::coroutine_handle<> awaiter) noexcept + std::experimental::coroutine_handle<> awaiter) noexcept { m_awaiter = awaiter; m_service.schedule_impl(this); } cppcoro::io_service::timed_schedule_operation::timed_schedule_operation( - io_service& service, - std::chrono::high_resolution_clock::time_point resumeTime, - cppcoro::cancellation_token cancellationToken) noexcept - : m_scheduleOperation(service) - , m_resumeTime(resumeTime) - , m_cancellationToken(std::move(cancellationToken)) - , m_refCount(2) + io_service& service, + std::chrono::high_resolution_clock::time_point resumeTime, + cppcoro::cancellation_token cancellationToken) noexcept +: m_scheduleOperation(service) + , m_resumeTime(resumeTime) + , m_cancellationToken(std::move(cancellationToken)) + , m_refCount(2) { } cppcoro::io_service::timed_schedule_operation::timed_schedule_operation( - timed_schedule_operation&& other) noexcept - : m_scheduleOperation(std::move(other.m_scheduleOperation)) - , m_resumeTime(std::move(other.m_resumeTime)) - , m_cancellationToken(std::move(other.m_cancellationToken)) - , m_refCount(2) + timed_schedule_operation&& other) noexcept +: m_scheduleOperation(std::move(other.m_scheduleOperation)) + , m_resumeTime(std::move(other.m_resumeTime)) + , m_cancellationToken(std::move(other.m_cancellationToken)) + , m_refCount(2) { } @@ -1126,7 +1126,7 @@ bool cppcoro::io_service::timed_schedule_operation::await_ready() const noexcept } void cppcoro::io_service::timed_schedule_operation::await_suspend( - std::experimental::coroutine_handle<> awaiter) + std::experimental::coroutine_handle<> awaiter) { m_scheduleOperation.m_awaiter = awaiter; @@ -1170,10 +1170,10 @@ void cppcoro::io_service::timed_schedule_operation::await_suspend( { m_next = prev; } while (!timerState->m_newlyQueuedTimers.compare_exchange_weak( - prev, - this, - std::memory_order_release, - std::memory_order_acquire)); + prev, + this, + std::memory_order_release, + std::memory_order_acquire)); if (prev == nullptr) { From ab4b9b7e6406e6d8ddd6032aaae1d1828c0dae6f Mon Sep 17 00:00:00 2001 From: Anirudh Badam Date: Wed, 20 Jun 2018 15:48:27 -0700 Subject: [PATCH 30/34] indentation fixes. --- include/cppcoro/io_service.hpp | 50 +++++++++++++++++----------------- lib/io_service.cpp | 36 ++++++++++++------------ 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/include/cppcoro/io_service.hpp b/include/cppcoro/io_service.hpp index 3702f76b..027fcaea 100644 --- a/include/cppcoro/io_service.hpp +++ b/include/cppcoro/io_service.hpp @@ -199,7 +199,7 @@ namespace cppcoro schedule_operation(io_service& service) noexcept : m_service(service) - {} + {} bool await_ready() const noexcept { return false; } void await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept; @@ -260,48 +260,48 @@ namespace cppcoro explicit io_work_scope(io_service& service) noexcept : m_service(&service) - { - service.notify_work_started(); - } + { + service.notify_work_started(); + } io_work_scope(const io_work_scope& other) noexcept : m_service(other.m_service) + { + if (m_service != nullptr) { - if (m_service != nullptr) - { - m_service->notify_work_started(); - } + m_service->notify_work_started(); } + } io_work_scope(io_work_scope&& other) noexcept : m_service(other.m_service) - { - other.m_service = nullptr; - } + { + other.m_service = nullptr; + } ~io_work_scope() + { + if (m_service != nullptr) { - if (m_service != nullptr) - { - m_service->notify_work_finished(); - } + m_service->notify_work_finished(); } + } void swap(io_work_scope& other) noexcept - { - std::swap(m_service, other.m_service); - } + { + std::swap(m_service, other.m_service); + } io_work_scope& operator=(io_work_scope other) noexcept - { - swap(other); - return *this; - } + { + swap(other); + return *this; + } io_service& service() noexcept - { - return *m_service; - } + { + return *m_service; + } private: diff --git a/lib/io_service.cpp b/lib/io_service.cpp index a6380098..9b9f1e7b 100644 --- a/lib/io_service.cpp +++ b/lib/io_service.cpp @@ -124,16 +124,16 @@ class cppcoro::io_service::timer_queue timer_entry(cppcoro::io_service::timed_schedule_operation* timer) : m_dueTime(timer->m_resumeTime) , m_timer(timer) - {} + {} time_point m_dueTime; cppcoro::io_service::timed_schedule_operation* m_timer; }; static bool compare_entries(const timer_entry& a, const timer_entry& b) noexcept - { - return a.m_dueTime > b.m_dueTime; - } + { + return a.m_dueTime > b.m_dueTime; + } // A heap-sorted list of active timer entries // Earliest due timer is at the front of the queue @@ -278,22 +278,22 @@ void cppcoro::io_service::timer_queue::remove_cancelled_timers( compare_entries); } + { + timed_schedule_operation** current = &m_overflowTimers; + while ((*current) != nullptr) { - timed_schedule_operation** current = &m_overflowTimers; - while ((*current) != nullptr) + auto* timer = (*current); + if (timer->m_cancellationToken.is_cancellation_requested()) { - auto* timer = (*current); - if (timer->m_cancellationToken.is_cancellation_requested()) - { - *current = timer->m_next; - addTimerToList(timer); - } - else - { - current = &timer->m_next; - } + *current = timer->m_next; + addTimerToList(timer); + } + else + { + current = &timer->m_next; } } + } } class cppcoro::io_service::timer_thread_state @@ -502,7 +502,7 @@ void cppcoro::io_service::queue_overflow_operation_to_tail(schedule_operation* o { tail = tail->m_next; } - + schedule_operation* head = nullptr; while (!m_scheduleOperations.compare_exchange_weak( head, @@ -512,7 +512,7 @@ void cppcoro::io_service::queue_overflow_operation_to_tail(schedule_operation* o { tail->m_next = head; } - + return; } From d38a213b5a5a0d3394c423dfa01ffc9bd137d901 Mon Sep 17 00:00:00 2001 From: Anirudh Badam Date: Wed, 20 Jun 2018 15:56:58 -0700 Subject: [PATCH 31/34] more indentation changes. Moving from cc mode to llvm style. --- include/cppcoro/io_service.hpp | 2 +- lib/io_service.cpp | 164 +++++++++++++++++---------------- 2 files changed, 84 insertions(+), 82 deletions(-) diff --git a/include/cppcoro/io_service.hpp b/include/cppcoro/io_service.hpp index 027fcaea..2d7616bd 100644 --- a/include/cppcoro/io_service.hpp +++ b/include/cppcoro/io_service.hpp @@ -305,7 +305,7 @@ namespace cppcoro private: - io_service* m_service; + io_service * m_service; }; diff --git a/lib/io_service.cpp b/lib/io_service.cpp index 9b9f1e7b..d7e6004d 100644 --- a/lib/io_service.cpp +++ b/lib/io_service.cpp @@ -148,8 +148,8 @@ class cppcoro::io_service::timer_queue }; cppcoro::io_service::timer_queue::timer_queue() noexcept -: m_timerEntries() - , m_overflowTimers(nullptr) + : m_timerEntries() + , m_overflowTimers(nullptr) {} cppcoro::io_service::timer_queue::~timer_queue() @@ -257,8 +257,8 @@ void cppcoro::io_service::timer_queue::remove_cancelled_timers( addTimerToList(nonCancelledEnd->m_timer); for (auto iter = firstCancelledEntry + 1; - iter != m_timerEntries.end(); - ++iter) + iter != m_timerEntries.end(); + ++iter) { if (isTimerCancelled(*iter)) { @@ -335,7 +335,7 @@ cppcoro::io_service::io_service() : io_service(0) #endif #if CPPCORO_OS_LINUX - : io_service(10) //queue size of message queue must be at least 10 + : io_service(10) //queue size of message queue must be at least 10 #endif { } @@ -347,7 +347,7 @@ cppcoro::io_service::io_service( #if CPPCORO_OS_LINUX size_t queue_length #endif - ) +) : m_threadState(0) , m_workCount(0) #if CPPCORO_OS_WINNT @@ -452,8 +452,8 @@ void cppcoro::io_service::stop() noexcept if ((oldState & stop_requested_flag) == 0) { for (auto activeThreadCount = oldState / active_thread_count_increment; - activeThreadCount > 0; - --activeThreadCount) + activeThreadCount > 0; + --activeThreadCount) { post_wake_up_event(); } @@ -505,10 +505,10 @@ void cppcoro::io_service::queue_overflow_operation_to_tail(schedule_operation* o schedule_operation* head = nullptr; while (!m_scheduleOperations.compare_exchange_weak( - head, - operation, - std::memory_order_release, - std::memory_order_relaxed)) + head, + operation, + std::memory_order_release, + std::memory_order_relaxed)) { tail->m_next = head; } @@ -530,17 +530,17 @@ void cppcoro::io_service::queue_overflow_operation_to_head(schedule_operation* o { operation->m_next = head; } while (!m_scheduleOperations.compare_exchange_weak( - head, - operation, - std::memory_order_release, - std::memory_order_acquire)); + head, + operation, + std::memory_order_release, + std::memory_order_acquire)); } void cppcoro::io_service::schedule_impl(schedule_operation* operation) noexcept { #if CPPCORO_OS_LINUX const bool ok = m_mq->enqueue_message(reinterpret_cast(operation->m_awaiter.address()), - detail::linux::RESUME_TYPE); + detail::linux::RESUME_TYPE); #elif CPPCORO_OS_WINNT const BOOL ok = ::PostQueuedCompletionStatus( m_iocpHandle.handle(), @@ -568,10 +568,10 @@ void cppcoro::io_service::try_reschedule_overflow_operations() noexcept nullptr); #elif CPPCORO_OS_LINUX bool ok = m_mq->enqueue_message(reinterpret_cast(operation->m_awaiter.address()), - detail::linux::RESUME_TYPE); + detail::linux::RESUME_TYPE); #endif - if(!ok) + if (!ok) { queue_overflow_operation_to_tail(operation); } @@ -590,9 +590,9 @@ bool cppcoro::io_service::try_enter_event_loop() noexcept return false; } } while (!m_threadState.compare_exchange_weak( - currentState, - currentState + active_thread_count_increment, - std::memory_order_relaxed)); + currentState, + currentState + active_thread_count_increment, + std::memory_order_relaxed)); return true; } @@ -604,13 +604,13 @@ void cppcoro::io_service::exit_event_loop() noexcept bool cppcoro::io_service::try_process_one_event(bool waitForEvent) { - if(is_stop_requested()) + if (is_stop_requested()) { return false; } #if CPPCORO_OS_LINUX - while(true) + while (true) { try_reschedule_overflow_operations(); void* message = NULL; @@ -618,16 +618,16 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) bool status = m_mq->dequeue_message(message, type, waitForEvent); - if(!status) + if (!status) { return false; } - if(type == detail::linux::CALLBACK_TYPE) + if (type == detail::linux::CALLBACK_TYPE) { auto* state = - static_cast(reinterpret_cast(message)); + static_cast(reinterpret_cast(message)); state->m_callback(state); @@ -635,10 +635,10 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) } else { - if((unsigned long long)message != 0) + if ((unsigned long long)message != 0) { std::experimental - ::coroutine_handle<>::from_address(reinterpret_cast(message)).resume(); + ::coroutine_handle<>::from_address(reinterpret_cast(message)).resume(); return true; } @@ -673,8 +673,8 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) DWORD errorCode = ok ? ERROR_SUCCESS : ::GetLastError(); auto* state = static_cast(reinterpret_cast(overlapped)); + ::io_state*>(reinterpret_cast(overlapped)); state->m_callback( state, @@ -691,8 +691,8 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) // This was a coroutine scheduled via a call to // io_service::schedule(). std::experimental - ::coroutine_handle<> - ::from_address(reinterpret_cast(completionKey)).resume(); + ::coroutine_handle<> + ::from_address(reinterpret_cast(completionKey)).resume(); return true; } @@ -749,10 +749,10 @@ cppcoro::io_service::ensure_timer_thread_started() { auto newTimerState = std::make_unique(); if (m_timerState.compare_exchange_strong( - timerState, - newTimerState.get(), - std::memory_order_release, - std::memory_order_acquire)) + timerState, + newTimerState.get(), + std::memory_order_release, + std::memory_order_acquire)) { // We managed to install our timer_thread_state before some // other thread did, don't free it here - it will be freed in @@ -780,11 +780,11 @@ cppcoro::io_service::timer_thread_state::timer_thread_state() : , m_thread([this] { this->run(); }) { #if CPPCORO_OS_LINUX - epoll_event wake_ev = {0}; + epoll_event wake_ev = { 0 }; wake_ev.events = EPOLLIN; wake_ev.data.fd = m_wakeupfd.fd(); - if(epoll_ctl(m_epollfd.fd(), EPOLL_CTL_ADD, m_wakeupfd.fd(), &wake_ev) == -1) + if (epoll_ctl(m_epollfd.fd(), EPOLL_CTL_ADD, m_wakeupfd.fd(), &wake_ev) == -1) { throw std::system_error { @@ -794,11 +794,11 @@ cppcoro::io_service::timer_thread_state::timer_thread_state() : }; } - epoll_event timer_ev = {0}; + epoll_event timer_ev = { 0 }; timer_ev.events = EPOLLIN; timer_ev.data.fd = m_timerfd.fd(); - if(epoll_ctl(m_epollfd.fd(), EPOLL_CTL_ADD, m_timerfd.fd(), &timer_ev) == -1) + if (epoll_ctl(m_epollfd.fd(), EPOLL_CTL_ADD, m_timerfd.fd(), &timer_ev) == -1) { throw std::system_error { @@ -820,7 +820,7 @@ cppcoro::io_service::timer_thread_state::~timer_thread_state() void cppcoro::io_service::timer_thread_state::request_timer_cancellation() noexcept { const bool wasTimerCancellationAlreadyRequested = - m_timerCancellationRequested.exchange(true, std::memory_order_release); + m_timerCancellationRequested.exchange(true, std::memory_order_release); if (!wasTimerCancellationAlreadyRequested) { wake_up_timer_thread(); @@ -860,7 +860,7 @@ void cppcoro::io_service::timer_thread_state::run() timeout, FALSE); // alertable - if(waitResult == WAIT_OBJECT_0 || waitResult == WAIT_FAILED) + if (waitResult == WAIT_OBJECT_0 || waitResult == WAIT_FAILED) { // Wake-up event (WAIT_OBJECT_0) // @@ -876,7 +876,7 @@ void cppcoro::io_service::timer_thread_state::run() waitEvent = true; } - else if(waitResult == WAIT_OBJECT_0 + 1) + else if (waitResult == WAIT_OBJECT_0 + 1) { timerEvent = true; } @@ -884,7 +884,7 @@ void cppcoro::io_service::timer_thread_state::run() epoll_event ev; const int status = epoll_wait(m_epollfd.fd(), &ev, 1, timeout); - if(status == -1) + if (status == -1) { throw std::system_error { @@ -894,10 +894,10 @@ void cppcoro::io_service::timer_thread_state::run() }; } - if(status == 0 || (status == 1 && ev.data.fd == m_wakeupfd.fd())) + if (status == 0 || (status == 1 && ev.data.fd == m_wakeupfd.fd())) { uint64_t count; - if(read(m_wakeupfd.fd(), &count, sizeof(uint64_t)) != sizeof(uint64_t)) + if (read(m_wakeupfd.fd(), &count, sizeof(uint64_t)) != sizeof(uint64_t)) { throw std::system_error { @@ -908,10 +908,11 @@ void cppcoro::io_service::timer_thread_state::run() } waitEvent = true; - } else if (status == 1 && ev.data.fd == m_timerfd.fd()) + } + else if (status == 1 && ev.data.fd == m_timerfd.fd()) { uint64_t count; - if(read(m_timerfd.fd(), &count, sizeof(uint64_t)) != sizeof(uint64_t)) + if (read(m_timerfd.fd(), &count, sizeof(uint64_t)) != sizeof(uint64_t)) { throw std::system_error { @@ -925,7 +926,7 @@ void cppcoro::io_service::timer_thread_state::run() } #endif - if(waitEvent) + if (waitEvent) { if (m_timerCancellationRequested.exchange(false, std::memory_order_acquire)) { @@ -949,7 +950,8 @@ void cppcoro::io_service::timer_thread_state::run() timerQueue.enqueue_timer(timer); } } - } else if (timerEvent) + } + else if (timerEvent) { lastSetWaitEventTime = time_point::max(); } @@ -971,7 +973,7 @@ void cppcoro::io_service::timer_thread_state::run() if (earliestDueTime != lastSetWaitEventTime) { using ticks = std::chrono::duration>; + std::ratio<1, 10'000'000>>; auto timeUntilNextDueTime = earliestDueTime - currentTime; @@ -979,7 +981,7 @@ void cppcoro::io_service::timer_thread_state::run() #if CPPCORO_OS_WINNT LARGE_INTEGER dueTime; dueTime.QuadPart = -std::chrono::duration_cast - (timeUntilNextDueTime).count(); + (timeUntilNextDueTime).count(); // Period of 0 indicates no repeat on the timer. const LONG period = 0; @@ -997,23 +999,23 @@ void cppcoro::io_service::timer_thread_state::run() resumeFromSuspend); #elif CPPCORO_OS_LINUX bool ok = false; - itimerspec alarm_time = {0}; + itimerspec alarm_time = { 0 }; alarm_time.it_value.tv_sec = - std::chrono:: - duration_cast(timeUntilNextDueTime).count(); + std::chrono:: + duration_cast(timeUntilNextDueTime).count(); alarm_time.it_value.tv_nsec = - (std::chrono:: - duration_cast(timeUntilNextDueTime).count() % 10000000); - if(alarm_time.it_value.tv_sec == 0 && alarm_time.it_value.tv_nsec == 0) + (std::chrono:: + duration_cast(timeUntilNextDueTime).count() % 10000000); + if (alarm_time.it_value.tv_sec == 0 && alarm_time.it_value.tv_nsec == 0) { //linux timer of 0 time will not generate events //so let's set it to 1 nsec alarm_time.it_value.tv_nsec = 1; } - if(timerfd_settime(m_timerfd.fd(), 0, &alarm_time, NULL) == -1) + if (timerfd_settime(m_timerfd.fd(), 0, &alarm_time, NULL) == -1) { ok = false; } @@ -1045,9 +1047,9 @@ void cppcoro::io_service::timer_thread_state::run() else if (timeUntilNextDueTime > 1ms) { timeout = static_cast - (std::chrono:: - duration_cast(timeUntilNextDueTime).count()); + (std::chrono:: + duration_cast(timeUntilNextDueTime).count()); } else { @@ -1100,19 +1102,19 @@ cppcoro::io_service::timed_schedule_operation::timed_schedule_operation( io_service& service, std::chrono::high_resolution_clock::time_point resumeTime, cppcoro::cancellation_token cancellationToken) noexcept -: m_scheduleOperation(service) - , m_resumeTime(resumeTime) - , m_cancellationToken(std::move(cancellationToken)) - , m_refCount(2) + : m_scheduleOperation(service) + , m_resumeTime(resumeTime) + , m_cancellationToken(std::move(cancellationToken)) + , m_refCount(2) { } cppcoro::io_service::timed_schedule_operation::timed_schedule_operation( timed_schedule_operation&& other) noexcept -: m_scheduleOperation(std::move(other.m_scheduleOperation)) - , m_resumeTime(std::move(other.m_resumeTime)) - , m_cancellationToken(std::move(other.m_cancellationToken)) - , m_refCount(2) + : m_scheduleOperation(std::move(other.m_scheduleOperation)) + , m_resumeTime(std::move(other.m_resumeTime)) + , m_cancellationToken(std::move(other.m_cancellationToken)) + , m_refCount(2) { } @@ -1138,9 +1140,9 @@ void cppcoro::io_service::timed_schedule_operation::await_suspend( if (m_cancellationToken.can_be_cancelled()) { m_cancellationRegistration.emplace(m_cancellationToken, [timerState] - { - timerState->request_timer_cancellation(); - }); + { + timerState->request_timer_cancellation(); + }); } // Queue the timer schedule to the queue of incoming new timers. @@ -1170,10 +1172,10 @@ void cppcoro::io_service::timed_schedule_operation::await_suspend( { m_next = prev; } while (!timerState->m_newlyQueuedTimers.compare_exchange_weak( - prev, - this, - std::memory_order_release, - std::memory_order_acquire)); + prev, + this, + std::memory_order_release, + std::memory_order_acquire)); if (prev == nullptr) { From 169693630250bdc170210f4025ac3a82b784b027 Mon Sep 17 00:00:00 2001 From: Anirudh Badam Date: Wed, 20 Jun 2018 16:43:55 -0700 Subject: [PATCH 32/34] reverting some changes since the functions are no longer needed. --- include/cppcoro/io_service.hpp | 2 +- lib/io_service.cpp | 81 ++++++++++++++-------------------- 2 files changed, 34 insertions(+), 49 deletions(-) diff --git a/include/cppcoro/io_service.hpp b/include/cppcoro/io_service.hpp index 2d7616bd..027fcaea 100644 --- a/include/cppcoro/io_service.hpp +++ b/include/cppcoro/io_service.hpp @@ -305,7 +305,7 @@ namespace cppcoro private: - io_service * m_service; + io_service* m_service; }; diff --git a/lib/io_service.cpp b/lib/io_service.cpp index d7e6004d..afb4ed5d 100644 --- a/lib/io_service.cpp +++ b/lib/io_service.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #if CPPCORO_OS_WINNT # ifndef WIN32_LEAN_AND_MEAN @@ -493,49 +492,6 @@ cppcoro::detail::win32::handle_t cppcoro::io_service::native_iocp_handle() noexc } #endif -void cppcoro::io_service::queue_overflow_operation_to_tail(schedule_operation* operation) noexcept -{ - // Still unable to queue these operations. - // Put them back on the list of overflow operations. - auto* tail = operation; - while (tail->m_next != nullptr) - { - tail = tail->m_next; - } - - schedule_operation* head = nullptr; - while (!m_scheduleOperations.compare_exchange_weak( - head, - operation, - std::memory_order_release, - std::memory_order_relaxed)) - { - tail->m_next = head; - } - - return; -} - -void cppcoro::io_service::queue_overflow_operation_to_head(schedule_operation* operation) noexcept -{ - // Failed to post to the I/O completion port. - // - // This is most-likely because the queue is currently full. - // - // We'll queue up the operation to a linked-list using a lock-free - // push and defer the dispatch to the completion port until some I/O - // thread next enters its event loop. - auto* head = m_scheduleOperations.load(std::memory_order_acquire); - do - { - operation->m_next = head; - } while (!m_scheduleOperations.compare_exchange_weak( - head, - operation, - std::memory_order_release, - std::memory_order_acquire)); -} - void cppcoro::io_service::schedule_impl(schedule_operation* operation) noexcept { #if CPPCORO_OS_LINUX @@ -550,7 +506,22 @@ void cppcoro::io_service::schedule_impl(schedule_operation* operation) noexcept #endif if (!ok) { - queue_overflow_operation_to_head(operation); + // Failed to post to the I/O completion port. + // + // This is most-likely because the queue is currently full. + // + // We'll queue up the operation to a linked-list using a lock-free + // push and defer the dispatch to the completion port until some I/O + // thread next enters its event loop. + auto* head = m_scheduleOperations.load(std::memory_order_acquire); + do + { + operation->m_next = head; + } while (!m_scheduleOperations.compare_exchange_weak( + head, + operation, + std::memory_order_release, + std::memory_order_acquire)); } } @@ -573,7 +544,23 @@ void cppcoro::io_service::try_reschedule_overflow_operations() noexcept if (!ok) { - queue_overflow_operation_to_tail(operation); + // Still unable to queue these operations. + // Put them back on the list of overflow operations. + auto* tail = operation; + while (tail->m_next != nullptr) + { + tail = tail->m_next; + } + + schedule_operation* head = nullptr; + while (!m_scheduleOperations.compare_exchange_weak( + head, + operation, + std::memory_order_release, + std::memory_order_relaxed)) + { + tail->m_next = head; + } } operation = next; @@ -1023,8 +1010,6 @@ void cppcoro::io_service::timer_thread_state::run() { ok = true; } - - lastSetWaitEventTime = earliestDueTime; #endif if (ok) { From 751232b4cd7bf25c303e9309f04d4b1bfaaaa6bf Mon Sep 17 00:00:00 2001 From: Anirudh Badam Date: Thu, 21 Jun 2018 12:26:46 -0700 Subject: [PATCH 33/34] windows tests passing. --- lib/io_service.cpp | 189 +++++++++++++++++++++------------------------ 1 file changed, 88 insertions(+), 101 deletions(-) diff --git a/lib/io_service.cpp b/lib/io_service.cpp index afb4ed5d..662aa53a 100644 --- a/lib/io_service.cpp +++ b/lib/io_service.cpp @@ -24,6 +24,7 @@ #if CPPCORO_OS_LINUX typedef int DWORD; #define INFINITE (DWORD)-1 //needed for timeout values in io_service::timer_thread_state::run() +typedef long long int LONGLONG #endif namespace @@ -307,7 +308,7 @@ class cppcoro::io_service::timer_thread_state void request_timer_cancellation() noexcept; - void run(); + void run() noexcept; void wake_up_timer_thread() noexcept; @@ -329,12 +330,13 @@ class cppcoro::io_service::timer_thread_state std::thread m_thread; }; + + cppcoro::io_service::io_service() #if CPPCORO_OS_WINNT : io_service(0) -#endif -#if CPPCORO_OS_LINUX - : io_service(10) //queue size of message queue must be at least 10 +#elif CPPCORO_OS_LINUX + : io_service(10) #endif { } @@ -342,17 +344,15 @@ cppcoro::io_service::io_service() cppcoro::io_service::io_service( #if CPPCORO_OS_WINNT std::uint32_t concurrencyHint -#endif -#if CPPCORO_OS_LINUX - size_t queue_length +#elif CPPCORO_OS_LINUX + std::uint32_t queue_length #endif ) : m_threadState(0) , m_workCount(0) #if CPPCORO_OS_WINNT , m_iocpHandle(create_io_completion_port(concurrencyHint)) -#endif -#if CPPCORO_OS_LINUX +#elif CPPCORO_OS_LINUX , m_mq(new detail::linux::message_queue(queue_length)) #endif , m_scheduleOperations(nullptr) @@ -485,24 +485,22 @@ void cppcoro::io_service::notify_work_finished() noexcept } } -#if CPPCORO_OS_WINNT cppcoro::detail::win32::handle_t cppcoro::io_service::native_iocp_handle() noexcept { return m_iocpHandle.handle(); } -#endif void cppcoro::io_service::schedule_impl(schedule_operation* operation) noexcept { -#if CPPCORO_OS_LINUX - const bool ok = m_mq->enqueue_message(reinterpret_cast(operation->m_awaiter.address()), - detail::linux::RESUME_TYPE); -#elif CPPCORO_OS_WINNT +#if CPPCORO_OS_WINNT const BOOL ok = ::PostQueuedCompletionStatus( m_iocpHandle.handle(), 0, reinterpret_cast(operation->m_awaiter.address()), nullptr); +#elif CPPCORO_OS_LINUX + const bool ok = m_mq->enqueue_message(reinterpret_cast(operation->m_awaiter.address()), + detail::linux::RESUME_TYPE); #endif if (!ok) { @@ -541,7 +539,6 @@ void cppcoro::io_service::try_reschedule_overflow_operations() noexcept bool ok = m_mq->enqueue_message(reinterpret_cast(operation->m_awaiter.address()), detail::linux::RESUME_TYPE); #endif - if (!ok) { // Still unable to queue these operations. @@ -561,6 +558,8 @@ void cppcoro::io_service::try_reschedule_overflow_operations() noexcept { tail->m_next = head; } + + return; } operation = next; @@ -596,47 +595,6 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) return false; } -#if CPPCORO_OS_LINUX - while (true) - { - try_reschedule_overflow_operations(); - void* message = NULL; - detail::linux::message_type type = detail::linux::RESUME_TYPE; - - bool status = m_mq->dequeue_message(message, type, waitForEvent); - - if (!status) - { - return false; - } - - if (type == detail::linux::CALLBACK_TYPE) - { - auto* state = - static_cast(reinterpret_cast(message)); - - state->m_callback(state); - - return true; - } - else - { - if ((unsigned long long)message != 0) - { - std::experimental - ::coroutine_handle<>::from_address(reinterpret_cast(message)).resume(); - return true; - } - - if (is_stop_requested()) - { - return false; - } - } - } -#endif - #if CPPCORO_OS_WINNT const DWORD timeout = waitForEvent ? INFINITE : 0; @@ -659,9 +617,8 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) { DWORD errorCode = ok ? ERROR_SUCCESS : ::GetLastError(); - auto* state = static_cast(reinterpret_cast(overlapped)); + auto* state = static_cast( + reinterpret_cast(overlapped)); state->m_callback( state, @@ -677,9 +634,8 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) { // This was a coroutine scheduled via a call to // io_service::schedule(). - std::experimental - ::coroutine_handle<> - ::from_address(reinterpret_cast(completionKey)).resume(); + std::experimental::coroutine_handle<>::from_address( + reinterpret_cast(completionKey)).resume(); return true; } @@ -709,6 +665,45 @@ bool cppcoro::io_service::try_process_one_event(bool waitForEvent) }; } } +#elif CPPCORO_OS_LINUX + while (true) + { + try_reschedule_overflow_operations(); + void* message = NULL; + detail::linux::message_type type = detail::linux::RESUME_TYPE; + + bool status = m_mq->dequeue_message(message, type, waitForEvent); + + if (!status) + { + return false; + } + + if (type == detail::linux::CALLBACK_TYPE) + { + auto* state = + static_cast(reinterpret_cast(message)); + + state->m_callback(state); + + return true; + } + else + { + if ((unsigned long long)message != 0) + { + std::experimental + ::coroutine_handle<>::from_address(reinterpret_cast(message)).resume(); + return true; + } + + if (is_stop_requested()) + { + return false; + } + } + } #endif } @@ -721,9 +716,7 @@ void cppcoro::io_service::post_wake_up_event() noexcept // and the system is out of memory. In this case threads should find other events // in the queue next time they check anyway and thus wake-up. (void)::PostQueuedCompletionStatus(m_iocpHandle.handle(), 0, 0, nullptr); -#endif - -#if CPPCORO_OS_LINUX +#elif CPPCORO_OS_LINUX (void)m_mq->enqueue_message(NULL, detail::linux::RESUME_TYPE); #endif } @@ -751,17 +744,16 @@ cppcoro::io_service::ensure_timer_thread_started() return timerState; } -cppcoro::io_service::timer_thread_state::timer_thread_state() : +cppcoro::io_service::timer_thread_state::timer_thread_state() #if CPPCORO_OS_WINNT - m_wakeUpEvent(create_auto_reset_event()) - , m_waitableTimerEvent(create_waitable_timer_event()), -#endif -#if CPPCORO_OS_LINUX - m_wakeupfd(detail::linux::create_event_fd()) + : m_wakeUpEvent(create_auto_reset_event()) + , m_waitableTimerEvent(create_waitable_timer_event()) +#elif CPPCORO_OS_LINUX + :m_wakeupfd(detail::linux::create_event_fd()) , m_timerfd(detail::linux::create_timer_fd()) , m_epollfd(detail::linux::create_epoll_fd()), #endif - m_newlyQueuedTimers(nullptr) + , m_newlyQueuedTimers(nullptr) , m_timerCancellationRequested(false) , m_shutDownRequested(false) , m_thread([this] { this->run(); }) @@ -814,7 +806,7 @@ void cppcoro::io_service::timer_thread_state::request_timer_cancellation() noexc } } -void cppcoro::io_service::timer_thread_state::run() +void cppcoro::io_service::timer_thread_state::run() noexcept { using clock = std::chrono::high_resolution_clock; using time_point = clock::time_point; @@ -829,17 +821,16 @@ void cppcoro::io_service::timer_thread_state::run() m_waitableTimerEvent.handle() }; #endif - DWORD timeout = INFINITE; - time_point lastSetWaitEventTime = time_point::max(); timed_schedule_operation* timersReadyToResume = nullptr; + DWORD timeout = INFINITE; while (!m_shutDownRequested.load(std::memory_order_relaxed)) { bool waitEvent = false; bool timerEvent = false; -#if CPPCORO_OS_WINNNT +#if CPPCORO_OS_WINNT const DWORD waitResult = ::WaitForMultipleObjectsEx( waitHandleCount, waitHandles, @@ -849,18 +840,6 @@ void cppcoro::io_service::timer_thread_state::run() if (waitResult == WAIT_OBJECT_0 || waitResult == WAIT_FAILED) { - // Wake-up event (WAIT_OBJECT_0) - // - // We are only woken up for: - // - handling timer cancellation - // - handling newly queued timers - // - shutdown - // - // We also handle WAIT_FAILED here so that we remain responsive - // to new timers and cancellation even if the OS fails to perform - // the wait operation for some reason. - // Handle cancelled timers - waitEvent = true; } else if (waitResult == WAIT_OBJECT_0 + 1) @@ -912,9 +891,20 @@ void cppcoro::io_service::timer_thread_state::run() timerEvent = true; } #endif - if (waitEvent) { + // Wake-up event (WAIT_OBJECT_0) + // + // We are only woken up for: + // - handling timer cancellation + // - handling newly queued timers + // - shutdown + // + // We also handle WAIT_FAILED here so that we remain responsive + // to new timers and cancellation even if the OS fails to perform + // the wait operation for some reason. + + // Handle cancelled timers if (m_timerCancellationRequested.exchange(false, std::memory_order_acquire)) { timerQueue.remove_cancelled_timers(timersReadyToResume); @@ -959,16 +949,14 @@ void cppcoro::io_service::timer_thread_state::run() // amount of time to wait until the next timer is ready. if (earliestDueTime != lastSetWaitEventTime) { - using ticks = std::chrono::duration>; + using ticks = std::chrono::duration>; auto timeUntilNextDueTime = earliestDueTime - currentTime; // Negative value indicates relative time. #if CPPCORO_OS_WINNT LARGE_INTEGER dueTime; - dueTime.QuadPart = -std::chrono::duration_cast - (timeUntilNextDueTime).count(); + dueTime.QuadPart = -std::chrono::duration_cast(timeUntilNextDueTime).count(); // Period of 0 indicates no repeat on the timer. const LONG period = 0; @@ -977,7 +965,7 @@ void cppcoro::io_service::timer_thread_state::run() // raise the timer event. const BOOL resumeFromSuspend = FALSE; - BOOL ok = ::SetWaitableTimer( + const BOOL ok = ::SetWaitableTimer( m_waitableTimerEvent.handle(), &dueTime, period, @@ -1018,9 +1006,9 @@ void cppcoro::io_service::timer_thread_state::run() } else { - // Not sure what could cause the call to waitable timer + // Not sure what could cause the call to SetWaitableTimer() // to fail here but we'll just try falling back to using - // the timeout parameter of the WaitFor or epoll call. + // the timeout parameter of the WaitForMultipleObjects() call. // // wake-up at least once every second and retry setting // the timer at that point. @@ -1031,10 +1019,9 @@ void cppcoro::io_service::timer_thread_state::run() } else if (timeUntilNextDueTime > 1ms) { - timeout = static_cast - (std::chrono:: - duration_cast(timeUntilNextDueTime).count()); + timeout = static_cast( + std::chrono::duration_cast( + timeUntilNextDueTime).count()); } else { From f870053ba0bd74960b70b1690568d1cb9dfe0498 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 21 Jun 2018 19:50:15 +0000 Subject: [PATCH 34/34] All linux tests passing. --- include/cppcoro/io_service.hpp | 4 ++-- lib/io_service.cpp | 24 ++++++++---------------- tools/cake | 2 +- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/include/cppcoro/io_service.hpp b/include/cppcoro/io_service.hpp index 027fcaea..9f100a80 100644 --- a/include/cppcoro/io_service.hpp +++ b/include/cppcoro/io_service.hpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace cppcoro { @@ -48,8 +49,7 @@ namespace cppcoro /// above this number. #if CPPCORO_OS_WINNT io_service(std::uint32_t concurrencyHint); -#endif -#if CPPCORO_OS_LINUX +#elif CPPCORO_OS_LINUX io_service(size_t queue_length); #endif ~io_service(); diff --git a/lib/io_service.cpp b/lib/io_service.cpp index 662aa53a..d08f74d2 100644 --- a/lib/io_service.cpp +++ b/lib/io_service.cpp @@ -24,7 +24,7 @@ #if CPPCORO_OS_LINUX typedef int DWORD; #define INFINITE (DWORD)-1 //needed for timeout values in io_service::timer_thread_state::run() -typedef long long int LONGLONG +typedef long long int LONGLONG; #endif namespace @@ -308,7 +308,7 @@ class cppcoro::io_service::timer_thread_state void request_timer_cancellation() noexcept; - void run() noexcept; + void run(); void wake_up_timer_thread() noexcept; @@ -345,7 +345,7 @@ cppcoro::io_service::io_service( #if CPPCORO_OS_WINNT std::uint32_t concurrencyHint #elif CPPCORO_OS_LINUX - std::uint32_t queue_length + size_t queue_length #endif ) : m_threadState(0) @@ -485,10 +485,12 @@ void cppcoro::io_service::notify_work_finished() noexcept } } +#if CPPCORO_OS_WINNT cppcoro::detail::win32::handle_t cppcoro::io_service::native_iocp_handle() noexcept { return m_iocpHandle.handle(); } +#endif void cppcoro::io_service::schedule_impl(schedule_operation* operation) noexcept { @@ -751,7 +753,7 @@ cppcoro::io_service::timer_thread_state::timer_thread_state() #elif CPPCORO_OS_LINUX :m_wakeupfd(detail::linux::create_event_fd()) , m_timerfd(detail::linux::create_timer_fd()) - , m_epollfd(detail::linux::create_epoll_fd()), + , m_epollfd(detail::linux::create_epoll_fd()) #endif , m_newlyQueuedTimers(nullptr) , m_timerCancellationRequested(false) @@ -806,7 +808,7 @@ void cppcoro::io_service::timer_thread_state::request_timer_cancellation() noexc } } -void cppcoro::io_service::timer_thread_state::run() noexcept +void cppcoro::io_service::timer_thread_state::run() { using clock = std::chrono::high_resolution_clock; using time_point = clock::time_point; @@ -850,17 +852,7 @@ void cppcoro::io_service::timer_thread_state::run() noexcept epoll_event ev; const int status = epoll_wait(m_epollfd.fd(), &ev, 1, timeout); - if (status == -1) - { - throw std::system_error - { - static_cast(errno), - std::system_category(), - "Error in timer thread: epoll wait" - }; - } - - if (status == 0 || (status == 1 && ev.data.fd == m_wakeupfd.fd())) + if (status == 0 || status == -1 || (status == 1 && ev.data.fd == m_wakeupfd.fd())) { uint64_t count; if (read(m_wakeupfd.fd(), &count, sizeof(uint64_t)) != sizeof(uint64_t)) diff --git a/tools/cake b/tools/cake index 429a0f9d..db43f336 160000 --- a/tools/cake +++ b/tools/cake @@ -1 +1 @@ -Subproject commit 429a0f9d5d13257b29fd445e0d2391268fe97d45 +Subproject commit db43f336b95d367163b967491f4e22d252ae59b4