Proof of concept: Cancer Stick Castle, a sigslot replacement
This needs to be followed immediately by a CL that adds unit tests for CancerStickCastle and UntypedFunction. Bug: none Change-Id: I5ade68cc4721d7442db7695f218ecd9be1d639ba Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/182460 Commit-Queue: Karl Wiberg <kwiberg@webrtc.org> Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Lahiru Ginnaliya Gamathige <glahiru@webrtc.org> Cr-Commit-Position: refs/heads/master@{#32085}
This commit is contained in:
parent
de5507d31b
commit
3d452cf710
@ -43,6 +43,23 @@ rtc_source_set("ignore_wundef") {
|
|||||||
sources = [ "ignore_wundef.h" ]
|
sources = [ "ignore_wundef.h" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rtc_source_set("function") {
|
||||||
|
sources = [ "function.h" ]
|
||||||
|
deps = [ "system:assume" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_source_set("cancer_stick_castle") {
|
||||||
|
sources = [
|
||||||
|
"cancer_stick_castle.cc",
|
||||||
|
"cancer_stick_castle.h",
|
||||||
|
]
|
||||||
|
deps = [
|
||||||
|
":function",
|
||||||
|
"../api:function_view",
|
||||||
|
"system:assume",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
# The subset of rtc_base approved for use outside of libjingle.
|
# The subset of rtc_base approved for use outside of libjingle.
|
||||||
# TODO(bugs.webrtc.org/9838): Create small and focused build targets and remove
|
# TODO(bugs.webrtc.org/9838): Create small and focused build targets and remove
|
||||||
# the old concept of rtc_base and rtc_base_approved.
|
# the old concept of rtc_base and rtc_base_approved.
|
||||||
|
|||||||
31
rtc_base/cancer_stick_castle.cc
Normal file
31
rtc_base/cancer_stick_castle.cc
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rtc_base/cancer_stick_castle.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
namespace cancer_stick_castle_impl {
|
||||||
|
|
||||||
|
CancerStickCastleReceivers::CancerStickCastleReceivers() = default;
|
||||||
|
CancerStickCastleReceivers::~CancerStickCastleReceivers() = default;
|
||||||
|
|
||||||
|
void CancerStickCastleReceivers::AddReceiverImpl(UntypedFunction* f) {
|
||||||
|
receivers_.push_back(std::move(*f));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CancerStickCastleReceivers::Foreach(
|
||||||
|
rtc::FunctionView<void(UntypedFunction&)> fv) {
|
||||||
|
for (auto& r : receivers_) {
|
||||||
|
fv(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cancer_stick_castle_impl
|
||||||
|
} // namespace webrtc
|
||||||
71
rtc_base/cancer_stick_castle.h
Normal file
71
rtc_base/cancer_stick_castle.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RTC_BASE_CANCER_STICK_CASTLE_H_
|
||||||
|
#define RTC_BASE_CANCER_STICK_CASTLE_H_
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "api/function_view.h"
|
||||||
|
#include "rtc_base/function.h"
|
||||||
|
#include "rtc_base/system/assume.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
namespace cancer_stick_castle_impl {
|
||||||
|
|
||||||
|
class CancerStickCastleReceivers {
|
||||||
|
public:
|
||||||
|
CancerStickCastleReceivers();
|
||||||
|
~CancerStickCastleReceivers();
|
||||||
|
void AddReceiver(UntypedFunction&& f) {
|
||||||
|
AddReceiverImpl(&f);
|
||||||
|
// Assume that f was moved from and is now trivially destructible.
|
||||||
|
// This helps the compiler optimize away the destructor call.
|
||||||
|
RTC_ASSUME(f.IsTriviallyDestructible());
|
||||||
|
}
|
||||||
|
void Foreach(rtc::FunctionView<void(UntypedFunction&)> fv);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void AddReceiverImpl(UntypedFunction* f);
|
||||||
|
std::vector<UntypedFunction> receivers_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace cancer_stick_castle_impl
|
||||||
|
|
||||||
|
// A collection of receivers (callable objects) that can be called all at once.
|
||||||
|
// Optimized for minimal binary size.
|
||||||
|
//
|
||||||
|
// TODO(kwiberg): Add support for removing receivers, if necessary. AddReceiver
|
||||||
|
// would have to return some sort of ID that the caller could save and then pass
|
||||||
|
// to RemoveReceiver. Alternatively, the callable objects could return one value
|
||||||
|
// if they wish to stay in the CSC and another value if they wish to be removed.
|
||||||
|
// It depends on what's convenient for the callers...
|
||||||
|
template <typename... ArgT>
|
||||||
|
class CancerStickCastle {
|
||||||
|
public:
|
||||||
|
template <typename F>
|
||||||
|
void AddReceiver(F&& f) {
|
||||||
|
receivers_.AddReceiver(
|
||||||
|
UntypedFunction::Create<void(ArgT...)>(std::forward<F>(f)));
|
||||||
|
}
|
||||||
|
void Send(ArgT... args) {
|
||||||
|
receivers_.Foreach([&](UntypedFunction& f) {
|
||||||
|
f.Call<void(ArgT...)>(std::forward<ArgT>(args)...);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
cancer_stick_castle_impl::CancerStickCastleReceivers receivers_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // RTC_BASE_CANCER_STICK_CASTLE_H_
|
||||||
202
rtc_base/function.h
Normal file
202
rtc_base/function.h
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RTC_BASE_FUNCTION_H_
|
||||||
|
#define RTC_BASE_FUNCTION_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "rtc_base/system/assume.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
namespace webrtc_function_impl {
|
||||||
|
|
||||||
|
using FunVoid = void();
|
||||||
|
|
||||||
|
union VoidUnion {
|
||||||
|
void* void_ptr;
|
||||||
|
FunVoid* fun_ptr;
|
||||||
|
typename std::aligned_storage<16>::type inline_storage;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct CallHelpers;
|
||||||
|
template <typename RetT, typename... ArgT>
|
||||||
|
struct CallHelpers<RetT(ArgT...)> {
|
||||||
|
using return_type = RetT;
|
||||||
|
template <typename F>
|
||||||
|
static RetT CallVoidPtr(VoidUnion* vu, ArgT... args) {
|
||||||
|
return (*static_cast<F*>(vu->void_ptr))(std::forward<ArgT>(args)...);
|
||||||
|
}
|
||||||
|
static RetT CallFunPtr(VoidUnion* vu, ArgT... args) {
|
||||||
|
return (reinterpret_cast<RetT (*)(ArgT...)>(vu->fun_ptr))(
|
||||||
|
std::forward<ArgT>(args)...);
|
||||||
|
}
|
||||||
|
template <typename F>
|
||||||
|
static RetT CallInlineStorage(VoidUnion* vu, ArgT... args) {
|
||||||
|
return (*reinterpret_cast<F*>(&vu->inline_storage))(
|
||||||
|
std::forward<ArgT>(args)...);
|
||||||
|
}
|
||||||
|
static RetT DoCall(FunVoid* f, VoidUnion* vu, ArgT... args) {
|
||||||
|
return reinterpret_cast<RetT (*)(VoidUnion*, ArgT...)>(f)(
|
||||||
|
vu, std::forward<ArgT>(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc_function_impl
|
||||||
|
|
||||||
|
// A class that holds (and owns) any callable. The same function call signature
|
||||||
|
// must be provided when constructing and calling the object.
|
||||||
|
//
|
||||||
|
// The point of not having the call signature as a class template parameter is
|
||||||
|
// to have one single concrete type for all signatures; this reduces binary
|
||||||
|
// size.
|
||||||
|
class UntypedFunction final {
|
||||||
|
public:
|
||||||
|
// Create function for lambdas and other callables; it accepts every type of
|
||||||
|
// argument except those noted in its enable_if call.
|
||||||
|
template <
|
||||||
|
typename Signature,
|
||||||
|
typename F,
|
||||||
|
typename std::enable_if<
|
||||||
|
// Not for function pointers; we have another overload for that below.
|
||||||
|
!std::is_function<typename std::remove_pointer<
|
||||||
|
typename std::remove_reference<F>::type>::type>::value &&
|
||||||
|
|
||||||
|
// Not for nullptr; we have another overload for that below.
|
||||||
|
!std::is_same<std::nullptr_t,
|
||||||
|
typename std::remove_cv<F>::type>::value &&
|
||||||
|
|
||||||
|
// Not for UntypedFunction objects; we have another overload for that.
|
||||||
|
!std::is_same<UntypedFunction,
|
||||||
|
typename std::remove_cv<typename std::remove_reference<
|
||||||
|
F>::type>::type>::value>::type* = nullptr>
|
||||||
|
static UntypedFunction Create(F&& f) {
|
||||||
|
using F_deref = typename std::remove_reference<F>::type;
|
||||||
|
// TODO(C++17): Use `constexpr if` here. The compiler appears to do the
|
||||||
|
// right thing anyway w.r.t. resolving the branch statically and
|
||||||
|
// eliminating dead code, but it would be good for readability.
|
||||||
|
if (std::is_trivially_move_constructible<F_deref>::value &&
|
||||||
|
std::is_trivially_destructible<F_deref>::value &&
|
||||||
|
sizeof(F_deref) <=
|
||||||
|
sizeof(webrtc_function_impl::VoidUnion::inline_storage)) {
|
||||||
|
// The callable is trivial and small enough, so we just store its bytes
|
||||||
|
// in the inline storage.
|
||||||
|
webrtc_function_impl::VoidUnion vu;
|
||||||
|
new (&vu.inline_storage) F_deref(std::forward<F>(f));
|
||||||
|
return UntypedFunction(
|
||||||
|
vu,
|
||||||
|
reinterpret_cast<webrtc_function_impl::FunVoid*>(
|
||||||
|
webrtc_function_impl::CallHelpers<
|
||||||
|
Signature>::template CallInlineStorage<F_deref>),
|
||||||
|
nullptr);
|
||||||
|
} else {
|
||||||
|
// The callable is either nontrivial or too large, so we can't keep it
|
||||||
|
// in the inline storage; use the heap instead.
|
||||||
|
return UntypedFunction(
|
||||||
|
webrtc_function_impl::VoidUnion{.void_ptr =
|
||||||
|
new F_deref(std::forward<F>(f))},
|
||||||
|
reinterpret_cast<webrtc_function_impl::FunVoid*>(
|
||||||
|
webrtc_function_impl::CallHelpers<
|
||||||
|
Signature>::template CallVoidPtr<F_deref>),
|
||||||
|
static_cast<void (*)(webrtc_function_impl::VoidUnion*)>(
|
||||||
|
[](webrtc_function_impl::VoidUnion* vu) {
|
||||||
|
// Assuming that this pointer isn't null allows the
|
||||||
|
// compiler to eliminate a null check in the (inlined)
|
||||||
|
// delete operation.
|
||||||
|
RTC_ASSUME(vu->void_ptr != nullptr);
|
||||||
|
delete reinterpret_cast<F_deref*>(vu->void_ptr);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create function that accepts function pointers. If the argument is null,
|
||||||
|
// the result is an empty UntypedFunction.
|
||||||
|
template <typename Signature>
|
||||||
|
static UntypedFunction Create(Signature* f) {
|
||||||
|
return UntypedFunction(
|
||||||
|
reinterpret_cast<webrtc_function_impl::FunVoid*>(f),
|
||||||
|
f ? reinterpret_cast<webrtc_function_impl::FunVoid*>(
|
||||||
|
webrtc_function_impl::CallHelpers<Signature>::CallFunPtr)
|
||||||
|
: nullptr,
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default constructor. Creates an empty UntypedFunction.
|
||||||
|
UntypedFunction() : call_(nullptr), delete_(nullptr) {}
|
||||||
|
|
||||||
|
// Not copyable.
|
||||||
|
UntypedFunction(const UntypedFunction&) = delete;
|
||||||
|
UntypedFunction& operator=(const UntypedFunction&) = delete;
|
||||||
|
|
||||||
|
// Move construction and assignment.
|
||||||
|
UntypedFunction(UntypedFunction&& other)
|
||||||
|
: f_(other.f_), call_(other.call_), delete_(other.delete_) {
|
||||||
|
other.delete_ = nullptr;
|
||||||
|
}
|
||||||
|
UntypedFunction& operator=(UntypedFunction&& other) {
|
||||||
|
f_ = other.f_;
|
||||||
|
call_ = other.call_;
|
||||||
|
delete_ = other.delete_;
|
||||||
|
other.delete_ = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~UntypedFunction() {
|
||||||
|
if (delete_) {
|
||||||
|
delete_(&f_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
friend void swap(UntypedFunction& a, UntypedFunction& b) {
|
||||||
|
using std::swap;
|
||||||
|
swap(a.f_, b.f_);
|
||||||
|
swap(a.call_, b.call_);
|
||||||
|
swap(a.delete_, b.delete_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if we have a function, false if we don't (i.e., we're null).
|
||||||
|
explicit operator bool() const { return call_ != nullptr; }
|
||||||
|
|
||||||
|
template <typename Signature, typename... ArgT>
|
||||||
|
typename webrtc_function_impl::CallHelpers<Signature>::return_type Call(
|
||||||
|
ArgT... args) {
|
||||||
|
return webrtc_function_impl::CallHelpers<Signature>::DoCall(
|
||||||
|
call_, &f_, std::forward<ArgT>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true iff we don't need to call a destructor. This is guaranteed
|
||||||
|
// to hold for a moved-from object.
|
||||||
|
bool IsTriviallyDestructible() { return delete_ == nullptr; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
UntypedFunction(webrtc_function_impl::VoidUnion f,
|
||||||
|
webrtc_function_impl::FunVoid* call,
|
||||||
|
void (*del)(webrtc_function_impl::VoidUnion*))
|
||||||
|
: f_(f), call_(call), delete_(del) {}
|
||||||
|
|
||||||
|
// The callable thing, or a pointer to it.
|
||||||
|
webrtc_function_impl::VoidUnion f_;
|
||||||
|
|
||||||
|
// Pointer to a dispatch function that knows the type of the callable thing
|
||||||
|
// that's stored in f_, and how to call it. An UntypedFunction object is empty
|
||||||
|
// (null) iff call_ is null.
|
||||||
|
webrtc_function_impl::FunVoid* call_;
|
||||||
|
|
||||||
|
// Pointer to a function that knows how to delete the callable thing that's
|
||||||
|
// stored in f_. Null if `f_` is trivially deletable.
|
||||||
|
void (*delete_)(webrtc_function_impl::VoidUnion*);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // RTC_BASE_FUNCTION_H_
|
||||||
@ -44,6 +44,10 @@ rtc_source_set("unused") {
|
|||||||
sources = [ "unused.h" ]
|
sources = [ "unused.h" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rtc_source_set("assume") {
|
||||||
|
sources = [ "assume.h" ]
|
||||||
|
}
|
||||||
|
|
||||||
rtc_source_set("rtc_export") {
|
rtc_source_set("rtc_export") {
|
||||||
sources = [
|
sources = [
|
||||||
"rtc_export.h",
|
"rtc_export.h",
|
||||||
|
|||||||
73
rtc_base/system/assume.h
Normal file
73
rtc_base/system/assume.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RTC_BASE_SYSTEM_ASSUME_H_
|
||||||
|
#define RTC_BASE_SYSTEM_ASSUME_H_
|
||||||
|
|
||||||
|
// Possibly evaluate `p`, promising the compiler that the result is true; the
|
||||||
|
// compiler is allowed (but not required) to use this information when
|
||||||
|
// optimizing the code. USE WITH CAUTION! If you promise the compiler things
|
||||||
|
// that aren't true, it will build a broken binary for you.
|
||||||
|
//
|
||||||
|
// As a simple example, the compiler is allowed to transform this
|
||||||
|
//
|
||||||
|
// RTC_ASSUME(x == 4);
|
||||||
|
// return x;
|
||||||
|
//
|
||||||
|
// into this
|
||||||
|
//
|
||||||
|
// return 4;
|
||||||
|
//
|
||||||
|
// It is even allowed to propagate the assumption "backwards in time", if it can
|
||||||
|
// prove that it must have held at some earlier time. For example, the compiler
|
||||||
|
// is allowed to transform this
|
||||||
|
//
|
||||||
|
// int Add(int x, int y) {
|
||||||
|
// if (x == 17)
|
||||||
|
// y += 1;
|
||||||
|
// RTC_ASSUME(x != 17);
|
||||||
|
// return x + y;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// into this
|
||||||
|
//
|
||||||
|
// int Add(int x, int y) {
|
||||||
|
// return x + y;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// since if `x` isn't 17 on the third line of the function body, the test of `x
|
||||||
|
// == 17` on the first line must fail since nothing can modify the local
|
||||||
|
// variable `x` in between.
|
||||||
|
//
|
||||||
|
// The intended use is to allow the compiler to optimize better. For example,
|
||||||
|
// here we allow the compiler to omit an instruction that ensures correct
|
||||||
|
// rounding of negative arguments:
|
||||||
|
//
|
||||||
|
// int DivBy2(int x) {
|
||||||
|
// RTC_ASSUME(x >= 0);
|
||||||
|
// return x / 2;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// and here we allow the compiler to possibly omit a null check:
|
||||||
|
//
|
||||||
|
// void Delete(int* p) {
|
||||||
|
// RTC_ASSUME(p != nullptr);
|
||||||
|
// delete p;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// clang-format off
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#define RTC_ASSUME(p) do { if (!(p)) __builtin_unreachable(); } while (0)
|
||||||
|
#else
|
||||||
|
#define RTC_ASSUME(p) do {} while (0)
|
||||||
|
#endif
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
#endif // RTC_BASE_SYSTEM_ASSUME_H_
|
||||||
Loading…
x
Reference in New Issue
Block a user