From 4a2d85a21b3a21d2af1d749892bb7210e8bc3d89 Mon Sep 17 00:00:00 2001 From: archi Date: Fri, 26 Aug 2022 16:45:06 +0300 Subject: [PATCH] Introduce `HeapSize()` This PR adds the possibility to determine for a chain of eventuals the maximum amount of heap memory that needs to get allocated. --- eventuals/catch.h | 5 +++++ eventuals/closure.h | 5 +++++ eventuals/concurrent-ordered.h | 9 ++++++++ eventuals/concurrent.h | 5 +++++ eventuals/conditional.h | 5 +++++ eventuals/do-all.h | 5 +++++ eventuals/event-loop.h | 17 +++++++++++++++ eventuals/eventual.h | 5 +++++ eventuals/filter.h | 5 +++++ eventuals/finally.h | 5 +++++ eventuals/flat-map.h | 5 +++++ eventuals/generator.h | 18 +++++++++++++-- eventuals/head.h | 5 +++++ eventuals/http.h | 5 +++++ eventuals/if.h | 5 +++++ eventuals/lock.h | 13 +++++++++++ eventuals/loop.h | 5 +++++ eventuals/map.h | 5 +++++ eventuals/on-begin.h | 5 +++++ eventuals/on-ended.h | 5 +++++ eventuals/raise.h | 5 +++++ eventuals/range.h | 5 +++++ eventuals/reduce.h | 5 +++++ eventuals/repeat.h | 5 +++++ eventuals/scheduler.h | 13 +++++++++++ eventuals/static-thread-pool.h | 5 +++++ eventuals/stream.h | 5 +++++ eventuals/take.h | 9 ++++++++ eventuals/task.h | 18 +++++++++++++-- eventuals/terminal.h | 5 +++++ eventuals/then.h | 9 ++++++++ eventuals/transformer.h | 17 +++++++++++++-- eventuals/until.h | 9 ++++++++ test/catch.cc | 26 ++++++++++++++++++++++ test/closure.cc | 17 +++++++++++++++ test/concurrent/success.cc | 2 ++ test/conditional.cc | 19 ++++++++++++++++ test/do-all.cc | 17 +++++++++++++++ test/eventual.cc | 16 ++++++++++++++ test/filter.cc | 22 ++++++++++++++++++- test/finally.cc | 19 +++++++++++++++- test/flat-map.cc | 16 ++++++++++++++ test/generator.cc | 21 ++++++++++++++++++ test/http.cc | 8 +++++++ test/if.cc | 20 +++++++++++++++++ test/lock.cc | 6 +++++ test/range.cc | 17 ++++++++++++++- test/repeat.cc | 2 ++ test/static-thread-pool.cc | 18 ++++++++++++--- test/stream.cc | 34 +++++++++++++++++++++++++++-- test/take.cc | 40 +++++++++++++++++++++++++++++++++- test/task.cc | 16 ++++++++++++++ test/transformer.cc | 28 ++++++++++++++++++++++++ 53 files changed, 596 insertions(+), 15 deletions(-) diff --git a/eventuals/catch.h b/eventuals/catch.h index 5516e40a1..d78cc5f53 100644 --- a/eventuals/catch.h +++ b/eventuals/catch.h @@ -7,6 +7,7 @@ #include "eventuals/terminal.h" #include "eventuals/then.h" #include "eventuals/type-traits.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -174,6 +175,10 @@ struct _Catch final { // the handler. } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + std::tuple catch_handlers_; Interrupt* interrupt_ = nullptr; diff --git a/eventuals/closure.h b/eventuals/closure.h index e397019ed..4dc4d705d 100644 --- a/eventuals/closure.h +++ b/eventuals/closure.h @@ -5,6 +5,7 @@ #include "eventuals/compose.h" #include "eventuals/interrupt.h" #include "eventuals/type-erased-stream.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -63,6 +64,10 @@ struct _Closure final { return *continuation_; } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + F_ f_; Interrupt* interrupt_ = nullptr; diff --git a/eventuals/concurrent-ordered.h b/eventuals/concurrent-ordered.h index dd1951dcf..7db3993fd 100644 --- a/eventuals/concurrent-ordered.h +++ b/eventuals/concurrent-ordered.h @@ -13,6 +13,7 @@ #include "eventuals/map.h" #include "eventuals/stream.h" #include "eventuals/terminal.h" +#include "stout/bytes.h" ///////////////////////////////////////////////////////////////////// @@ -94,6 +95,10 @@ struct _ReorderAdaptor final { upstream_->Done(); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + TypeErasedStream* upstream_ = nullptr; std::map> buffer_; @@ -208,6 +213,10 @@ struct _ConcurrentOrderedAdaptor final { upstream_->Done(); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + bool ended_ = false; std::optional index_; diff --git a/eventuals/concurrent.h b/eventuals/concurrent.h index 10df0818b..9e4c64b8f 100644 --- a/eventuals/concurrent.h +++ b/eventuals/concurrent.h @@ -14,6 +14,7 @@ #include "eventuals/terminal.h" #include "eventuals/then.h" #include "eventuals/until.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -705,6 +706,10 @@ struct _Concurrent final { handler_->Install(); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + Adaptor adaptor_; TypeErasedStream* stream_ = nullptr; diff --git a/eventuals/conditional.h b/eventuals/conditional.h index e666a0d21..16203a596 100644 --- a/eventuals/conditional.h +++ b/eventuals/conditional.h @@ -2,6 +2,7 @@ #include "eventuals/then.h" // For '_Then::Adaptor'. #include "eventuals/type-traits.h" // For 'type_identity'. +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -78,6 +79,10 @@ struct _Conditional { k_.Register(interrupt); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + Condition_ condition_; Then_ then_; Else_ else_; diff --git a/eventuals/do-all.h b/eventuals/do-all.h index 1fe8ef2d4..3e24b09fb 100644 --- a/eventuals/do-all.h +++ b/eventuals/do-all.h @@ -8,6 +8,7 @@ #include "eventuals/compose.h" #include "eventuals/scheduler.h" #include "eventuals/terminal.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -285,6 +286,10 @@ struct _DoAll final { handler_->Install(); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + // NOTE: need to destruct the fibers LAST since they have a // Scheduler::Context which may get borrowed in 'adaptor_' and // it's continuations so those need to be destructed first. diff --git a/eventuals/event-loop.h b/eventuals/event-loop.h index dbee768a4..5dfc4113b 100644 --- a/eventuals/event-loop.h +++ b/eventuals/event-loop.h @@ -18,6 +18,7 @@ #include "eventuals/then.h" #include "eventuals/type-traits.h" #include "stout/borrowed_ptr.h" +#include "stout/bytes.h" #include "uv.h" //////////////////////////////////////////////////////////////////////// @@ -398,6 +399,10 @@ class EventLoop final : public Scheduler { handler_->Install(); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + private: EventLoop& loop() { return clock_->loop(); @@ -738,6 +743,10 @@ class EventLoop final : public Scheduler { handler_->Install(); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + private: // Adaptors to libuv functions. uv_signal_t* signal() { @@ -1039,6 +1048,10 @@ class EventLoop final : public Scheduler { handler_->Install(); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + private: // Adaptors to libuv functions. uv_poll_t* poll() { @@ -1259,6 +1272,10 @@ struct _EventLoopSchedule final { } } + Bytes StaticHeapSize() { + return Bytes(sizeof(Adapted_)) + k_.StaticHeapSize(); + } + E_ e_; std::optional< diff --git a/eventuals/eventual.h b/eventuals/eventual.h index dd55911db..0e85351ea 100644 --- a/eventuals/eventual.h +++ b/eventuals/eventual.h @@ -6,6 +6,7 @@ #include "eventuals/interrupt.h" #include "eventuals/scheduler.h" #include "eventuals/undefined.h" +#include "stout/bytes.h" // TODO(benh): catch exceptions from 'start', 'fail', 'stop', etc. @@ -170,6 +171,10 @@ struct _Eventual { return adaptor_; } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + Context_ context_; Start_ start_; Fail_ fail_; diff --git a/eventuals/filter.h b/eventuals/filter.h index 8b1d37ef0..1ebf85da0 100644 --- a/eventuals/filter.h +++ b/eventuals/filter.h @@ -1,6 +1,7 @@ #pragma once #include "eventuals/stream.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -46,6 +47,10 @@ struct _Filter final { k_.Register(interrupt); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + F_ f_; TypeErasedStream* stream_ = nullptr; diff --git a/eventuals/finally.h b/eventuals/finally.h index 29189c7ef..81202d3d0 100644 --- a/eventuals/finally.h +++ b/eventuals/finally.h @@ -3,6 +3,7 @@ #include "eventuals/expected.h" #include "eventuals/terminal.h" // For 'StoppedException'. #include "eventuals/then.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -41,6 +42,10 @@ struct _Finally final { k_.Register(interrupt); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + K_ k_; }; diff --git a/eventuals/flat-map.h b/eventuals/flat-map.h index 8cf89e424..366285c07 100644 --- a/eventuals/flat-map.h +++ b/eventuals/flat-map.h @@ -4,6 +4,7 @@ #include "eventuals/stream.h" #include "eventuals/terminal.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -129,6 +130,10 @@ struct _FlatMap final { }); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + F_ f_; TypeErasedStream* outer_ = nullptr; diff --git a/eventuals/generator.h b/eventuals/generator.h index a148e19c6..d3cba8eec 100644 --- a/eventuals/generator.h +++ b/eventuals/generator.h @@ -9,6 +9,7 @@ #include "eventuals/terminal.h" #include "eventuals/then.h" #include "eventuals/type-traits.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -202,9 +203,11 @@ struct _Generator final { Continuation( K_ k, std::tuple&& args, - DispatchCallback&& dispatch) + DispatchCallback&& dispatch, + Bytes&& static_heap_size) : args_(std::move(args)), dispatch_(std::move(dispatch)), + static_heap_size_(std::move(static_heap_size)), k_(std::move(k)) {} // All Continuation functions just trigger dispatch Callback, @@ -291,6 +294,10 @@ struct _Generator final { args_); } + Bytes StaticHeapSize() { + return static_heap_size_ + k_.StaticHeapSize(); + } + std::tuple args_; DispatchCallback dispatch_; @@ -298,6 +305,8 @@ struct _Generator final { std::unique_ptr> e_; Interrupt* interrupt_ = nullptr; + Bytes static_heap_size_ = 0; + // NOTE: we store 'k_' as the _last_ member so it will be // destructed _first_ and thus we won't have any use-after-delete // issues during destruction of 'k_' if it holds any references or @@ -399,6 +408,8 @@ struct _Generator final { std::is_convertible_v, "eventual result type can not be converted into type of 'Generator'"); + static_heap_size_ = Bytes(sizeof(HeapGenerator)); + dispatch_ = [f = std::move(f)]( Action action, std::optional&& exception, @@ -474,7 +485,8 @@ struct _Generator final { return Continuation( std::move(k), std::move(args_), - std::move(dispatch_)); + std::move(dispatch_), + std::move(static_heap_size_)); } std::conditional_t< @@ -484,6 +496,8 @@ struct _Generator final { dispatch_; std::tuple args_; + + Bytes static_heap_size_ = 0; }; }; diff --git a/eventuals/head.h b/eventuals/head.h index 9b63adbdc..67dc9e68d 100644 --- a/eventuals/head.h +++ b/eventuals/head.h @@ -1,6 +1,7 @@ #pragma once #include "eventuals/stream.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -45,6 +46,10 @@ struct _Head final { k_.Register(interrupt); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + std::optional arg_; TypeErasedStream* stream_ = nullptr; diff --git a/eventuals/http.h b/eventuals/http.h index bdf47dc33..856060e20 100644 --- a/eventuals/http.h +++ b/eventuals/http.h @@ -10,6 +10,7 @@ #include "eventuals/event-loop.h" #include "eventuals/scheduler.h" #include "eventuals/x509.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -1141,6 +1142,10 @@ struct _HTTP final { handler_->Install(); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.HeapSize(); + } + private: EventLoop& loop_; diff --git a/eventuals/if.h b/eventuals/if.h index 780670a7f..71cbab158 100644 --- a/eventuals/if.h +++ b/eventuals/if.h @@ -3,6 +3,7 @@ #include "eventuals/eventual.h" #include "eventuals/then.h" // For '_Then::Adaptor'. #include "eventuals/type-traits.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -57,6 +58,10 @@ struct _If final { k_.Register(interrupt); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + bool condition_; YesE_ yes_; NoE_ no_; diff --git a/eventuals/lock.h b/eventuals/lock.h index 98eb69af5..b88ff4534 100644 --- a/eventuals/lock.h +++ b/eventuals/lock.h @@ -10,6 +10,7 @@ #include "eventuals/stream.h" #include "eventuals/then.h" #include "eventuals/undefined.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -456,6 +457,10 @@ struct _Acquire final { k_.Register(interrupt); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + Lock* lock_; Lock::Waiter waiter_; std::optional< @@ -548,6 +553,10 @@ struct _Release final { k_.Register(interrupt); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + Lock* lock_; // NOTE: we store 'k_' as the _last_ member so it will be @@ -776,6 +785,10 @@ struct _Wait final { k_.Register(interrupt); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + Lock* lock_; F_ f_; diff --git a/eventuals/loop.h b/eventuals/loop.h index f710d8423..aa0e7b3e9 100644 --- a/eventuals/loop.h +++ b/eventuals/loop.h @@ -6,6 +6,7 @@ #include "eventuals/stream.h" #include "eventuals/type-traits.h" #include "eventuals/undefined.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -216,6 +217,10 @@ struct _Loop final { return adaptor_; } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + Context_ context_; Begin_ begin_; Body_ body_; diff --git a/eventuals/map.h b/eventuals/map.h index 902125a8d..3613053a6 100644 --- a/eventuals/map.h +++ b/eventuals/map.h @@ -3,6 +3,7 @@ #include "eventuals/compose.h" // For 'HasValueFrom'. #include "eventuals/stream.h" #include "eventuals/then.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -78,6 +79,10 @@ struct _Map final { k_.Register(interrupt); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + E_ e_; using Adapted_ = decltype(std::declval().template k( diff --git a/eventuals/on-begin.h b/eventuals/on-begin.h index 1ea942226..955ca7351 100644 --- a/eventuals/on-begin.h +++ b/eventuals/on-begin.h @@ -3,6 +3,7 @@ #include "eventuals/eventual.h" #include "eventuals/stream.h" #include "eventuals/then.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -89,6 +90,10 @@ struct _OnBegin final { k_.Register(interrupt); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + E_ e_; Interrupt* interrupt_ = nullptr; diff --git a/eventuals/on-ended.h b/eventuals/on-ended.h index 57f50dcec..0f0ebf27d 100644 --- a/eventuals/on-ended.h +++ b/eventuals/on-ended.h @@ -3,6 +3,7 @@ #include "eventuals/eventual.h" #include "eventuals/stream.h" #include "eventuals/then.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -92,6 +93,10 @@ struct _OnEnded final { k_.Register(interrupt); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + E_ e_; Interrupt* interrupt_ = nullptr; diff --git a/eventuals/raise.h b/eventuals/raise.h index 9e562726c..244edfe00 100644 --- a/eventuals/raise.h +++ b/eventuals/raise.h @@ -4,6 +4,7 @@ #include "eventuals/interrupt.h" #include "eventuals/type-traits.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -36,6 +37,10 @@ struct _Raise final { k_.Register(interrupt); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + T_ t_; // NOTE: we store 'k_' as the _last_ member so it will be diff --git a/eventuals/range.h b/eventuals/range.h index 3555fd91a..196176e91 100644 --- a/eventuals/range.h +++ b/eventuals/range.h @@ -1,6 +1,7 @@ #pragma once #include "eventuals/stream.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -66,6 +67,10 @@ struct _Range final { }); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + int from_; const int to_; const int step_; diff --git a/eventuals/reduce.h b/eventuals/reduce.h index 201acf15a..fe40f479d 100644 --- a/eventuals/reduce.h +++ b/eventuals/reduce.h @@ -1,6 +1,7 @@ #pragma once #include "eventuals/stream.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -84,6 +85,10 @@ struct _Reduce final { k_.Register(interrupt); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + T_ t_; F_ f_; diff --git a/eventuals/repeat.h b/eventuals/repeat.h index fa2a1b409..69046cc2a 100644 --- a/eventuals/repeat.h +++ b/eventuals/repeat.h @@ -3,6 +3,7 @@ #include "eventuals/compose.h" // For 'HasValueFrom'. #include "eventuals/map.h" #include "eventuals/stream.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -53,6 +54,10 @@ struct _Repeat final { }); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + stout::borrowed_ptr previous_; // NOTE: we store 'k_' as the _last_ member so it will be diff --git a/eventuals/scheduler.h b/eventuals/scheduler.h index 0706e08bc..4f10ade0b 100644 --- a/eventuals/scheduler.h +++ b/eventuals/scheduler.h @@ -15,6 +15,7 @@ #include "eventuals/terminal.h" #include "eventuals/undefined.h" #include "stout/borrowable.h" +#include "stout/bytes.h" #include "stout/stringify.h" //////////////////////////////////////////////////////////////////////// @@ -288,6 +289,10 @@ struct _Reschedule final { k_.Register(interrupt); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + stout::borrowed_ref context_; std::optional< @@ -392,6 +397,10 @@ struct Reschedulable final { interrupt_ = &interrupt; } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + Interrupt* interrupt_ = nullptr; using Continuation_ = @@ -471,6 +480,10 @@ struct _Preempt final { } } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + Scheduler::Context context_; E_ e_; diff --git a/eventuals/static-thread-pool.h b/eventuals/static-thread-pool.h index ae6436853..05a1f154c 100644 --- a/eventuals/static-thread-pool.h +++ b/eventuals/static-thread-pool.h @@ -15,6 +15,7 @@ #include "eventuals/scheduler.h" #include "eventuals/semaphore.h" #include "stout/borrowed_ptr.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -484,6 +485,10 @@ struct _StaticThreadPoolSchedule final { return cpu; } + Bytes StaticHeapSize() { + return Bytes(sizeof(Adapted_)) + k_.StaticHeapSize(); + } + E_ e_; std::optional< diff --git a/eventuals/stream.h b/eventuals/stream.h index d511c56c2..4ec4dd632 100644 --- a/eventuals/stream.h +++ b/eventuals/stream.h @@ -21,6 +21,7 @@ #include "eventuals/type-traits.h" #include "eventuals/undefined.h" #include "stout/borrowed_ptr.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -303,6 +304,10 @@ struct _Stream final { return adaptor_; } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + Context_ context_; Begin_ begin_; Next_ next_; diff --git a/eventuals/take.h b/eventuals/take.h index 9f23af081..5ed928153 100644 --- a/eventuals/take.h +++ b/eventuals/take.h @@ -6,6 +6,7 @@ #include "eventuals/eventual.h" #include "eventuals/filter.h" #include "eventuals/stream.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -94,6 +95,10 @@ struct _TakeLast final { }); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + size_t n_; // NOTE: because we are "taking" we need a value type here (not @@ -212,6 +217,10 @@ struct _TakeRange final { k_.Register(interrupt); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + size_t begin_; size_t amount_; size_t i_ = 0; diff --git a/eventuals/task.h b/eventuals/task.h index 438a8b8e2..bd957ea04 100644 --- a/eventuals/task.h +++ b/eventuals/task.h @@ -11,6 +11,7 @@ #include "eventuals/raise.h" #include "eventuals/terminal.h" #include "eventuals/type-traits.h" +#include "stout/bytes.h" #include "stout/stringify.h" //////////////////////////////////////////////////////////////////////// @@ -204,9 +205,11 @@ struct _TaskFromToWith final { std::variant< MonostateIfVoidOrReferenceWrapperOr, DispatchCallback>&& - value_or_dispatch) + value_or_dispatch, + Bytes&& static_heap_size) : args_(std::move(args)), value_or_dispatch_(std::move(value_or_dispatch)), + static_heap_size_(std::move(static_heap_size)), k_(std::move(k)) {} template @@ -258,6 +261,10 @@ struct _TaskFromToWith final { k_.Register(interrupt); } + Bytes StaticHeapSize() { + return static_heap_size_ + k_.StaticHeapSize(); + } + void Dispatch( Action action, std::optional>&& from = std::nullopt, @@ -299,6 +306,8 @@ struct _TaskFromToWith final { std::unique_ptr> e_; Interrupt* interrupt_ = nullptr; + Bytes static_heap_size_ = 0; + // NOTE: we store 'k_' as the _last_ member so it will be // destructed _first_ and thus we won't have any use-after-delete // issues during destruction of 'k_' if it holds any references or @@ -383,6 +392,8 @@ struct _TaskFromToWith final { std::is_convertible>, "eventual result type can not be converted into type of 'Task'"); + static_heap_size_ = Bytes(sizeof(HeapTask)); + value_or_dispatch_ = [f = std::move(f)]( Action action, std::optional&& exception, @@ -451,7 +462,8 @@ struct _TaskFromToWith final { return Continuation( std::move(k), std::move(args_), - std::move(value_or_dispatch_.value())); + std::move(value_or_dispatch_.value()), + std::move(static_heap_size_)); } // See comment in `Continuation` for explanation of `dispatch_` member. @@ -464,6 +476,8 @@ struct _TaskFromToWith final { value_or_dispatch_; std::tuple args_; + + Bytes static_heap_size_ = 0; }; }; diff --git a/eventuals/terminal.h b/eventuals/terminal.h index 266c87539..c5ab27370 100644 --- a/eventuals/terminal.h +++ b/eventuals/terminal.h @@ -3,6 +3,7 @@ #include "eventuals/compose.h" #include "eventuals/interrupt.h" #include "eventuals/undefined.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -57,6 +58,10 @@ struct _Terminal final { void Register(Interrupt&) {} + Bytes StaticHeapSize() { + return Bytes(0); + } + Context_ context_; Start_ start_; Fail_ fail_; diff --git a/eventuals/then.h b/eventuals/then.h index 8a92ad7c6..65854e045 100644 --- a/eventuals/then.h +++ b/eventuals/then.h @@ -3,6 +3,7 @@ #include "eventuals/compose.h" #include "eventuals/eventual.h" #include "eventuals/just.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -151,6 +152,10 @@ struct _Then::Continuation final { k_.Register(interrupt); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + F_ f_; // NOTE: we store 'k_' as the _last_ member so it will be @@ -195,6 +200,10 @@ struct _Then::Continuation final { k_.Register(interrupt); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + F_ f_; Interrupt* interrupt_ = nullptr; diff --git a/eventuals/transformer.h b/eventuals/transformer.h index c05d021f6..3d3de309c 100644 --- a/eventuals/transformer.h +++ b/eventuals/transformer.h @@ -6,6 +6,7 @@ #include "eventuals/callback.h" #include "eventuals/stream.h" #include "eventuals/terminal.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -148,8 +149,9 @@ struct _Transformer final { template struct Continuation final { template - Continuation(K_ k, Dispatch dispatch) + Continuation(K_ k, Dispatch dispatch, Bytes&& static_heap_size) : dispatch_(std::move(dispatch)), + static_heap_size_(std::move(static_heap_size)), k_(std::move(k)) {} void Begin(TypeErasedStream& stream) { @@ -209,6 +211,10 @@ struct _Transformer final { }); } + Bytes StaticHeapSize() { + return static_heap_size_ + k_.StaticHeapSize(); + } + Callback&&, @@ -225,6 +231,8 @@ struct _Transformer final { std::unique_ptr> e_; Interrupt* interrupt_ = nullptr; + Bytes static_heap_size_ = 0; + // NOTE: we store 'k_' as the _last_ member so it will be // destructed _first_ and thus we won't have any use-after-delete // issues during destruction of 'k_' if it holds any references or @@ -297,6 +305,8 @@ struct _Transformer final { "eventual result type can not be converted " "into type of 'Transformer'"); + static_heap_size_ = Bytes(sizeof(HeapTransformer)); + dispatch_ = [f = std::move(f)]( Action action, std::optional&& exception, @@ -360,7 +370,8 @@ struct _Transformer final { auto k(K k) && { return Continuation( std::move(k), - std::move(dispatch_)); + std::move(dispatch_), + std::move(static_heap_size_)); } Callback&&, Callback&&)> dispatch_; + + Bytes static_heap_size_ = 0; }; }; diff --git a/eventuals/until.h b/eventuals/until.h index 8314c7e14..618bf29a6 100644 --- a/eventuals/until.h +++ b/eventuals/until.h @@ -2,6 +2,7 @@ #include "eventuals/stream.h" #include "eventuals/then.h" +#include "stout/bytes.h" //////////////////////////////////////////////////////////////////////// @@ -154,6 +155,10 @@ struct _Until::Continuation final { k_.Ended(); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + F_ f_; TypeErasedStream* stream_ = nullptr; @@ -224,6 +229,10 @@ struct _Until::Continuation final { k_.Ended(); } + Bytes StaticHeapSize() { + return Bytes(0) + k_.StaticHeapSize(); + } + F_ f_; TypeErasedStream* stream_ = nullptr; diff --git a/test/catch.cc b/test/catch.cc index 55fcbf7d9..b0a2fe333 100644 --- a/test/catch.cc +++ b/test/catch.cc @@ -292,6 +292,8 @@ TEST(CatchTest, Interrupt) { auto [future, k] = PromisifyForTest(e()); + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + Interrupt interrupt; k.Register(interrupt); @@ -301,5 +303,29 @@ TEST(CatchTest, Interrupt) { EXPECT_EQ(future.get(), "100"); } +TEST(CatchTest, StaticHeapSize) { + auto e = []() { + return Just("error") + >> Then([](const char* i) { + return; + }) + >> Catch() + .raised([](std::exception&& error) { + EXPECT_STREQ(error.what(), "error"); + }) + >> Then([]() { + return 1; + }); + }; + + auto [future, k] = PromisifyForTest(e()); + + k.Start(); + + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + + EXPECT_EQ(1, future.get()); +} + } // namespace } // namespace eventuals::test diff --git a/test/closure.cc b/test/closure.cc index 0b13e915b..61fb75b6a 100644 --- a/test/closure.cc +++ b/test/closure.cc @@ -158,5 +158,22 @@ TEST(ClosureTest, Interrupt) { EXPECT_THROW(future.get(), eventuals::StoppedException); } +TEST(ClosureTest, StaticHeapSize) { + auto e = []() { + return Just(1) + >> Closure([i = 41]() { + return Then([&](int value) { return i + value; }); + }); + }; + + auto [future, k] = PromisifyForTest(e()); + + k.Start(); + + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + + EXPECT_EQ(42, future.get()); +} + } // namespace } // namespace eventuals::test diff --git a/test/concurrent/success.cc b/test/concurrent/success.cc index 0f7b81a68..a159aab30 100644 --- a/test/concurrent/success.cc +++ b/test/concurrent/success.cc @@ -49,6 +49,8 @@ TYPED_TEST(ConcurrentTypedTest, Success) { k.Start(); + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + ASSERT_EQ(2, callbacks.size()); EXPECT_EQ( diff --git a/test/conditional.cc b/test/conditional.cc index 855edcf21..c180a996b 100644 --- a/test/conditional.cc +++ b/test/conditional.cc @@ -184,5 +184,24 @@ TEST(ConditionalTest, Raise) { EXPECT_EQ(2, *c()); } +TEST(ConditionalTest, StaticHeapSize) { + auto e = []() { + return Just(1) + >> Then([](int i) { return i + 1; }) + >> Conditional( + [](int i) { return i > 1; }, + [](int i) { return Just(i); }, + [](int i) { return Raise("raise"); }); + }; + + auto [future, k] = PromisifyForTest(e()); + + k.Start(); + + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + + EXPECT_EQ(2, future.get()); +} + } // namespace } // namespace eventuals::test diff --git a/test/do-all.cc b/test/do-all.cc index 874b6af43..add303d58 100644 --- a/test/do-all.cc +++ b/test/do-all.cc @@ -127,5 +127,22 @@ TEST(DoAllTest, Interrupt) { EXPECT_THROW(future.get(), eventuals::StoppedException); } +TEST(DoAllTest, StaticHeapSize) { + auto e = []() { + return DoAll( + Eventual([](auto& k) { k.Start(42); }), + Eventual([](auto& k) { k.Start(std::string("hello")); }), + Eventual([](auto& k) { k.Start(); })); + }; + + auto [future, k] = PromisifyForTest(e()); + + k.Start(); + + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + + EXPECT_EQ(std::make_tuple(42, "hello", std::monostate{}), future.get()); +} + } // namespace } // namespace eventuals::test diff --git a/test/eventual.cc b/test/eventual.cc index fe217ca82..0d5ca694c 100644 --- a/test/eventual.cc +++ b/test/eventual.cc @@ -332,5 +332,21 @@ TEST(EventualTest, Ref) { EXPECT_EQ(110, x); } +TEST(EventualTest, StaticHeapSize) { + auto e = []() { + return Eventual([](auto& k) { + k.Start(1); + }); + }; + + auto [future, k] = PromisifyForTest(e()); + + k.Start(); + + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + + EXPECT_EQ(1, future.get()); +} + } // namespace } // namespace eventuals::test diff --git a/test/filter.cc b/test/filter.cc index ba41e17d6..ed4c5928f 100644 --- a/test/filter.cc +++ b/test/filter.cc @@ -8,9 +8,9 @@ #include "eventuals/iterate.h" #include "eventuals/loop.h" #include "eventuals/map.h" -#include "eventuals/promisify.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "test/promisify-for-test.h" namespace eventuals::test { namespace { @@ -93,5 +93,25 @@ TEST(Filter, OddMapCollectFlow) { EXPECT_THAT(*s(), UnorderedElementsAre(6, 18)); } +TEST(Filter, StaticHeapSize) { + std::vector v = {5, 12, 17}; + auto begin = v.begin(); + auto end = v.end(); + + auto e = [&]() { + return Iterate(begin, end) + >> Filter([](int x) { return x % 2 == 1; }) + >> Collect(); + }; + + auto [future, k] = PromisifyForTest(e()); + + k.Start(); + + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + + EXPECT_EQ((std::set{5, 17}), future.get()); +} + } // namespace } // namespace eventuals::test diff --git a/test/finally.cc b/test/finally.cc index f2554b06a..34fbff650 100644 --- a/test/finally.cc +++ b/test/finally.cc @@ -6,11 +6,11 @@ #include "eventuals/finally.h" #include "eventuals/if.h" #include "eventuals/just.h" -#include "eventuals/promisify.h" #include "eventuals/raise.h" #include "eventuals/then.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "test/promisify-for-test.h" namespace eventuals::test { namespace { @@ -151,5 +151,22 @@ TEST(Finally, FinallyInsideThen) { EXPECT_NO_THROW(*e()); } +TEST(Finally, StaticHeapSize) { + auto e = []() { + return Just(42) + >> Finally([](expected&& expected) { + return Just(std::move(expected)); + }); + }; + + auto [future, k] = PromisifyForTest(e()); + + k.Start(); + + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + + EXPECT_EQ(42, future.get()); +} + } // namespace } // namespace eventuals::test diff --git a/test/flat-map.cc b/test/flat-map.cc index 5fdb516e5..cf481cc47 100644 --- a/test/flat-map.cc +++ b/test/flat-map.cc @@ -277,5 +277,21 @@ TEST(FlatMap, InterruptReturn) { EXPECT_THROW(future.get(), eventuals::StoppedException); } +TEST(FlatMap, StaticHeapSize) { + auto s = []() { + return Range(2) + >> FlatMap([](int x) { return Range(2); }) + >> Collect(); + }; + + auto [future, k] = PromisifyForTest(s()); + + k.Start(); + + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + + EXPECT_EQ((std::vector{0, 1, 0, 1}), future.get()); +} + } // namespace } // namespace eventuals::test diff --git a/test/generator.cc b/test/generator.cc index fd0563898..e6782c414 100644 --- a/test/generator.cc +++ b/test/generator.cc @@ -561,5 +561,26 @@ TEST(Generator, Raises) { EXPECT_THROW(*e(), std::runtime_error); } +TEST(Generator, StaticHeapSize) { + auto stream = []() -> Generator::Of { + return []() { + return Iterate({1, 2, 3}); + }; + }; + + auto e = [&]() { + return stream() + >> Collect(); + }; + + auto [future, k] = PromisifyForTest(e()); + + k.Start(); + + EXPECT_TRUE(k.StaticHeapSize().bytes() > 0); + + EXPECT_THAT(future.get(), ElementsAre(1, 2, 3)); +} + } // namespace } // namespace eventuals::test diff --git a/test/http.cc b/test/http.cc index 45e5ed5c8..c907693b5 100644 --- a/test/http.cc +++ b/test/http.cc @@ -166,6 +166,8 @@ TEST_P(HttpTest, GetInterrupt) { Interrupt interrupt; + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + k.Register(interrupt); k.Start(); @@ -193,6 +195,8 @@ TEST_P(HttpTest, PostInterrupt) { k.Start(); + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + interrupt.Trigger(); RunUntil(future); @@ -211,6 +215,8 @@ TEST_P(HttpTest, GetInterruptAfterStart) { k.Register(interrupt); + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + k.Start(); // NOTE: now that we've started the continuation 'k' we will have @@ -243,6 +249,8 @@ TEST_P(HttpTest, PostInterruptAfterStart) { Interrupt interrupt; + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + k.Register(interrupt); k.Start(); diff --git a/test/if.cc b/test/if.cc index dd01f4359..c1f055cd2 100644 --- a/test/if.cc +++ b/test/if.cc @@ -130,5 +130,25 @@ TEST(IfTest, Raise) { EXPECT_EQ(42, *e()); } + +TEST(IfTest, StaticHeapSize) { + auto e = []() { + return Just(1) + >> Then([](int i) { + return If(i == 1) + .yes([]() { return Just("yes"); }) + .no([]() { return Just("no"); }); + }); + }; + + auto [future, k] = PromisifyForTest(e()); + + k.Start(); + + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + + EXPECT_EQ("yes", future.get()); +} + } // namespace } // namespace eventuals::test diff --git a/test/lock.cc b/test/lock.cc index f6ff16e73..75fe6881a 100644 --- a/test/lock.cc +++ b/test/lock.cc @@ -60,12 +60,18 @@ TEST(LockTest, Succeed) { t1.Start(); + EXPECT_EQ(0, t1.StaticHeapSize().bytes()); + EXPECT_EQ("t1", future1.get()); t2.Start(); + EXPECT_EQ(0, t2.StaticHeapSize().bytes()); + t3.Start(); + EXPECT_EQ(0, t3.StaticHeapSize().bytes()); + EXPECT_STREQ("t3", future3.get()); EXPECT_EQ("t2", future2.get()); diff --git a/test/range.cc b/test/range.cc index 0a5e1f7ff..7387bec17 100644 --- a/test/range.cc +++ b/test/range.cc @@ -3,9 +3,9 @@ #include #include "eventuals/collect.h" -#include "eventuals/promisify.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "test/promisify-for-test.h" namespace eventuals::test { namespace { @@ -89,5 +89,20 @@ TEST(Range, SpecifiedStepNegative) { EXPECT_THAT(*s, ElementsAre(0, -2, -4, -6, -8)); } +TEST(Range, StaticHeapSize) { + auto s = []() { + return Range(0, 5) + >> Collect(); + }; + + auto [future, k] = PromisifyForTest(s()); + + k.Start(); + + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + + EXPECT_EQ((std::vector{0, 1, 2, 3, 4}), future.get()); +} + } // namespace } // namespace eventuals::test diff --git a/test/repeat.cc b/test/repeat.cc index 1efd47051..eab359773 100644 --- a/test/repeat.cc +++ b/test/repeat.cc @@ -121,6 +121,8 @@ TEST(RepeatTest, Interrupt) { k.Register(interrupt); + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + EXPECT_CALL(start, Call()) .WillOnce([&]() { interrupt.Trigger(); diff --git a/test/static-thread-pool.cc b/test/static-thread-pool.cc index 9faf4e74c..7149437a5 100644 --- a/test/static-thread-pool.cc +++ b/test/static-thread-pool.cc @@ -10,13 +10,13 @@ #include "eventuals/let.h" #include "eventuals/loop.h" #include "eventuals/map.h" -#include "eventuals/promisify.h" #include "eventuals/repeat.h" #include "eventuals/scheduler.h" #include "eventuals/then.h" #include "eventuals/until.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "test/promisify-for-test.h" namespace eventuals::test { namespace { @@ -44,7 +44,13 @@ TEST(StaticThreadPoolTest, Schedulable) { Foo foo; - EXPECT_EQ(42, *foo.Operation()); + auto [future, k] = PromisifyForTest(foo.Operation()); + + k.Start(); + + EXPECT_TRUE(k.StaticHeapSize().bytes() > 0); + + EXPECT_EQ(42, future.get()); } @@ -74,7 +80,13 @@ TEST(StaticThreadPoolTest, Reschedulable) { })); }; - *e(); + auto [future, k] = PromisifyForTest(e()); + + k.Start(); + + EXPECT_TRUE(k.StaticHeapSize().bytes() > 0); + + future.get(); } diff --git a/test/stream.cc b/test/stream.cc index d59f8592d..c1d65d6e5 100644 --- a/test/stream.cc +++ b/test/stream.cc @@ -362,7 +362,13 @@ TEST(StreamTest, MapThenLoop) { }); }; - EXPECT_EQ(20, *s()); + auto [future, k] = PromisifyForTest(s()); + + k.Start(); + + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + + EXPECT_EQ(20, future.get()); } @@ -393,7 +399,13 @@ TEST(StreamTest, MapThenReduce) { }); }; - EXPECT_EQ(20, *s()); + auto [future, k] = PromisifyForTest(s()); + + k.Start(); + + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + + EXPECT_EQ(20, future.get()); } @@ -499,5 +511,23 @@ TEST(StreamTest, ThrowGeneralError) { ThrowsMessage(StrEq("error"))); } +TEST(StreamTest, HeadHeapSize) { + auto s = []() { + return Stream() + .next([](auto& k) { + k.Emit(42); + }) + >> Head(); + }; + + auto [future, k] = PromisifyForTest(s()); + + k.Start(); + + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + + EXPECT_EQ(42, future.get()); +} + } // namespace } // namespace eventuals::test diff --git a/test/take.cc b/test/take.cc index 24500a05b..709e745cc 100644 --- a/test/take.cc +++ b/test/take.cc @@ -5,9 +5,9 @@ #include "eventuals/collect.h" #include "eventuals/filter.h" #include "eventuals/iterate.h" -#include "eventuals/promisify.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "test/promisify-for-test.h" namespace eventuals::test { namespace { @@ -174,5 +174,43 @@ TEST(Take, TakeRangeInfiniteStream) { EXPECT_THAT(*s(), ElementsAre(0, 1)); } +TEST(Take, TakeLastStaticHeapSize) { + std::vector v = {5, 12, 17, 3}; + + auto s = [&]() { + return Iterate(v) + >> TakeLast(2) + >> Collect(); + }; + + EXPECT_THAT(*s(), ElementsAre(17, 3)); + + auto [future, k] = PromisifyForTest(s()); + + k.Start(); + + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + + EXPECT_THAT(future.get(), ElementsAre(17, 3)); +} + +TEST(Take, TakeRangeStaticHeapSize) { + std::vector v = {5, 12, 17, 20, 22, 1, 1, 1}; + + auto s = [&]() { + return Iterate(v) + >> TakeRange(1, 2) + >> Collect(); + }; + + auto [future, k] = PromisifyForTest(s()); + + k.Start(); + + EXPECT_EQ(0, k.StaticHeapSize().bytes()); + + EXPECT_THAT(future.get(), ElementsAre(12, 17)); +} + } // namespace } // namespace eventuals::test diff --git a/test/task.cc b/test/task.cc index 06a6c4bf6..cf7db243e 100644 --- a/test/task.cc +++ b/test/task.cc @@ -671,5 +671,21 @@ TEST(Task, RaisesWith) { EXPECT_EQ(42, *e()); } +TEST(Task, StaticHeapSize) { + auto e = []() -> Task::Of { + return [x = 42]() { + return Just(x); + }; + }; + + auto [future, k] = PromisifyForTest(e()); + + k.Start(); + + EXPECT_TRUE(k.StaticHeapSize().bytes() > 0); + + EXPECT_EQ(42, future.get()); +} + } // namespace } // namespace eventuals::test diff --git a/test/transformer.cc b/test/transformer.cc index f60142ae0..519f88c1b 100644 --- a/test/transformer.cc +++ b/test/transformer.cc @@ -242,5 +242,33 @@ TEST(Transformer, PropagateFail) { ThrowsMessage(StrEq("error"))); } +TEST(Transformer, StaticHeapSize) { + auto transformer = []() { + return Transformer::From::To( + []() { + return Map([](int x) { + return std::to_string(x); + }); + }); + }; + + auto e = [&]() { + return Iterate({100}) + >> transformer() + >> Map([](std::string s) { + return s; + }) + >> Collect(); + }; + + auto [future, k] = PromisifyForTest(e()); + + k.Start(); + + EXPECT_TRUE(k.StaticHeapSize().bytes() > 0); + + EXPECT_THAT(future.get(), ElementsAre("100")); +} + } // namespace } // namespace eventuals::test