Skip to content

Commit

Permalink
Shrink nested callbacks before wrapping them
Browse files Browse the repository at this point in the history
This allows circumventing allocations when there's still enough free
space in the buffer.
  • Loading branch information
muggenhor committed Nov 27, 2017
1 parent 4ae8b7a commit 734dd27
Showing 1 changed file with 58 additions and 1 deletion.
59 changes: 58 additions & 1 deletion callback.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,15 @@ namespace util
bool& ret;
};

struct callback_tag_determine_used_storage_size
{
constexpr callback_tag_determine_used_storage_size(size_t& used_storage_size) noexcept
: used_storage_size(used_storage_size)
{}

size_t& used_storage_size;
};

struct callback_tag_clone
{
explicit constexpr callback_tag_clone(void* dst) noexcept : dst(dst) {}
Expand Down Expand Up @@ -289,6 +298,7 @@ namespace util
using callback_method = boost::variant<
callback_tag_invoke<Args...>
, callback_tag_is_valid
, callback_tag_determine_used_storage_size
, callback_tag_move
, callback_tag_move_resize
, callback_tag_clone
Expand Down Expand Up @@ -415,6 +425,15 @@ namespace util
return {};
}

callback_ret<R> operator()(const callback_tag_determine_used_storage_size& op) const noexcept
{
op.used_storage_size = stored_internally
? (std::is_empty<callback_impl>::value ? 0 : sizeof(callback_impl))
: sizeof(callback_impl*)
;
return {};
}

callback_ret<R> operator()(const callback_tag_move& op) const noexcept
{
assert(op.src != op.dst && "move constructing to the same address is illegal!");
Expand Down Expand Up @@ -543,6 +562,37 @@ namespace util
return impl_t::template invoke_method<stored_internally>;
}

template <typename R, typename... Args, typename Storage, typename LockablePtr, typename FS, std::size_t FSBS>
callback_method_invoker<R, Args...> construct_callback_impl(Storage& s, LockablePtr p, callback<FS, FSBS>&& f, const std::size_t used_storage_size, std::integral_constant<std::size_t, 0>)
{
// Termination condition
assert(used_storage_size == 0);
return construct_callback_impl<R, Args...>(s, std::move(p), callback<FS, 0>{std::move(f)});
}

template <typename R, typename... Args, typename Storage, typename LockablePtr, std::size_t SBS, typename FS, std::size_t FSBS>
callback_method_invoker<R, Args...> construct_callback_impl(Storage& s, LockablePtr p, callback<FS, FSBS>&& f, const std::size_t used_storage_size, std::integral_constant<std::size_t, SBS> current_size)
{
const auto needed_blocks = (used_storage_size + Storage::align - 1) / Storage::align;
constexpr auto cur_blocks = (current_size + Storage::align - 1) / Storage::align;

if (needed_blocks < cur_blocks)
return construct_callback_impl<R, Args...>(s, std::move(p), std::move(f), used_storage_size, std::integral_constant<std::size_t, (cur_blocks - 1) * Storage::align>{});

// Fall back to pointer size if it won't fit in the storage area anyway
if (used_storage_size > current_size)
return construct_callback_impl<R, Args...>(s, std::move(p), callback<FS, sizeof(void*)>{std::move(f)});

// Seems we've found our size: go with it
return construct_callback_impl<R, Args...>(s, std::move(p), callback<FS, current_size>{std::move(f)});
}

template <typename R, typename... Args, typename Storage, typename LockablePtr, typename FS, std::size_t FSBS>
callback_method_invoker<R, Args...> construct_callback_impl(Storage& s, LockablePtr p, callback<FS, FSBS>&& f, const std::size_t used_storage_size)
{
return construct_callback_impl<R, Args...>(s, std::move(p), std::move(f), used_storage_size, std::integral_constant<std::size_t, Storage::size>{});
}

template <std::size_t SmallBufferSize>
class callback_storage
{
Expand Down Expand Up @@ -652,7 +702,14 @@ namespace util
|| std::is_void<R>::value
)>::type>
explicit callback(callback<FR(FArgs...), FSBS> f)
: invoker(detail::construct_callback_impl<R, Args...>(this->storage(), detail::always_valid_ptr{}, std::move(f)))
: invoker([&]() -> decltype(invoker) {
if (!f.invoker)
return nullptr;
size_t used_storage_size = 0;
f.invoker(f.data(), detail::callback_tag_determine_used_storage_size{used_storage_size});

return detail::construct_callback_impl<R, Args...>(this->storage(), detail::always_valid_ptr{}, std::move(f), used_storage_size);
}())
{
}

Expand Down

0 comments on commit 734dd27

Please sign in to comment.