- thread_state::m_mask was being incorrectly initialised
- Modified try_local_enqueue() to attempt to enlarge the local
queue if it looks like it's run out of space.
- Removed doubly-linked list from schedule_operation as it wasn't
needed.
- Catch any potential exceptions thrown by auto_reset_event::wait()
and just sleep for 1ms in this case rather than terminating the
thread.
Factors out implementation of calls to OS that was duplicated in
both cancellable and non-cancellable operation objects into an
'impl' class that can be used by both.
Also, fixed potential data race in some try_start() methods when
trying to read the m_skipCompletionOnSuccess member of a socket
after starting the I/O operation. Now ensure that this flag is
read before starting the operation.
Simplified logic for file_[read/write]_operation slightly by
passing out-parameter to receive number of bytes read/written
if operation completes synchronously. This avoids an extra call
to GetOverlappedResult() to retrieve this information.
- Adds cppcoro::net::socket class with support for TCP/UDP
over IPv4 or IPv6.
- Adds async accept/connect/disconnect/send/recv operations in
both cancellable and non-cancellable variants.
- Still need to add send_to/recv_from as well as overloads for
multi-buffer send/recv.
- Move common logic for Win32 OVERLAPPED-based operations out into
new win32_overlapped_operation[_cancellable] base-classes.
The concrete I/O operation classes are now greatly simplified
due to the reduction of boilerplate.
- Added overloads for read() and write() functions that don't
take a cancellation_token that return a simpler awaitable type
that generates much simpler assembly.
A compiler workaround was only being applied for compiler versions
below a known version and would break whenever a new version comes
out without a fix. It now just applies the workaround for all MSVC
versions. I'll re-add the version limit once MSVC actually fixes
the bug.
No longer decrement the reference count in final_suspend()
as it's not needed for a lazy shared_task where there is
always guaranteed to be one awaiting shared_task.
This also fixes a potential memory leak of the coroutine
frame if a shared_task is never awaited.
Modified condition used for decrementing ref-count to
work around potential codegen bug in Clang (PR35068).
Declare variadic when_all_ready() as forced-inline to
avoid it returning a reference to a when_all_read_task
object allocated in the stack-frame of when_all_ready().
It's no longer needed now that when_all() and sync_wait() have
been modified to work with arbitrary awaitables and create
coroutines to represent their continuations.
- Now supports when_all()/when_all_ready() on a std::vector of
arbitrary awaitables.
- Changed when_all() to use an fmap() composition with result
of when_all_ready() awaitable to extract the results from the
when_all_task objects.
This has the side-effect of meaning that
'co_await lvalueWhenAllAwaitable' now returns a prvalue result
rather than an lvalue-reference to the result.
However, the prvalue tuple/vector will contain a copy of the
results if awaiting an lvalue (allowing you to await it again
and get another copy) whereas if you co_await an rvalue awaitable
then the results will be moved into the tuple/vector.
- Re-added support for passing std::reference_wrapper-wrapped
awaitables to when_all().
- Renamed when_all_awaitable_counter to when_all_counter.
- Removed when_all_awaitable/when_all_awaitable2 as they are no
longer needed. Everything is built on when_all_ready_awaitable.
Now yields a tuple<when_all_task<T>...> when awaited which allows
the caller to retrieve individual results by calling .result()
without needing to co_await again. This should allow usage of the
result in non-coroutine contexts.
Add generic implementation for use under Clang.
Use std::forward<> rather than static_cast<> to avoid warnings
about cast from lvalue to rvalue under MSVC in some cases.
There was a section left over from before lazy_task was renamed
to task and the eager task was removed that was contrasting
lazy_task with the eager task. This comparison is no longer
necessary.
Removed the warnings about stack-overflow as these issues have
been worked around (albeit at cost of an atomic operation).
- Change fmap, schedule_on, resume_on, make_shared_task work with
arbitrary awaitables.
- Add make_task to wrap an arbitrary awaitable in a task<T>.
- Remove specialisation of fmap() for task<T>, shared_task<T> as
the generalised version obsoletes it.
Uses `std::mutex` and `std::condition_variable` to implement
the manual-rese-event concept in a more heavy-weight way but
in a way that should be portable to all other platforms.
With thanks to @yfinkelstein.
I was relying on coroutine_handle() default constructor initialising
the handle to nullptr in when_all_awaitable_counter constructor.
But this is not the case with the current MSVC implementation.
Now explicitly initialise handle to nullptr.
Now return prvalue rather than xvalue when co_awaiting a task<T>
rvalue when T is
This works around an issue with sync_wait() implementation under
MSVC that breaks for task<int>.
Clang was complaining about return-type of await_suspend()
when return-type is 'auto' and deduced to be 'bool',
saying that it must be 'bool' or 'void'.
- Added documentation to README
- Implemented fmap() for generator<T>, recursive_generator<T>,
shared_task<T>.
- Centralised operator| overloads for fmap() in fmap.hpp.
- Now provide two-argument fmap() overloads as the type-specific
implementations: fmap(func, value)
This replaces the need for detail::apply_fmap() functions.
The await_suspend() method of operator co_await() now returns bool
instead of void, to allow it to resume execution without suspending
if the task completes synchronously. However, this unfortunately
requires use of std::atomic operations to decide a potential race
between the awaiter suspending and the coroutine running to
completion.
This avoids a stack-overflow error when a coroutine awaits a
long sequence of task<T> values that complete synchronously.
eg. in a loop.
This typedef just makes available the template parameter of the
task type. This can be useful when writing generic functions that
need to operate on any of the task-types.
This allows registering continuations that are not coroutine
handles which can be useful for building some operators.
This comes at the expense of storage of an extra pointer per
coroutine and/or awaiter.
Adds the cppcoro::detail::resumer to wrap something that can
either be a callback + state or a coroutine_handle.
The promise_type::get_return_object() method was returning a reference
to the promise object, expecting that reference to be passed to the
constructor of recursive_generator<T>.
However, under Clang the result of get_return_object() is assigned to
an 'auto' variable, which ends up taking a copy of the promise object
on the stack and passing that copy into the recursive_generator<T>
constructor.
Modified get_return_object() to return the already-constructed
recursive_generator<T> object.
The io_service now launches a background thread on-demand for
keeping track of active timers. Background thread uses a waitable
timer event for performing timed waits and an auto-reset event
for signalling addition of new timers, timer cancellation and
thread shutdown.
In the case that a schedule_operation could not be queued to the
I/O completion port using PostQueuedCompletionStatus() it now
pushes the operation onto a lock-free linked-list of overflow
operations.
The event loop will check for these overflow operations and try
to requeue them to the I/O completion port at a later time.
This allows us to declare io_service::schedule_impl() noexcept
and thus the corresponding schedule_operation::await_suspend()
operation noexcept.
Added io_work_scope RAII class to help with scoped lifetime of
io_service event loop.
Having io_context always increment the ref-count of active I/O
operations whenever passed by-value to a function seems like
unnecessary atomic operations when compared to selected usages of
io_work_scope which generally only need creation for top-level
operations.
Clang didn't seem to allow conversion from get_return_object()
return value to coroutine return value via explicit constructor.
Now get_return_object() explicitly constructs task/lazy_task object
rather than returning coroutine_handle. This required moving
method definition to below task/lazy_task class definition.