Skip to content

Instantly share code, notes, and snippets.

@alkis
Created August 27, 2018 17:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alkis/3401f5cd4606105762b3c857d16520b9 to your computer and use it in GitHub Desktop.
Save alkis/3401f5cd4606105762b3c857d16520b9 to your computer and use it in GitHub Desktop.
#ifndef XXX_COMMON_THREAD_CALLBACK_CHAIN_H_
#define XXX_COMMON_THREAD_CALLBACK_CHAIN_H_
#include <atomic>
#include <cstring>
#include <utility>
#include "third_party/absl/container/internal/container_memory.h"
#include "third_party/absl/container/internal/layout.h"
namespace xxx {
class CallbackChain {
public:
CallbackChain() { (*tail_)(nullptr); }
~CallbackChain() {
auto* sealed = Cb::Sealed();
tail_.exchange(sealed)->ContinueWith(sealed);
}
template <class Fn>
void Add(Fn&& fn) {
auto* cb = Cb::New(std::forward<Fn>(fn));
tail_.exchange(cb)->ContinueWith(cb);
}
private:
class Cb {
public:
void operator()(Cb* prev) {
(*run_)(this);
if (prev) (*prev->destroy_)(prev);
}
void ContinueWith(Cb* cb) {
if (auto* old = next_.exchange(cb)) {
(*cb)(this);
}
}
template <class Fn>
static Cb* New(Fn&& fn);
static Cb* Sealed() {
static Cb res([](void*) {}, [](void*) {});
return &res;
}
private:
Cb() = default;
Cb(void (*run)(void*), void (*destroy)(void*))
: run_(run), destroy_(destroy) {}
std::atomic<Cb*> next_ = nullptr;
void (*run_)(void*);
void (*destroy_)(void*);
// followed by: Fn fn_;
};
std::atomic<Cb*> tail_ = Cb::New([] {});
};
template <class Fn>
CallbackChain::Cb* CallbackChain::Cb::New(Fn&& fn) {
using L = absl::container_internal::Layout<Cb, Fn>;
std::allocator<char> alloc;
char* mem =
static_cast<char*>(absl::container_internal::Allocate<L::Alignment()>(
&alloc, L(1, 1).AllocSize()));
auto get_fn = [](void* mem) {
return L(1, 1).template Pointer<Fn>(static_cast<char*>(mem));
};
auto get_cb = [](void* mem) {
return L(1, 1).template Pointer<Cb>(static_cast<char*>(mem));
};
new (get_fn(mem)) Fn(std::forward<Fn>(fn));
Cb* cb = get_cb(mem);
new (cb) Cb();
cb->run_ = [](void* self) {
auto* fn = L(1, 1).template Pointer<Fn>(static_cast<char*>(self));
(*fn)();
fn->~Fn();
auto* cb = L(1, 1).template Pointer<Cb>(static_cast<char*>(self));
auto* next = cb->next_.exchange(Sealed());
if (next) (*next)(cb);
};
cb->destroy_ = [](void* self) {
auto* cb = L(1, 1).template Pointer<Cb>(static_cast<char*>(self));
cb->~Cb();
std::allocator<char> alloc;
absl::container_internal::Deallocate<L::Alignment()>(&alloc, self,
L(1, 1).AllocSize());
};
return cb;
}
} // namespace xxx
#endif // XXX_COMMON_THREAD_CALLBACK_CHAIN_H_
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment