Since we have lots of queues, we need an efficient queue structure,
esp. for moveable types. libstdc++'s std::deque is quite hairy,
and boost's circular_buffer_space_optimized uses assignments instead of
constructors, which are both slower and less available than constructors.
This patch implements a growable circular buffer for these needs.
Here, transferring is defined as moving an object to a new location
(either via a move or copy constructor) and destroying the source. This
is useful when implementing containers.
Test case (added in the next patch):
promise<> p1;
promise<> p2;
auto f = p1.get_future().then([f = std::move(p2.get_future())] () mutable {
return std::move(f); // this future will fail
}).then([] {
// never reached, that's ok
});
p1.set_value();
p2.set_exception(std::runtime_error("boom"));
// f should get resolved eventually with error, but was not
The callback passed to it will be executed when future gets resolved,
successfully or not. The returned future will mimic the state of the
target future.
It allows for simpler client code because the client can assume that
when cancel() returns the callback will not run and thus there's no
need to handle the race between timer teardown and execution of the
callback.
This will allow the user to have one handler for both success and
failure. This simplifies client code a bit when both paths need to
work on the same object. Having to split the function into two
callbacks, one of which is passed to then() and the other to rescue()
on a resulting promise, would make it necessary to wrap the object in
a shared_ptr, which is suboptimal.
This method, intended for request parsers, receives a stream of temporary
buffers until it determines that parsing is done, at which point it can
return any unconsumed partial buffer back to the input stream.
It is intended as a replacement for read_until(), since it can parse in a
single pass.
timer_set maintains next timeout so no need to duplicate it.
This also fixes a potential issue, if upon timer expiry no timer is
pending we did not update _next_timeout which will result in timer fd
not being updated upon next arm().
This patch adds an ability to specify how much threads seastart should
spawn. Each thread run its own instance of event loop. Separate
communication channel is created between each pair of threads.
Currently thread_pool implements cross-thread communication channel
internally. Separate communication logic into its own class to
reuse it for smp communication in later patches.
Move reference counting into the deleter core, instead of relegating it
to a shared_deleter (which has to be allocated) and an external reference
counted (also allocated). This dramatically reduces dynamic allocations.
deleter::share() is causing massive amounts of allocation. First,
since usually a packet's deleter is not a shared_deleter, we need to
allocate that shared_deleter. Second, we need an external reference
count which requires yet another allocation.
Making reference counting part of the deleter class would solve both of
these problems, but we cannot easily do that, since users hold
std::unique_ptr<deleter> which is clearly not sharable.
We could do a massive s/unique_ptr/shared_ptr/ here, but that would have
the side effect of making sharing "too easy" - you simply copy the pointer.
We'd like to keep it explicit.
So to make the change easier, rename the existing unique_ptr<deleter> as
plain "deleter", whereas the old "deleter" becomes deleter::impl:
old name new name
-------- --------
deleter deleter::impl
unique_ptr<deleter> deleter
with exactly the same semantics. A later patch can then add explicit sharing.
This fixes SIGSEGV which happens when a promise gets resolved and
dies before the callback is registerred via then(). The latter assumes
that _promise must not be nullptr if the state is not resolved
yet. The dying promise should move the resolved state into the
future's local state.
The future/promise pair is for single-shot use only, which makes it
less useful for repetitive processing.
Add a stream/subscription abstraction, where a stream can produce data
which is repetitively consumed by the subscription. End-or-stream (or
error condition) is provided by a future.
Inspired by Dart's Stream class.
As the whole point of future/promise is to decouple the producer from the
consumer, available() is not generally useful. However it can still be
useful in special cases when we want to know who got there first.
One of them is deciding when to drop packets -- if the consumer is not
ready, and no queue space is available, we should just drop the packet
instead of waiting for the consumer.
The imperative form suggests that in addition to returning a future it
performs some action and thus is needed regardless of whether we want
to add a callback or not. But in fact it does not do anything, just
gives away a future.
Signed-off-by: Tomasz Grabiec <tgrabiec@cloudius-systems.com>
Instead of keeping the future state in heap storage, referenced from
the promise/future/task, keep the state in the promise or future and only
allocate it on the heap as a last resort (for a stack).
Improves httpd throughtput by ~20%.