/***********************************************************************************
 * rocket - lightweight & fast signal/slots & utility library                      *
 *                                                                                 *
 *   v2.0 - public domain                                                          *
 *   no warranty is offered or implied; use this code at your own risk             *
 *                                                                                 *
 * AUTHORS                                                                         *
 *                                                                                 *
 *   Written by Michael Bleis                                                      *
 *                                                                                 *
 *                                                                                 *
 * LICENSE                                                                         *
 *                                                                                 *
 *   This software is dual-licensed to the public domain and under the following   *
 *   license: you are granted a perpetual, irrevocable license to copy, modify,    *
 *   publish, and distribute this file as you see fit.                             *
 ***********************************************************************************/

#ifndef ROCKET_HPP_INCLUDED
#define ROCKET_HPP_INCLUDED

/***********************************************************************************
 * CONFIGURATION                                                                   *
 * ------------------------------------------------------------------------------- *
 * Define this if your compiler doesn't support std::optional.                     *
 * ------------------------------------------------------------------------------- */

#ifndef ROCKET_NO_STD_OPTIONAL
// #define ROCKET_NO_STD_OPTIONAL
#endif

/***********************************************************************************
 * ------------------------------------------------------------------------------- *
 * Define this if you want to disable exceptions.                                  *
 * ------------------------------------------------------------------------------- */

#ifndef ROCKET_NO_EXCEPTIONS
// #define ROCKET_NO_EXCEPTIONS
#endif

/***********************************************************************************
 * ------------------------------------------------------------------------------- *
 * Define this if you want to disable `set_timeout` and `set_interval` features.   *
 * ------------------------------------------------------------------------------- */

#ifndef ROCKET_NO_TIMERS
// #define ROCKET_NO_TIMERS
#endif

/***********************************************************************************
 * ------------------------------------------------------------------------------- *
 * Define this if you want to disable the connection blocking feature.             *
 * ------------------------------------------------------------------------------- */

#ifndef ROCKET_NO_BLOCKING_CONNECTIONS
// #define ROCKET_NO_BLOCKING_CONNECTIONS
#endif

/***********************************************************************************
 * ------------------------------------------------------------------------------- *
 * Redefine this if your compiler doesn't support the `thread_local`-keyword.      *
 * For Visual Studio < 2015 you can define it to `__declspec(thread)` for example. *
 * ------------------------------------------------------------------------------- */

#ifndef ROCKET_THREAD_LOCAL
#define ROCKET_THREAD_LOCAL thread_local
#endif

/***********************************************************************************
 * ------------------------------------------------------------------------------- *
 * Redefine this if your compiler doesn't support the `noexcept`-keyword.          *
 * For Visual Studio < 2015 you can define it to `throw()` for example.            *
 * ------------------------------------------------------------------------------- */

#ifndef ROCKET_NOEXCEPT
#define ROCKET_NOEXCEPT noexcept
#endif

/***********************************************************************************
 * USAGE                                                                           *
 * ------------------------------------------------------------------------------- *
 * 1. Creating your first signal                                                   *
 * ------------------------------------------------------------------------------- *

#include <iostream>

int main() {
    rocket::signal<void()> my_signal;

    // Connecting the first handler to our signal
    my_signal.connect([]() {
        std::cout << "First handler called!" << std::endl;
    });

    // Connecting a second handler to our signal using alternative syntax
    my_signal += []() {
        std::cout << "Second handler called!" << std::endl;
    };

    // Invoking the signal
    my_signal();
}

// Output:
//     First handler called!
//     Second handler called!

 * ------------------------------------------------------------------------------- *
 * 2. Passing arguments to the signal                                              *
 * ------------------------------------------------------------------------------- *

#include <string>
#include <iostream>

int main() {
    rocket::signal<void(std::string)> my_signal;

    my_signal.connect([](const std::string& argument) {
        std::cout << "Handler called with arg: " << argument << std::endl;
    });

    my_signal("Hello world");
}

// Output:
//     Handler called with arg: Hello world

 * ------------------------------------------------------------------------------- *
 * 3. Connecting class methods to the signal                                       *
 * ------------------------------------------------------------------------------- *

#include <string>
#include <iostream>

class Subject {
public:
    void setName(const std::string& newName) {
        if (name != newName) {
            name = newName;
            nameChanged(newName);
        }
    }

public:
    rocket::signal<void(std::string)> nameChanged;

private:
    std::string name;
};

class Observer {
public:
    Observer(Subject& subject) {
        // Register the `onNameChanged`-function of this object as a listener and
        // store the resultant connection object in the listener's connection set.

        // This is all your need to do for the most common case, if you want the
        // connection to be broken when the observer is destroyed.
        connections += {
            subject.nameChanged.connect(this, &Observer::onNameChanged)
        };
    }

    void onNameChanged(const std::string& name) {
        std::cout << "Subject received new name: " << name << std::endl;
    }

private:
    rocket::scoped_connection_container connections;
};

int main() {
    Subject s;
    Observer o{ s };
    s.setName("Peter");
}

// Output:
//     Subject received new name: Peter


#include <string>
#include <iostream>
#include <memory>

struct ILogger {
    virtual void logMessage(const std::string& message) = 0;
};

struct ConsoleLogger : ILogger {
    void logMessage(const std::string& message) override {
        std::cout << "New log message: " << message << std::endl;
    }
};

struct App {
    void run() {
        if (work()) {
            onSuccess("I finished my work!");
        }
    }
    bool work() {
        return true;
    }
    rocket::signal<void(std::string)> onSuccess;
};

int main() {
    std::unique_ptr<App> app = std::make_unique<App>();

    std::unique_ptr<ILogger> logger = std::make_unique<ConsoleLogger>();
    app->onSuccess.connect(logger.get(), &ILogger::logMessage);

    app->run();
}

// Output:
//     New log message: I finished my work!

 * ------------------------------------------------------------------------------- *
 * 4.a Handling lifetime and scope of connection objects                            *
 *                                                                                 *
 * What if we want to destroy our logger instance from example 3 but continue      *
 * to use the app instance?                                                        *
 *                                                                                 *
 * Solution: We use `scoped_connection`-objects to track our connected slots!      *
 * ------------------------------------------------------------------------------- *

// [...] (See example 3)

int main() {
    std::unique_ptr<App> app = std::make_unique<App>();
    {
        std::unique_ptr<ILogger> logger = std::make_unique<ConsoleLogger>();

        rocket::scoped_connection connection = app->onSuccess
            .connect(logger.get(), &ILogger::logMessage);

        app->run();

    } //<-- `logger`-instance is destroyed at the end of this block
      //<-- The `connection`-object is also destroyed here
      //        and therefore removed from App::onSuccess.

    // Run the app a second time
    //
    // This would normally cause a crash / undefined behavior because the logger
    // instance is destroyed at this point, but App::onSuccess still referenced it
    // in example 3.

    app->run();
}

// Output:
//     New log message: I finished my work!

 * ------------------------------------------------------------------------------- *
 * 4.b Advanced lifetime tracking                                                  *
 *                                                                                 *
 * The library can also track the lifetime of your class objects for you, if the   *
 * connected slot instances inherit from the `rocket::trackable` base class.       *
 * ------------------------------------------------------------------------------- *

 // [...] (See example 3)

struct ILogger : rocket::trackable {
    virtual void logMessage(const std::string& message) = 0;
};

// [...] (See example 3)

int main() {
    std::unique_ptr<App> app = std::make_unique<App>();
    {
        std::unique_ptr<ILogger> logger = std::make_unique<ConsoleLogger>();
        app->onSuccess.connect(logger.get(), &ILogger::logMessage);

        app->run();

    } //<-- `logger`-instance is destroyed at the end of this block

      //<-- Because `ILogger` inherits from `rocket::trackable`, the signal knows
      //        about its destruction and will automatically disconnect the slot!

    // Run the app a second time
    //
    // This would normally cause a crash / undefined behavior because the logger
    // instance is destroyed at this point, but App::onSuccess still referenced it
    // in example 3.

    app->run();
}

 * ------------------------------------------------------------------------------- *
 * 5. Getting return values from a call to a signal                                *
 *                                                                                 *
 * Slots can also return values to the emitting signal.                            *
 * Because a signal can have several slots attached to it, the return values are   *
 * collected by using the so called `value collectors`.                            *
 *                                                                                 *
 * The default value collector returns an `optional<T>` from a call to a           *
 * `signal<T(...)>::operator()`                                                    *
 *                                                                                 *
 * However, this behaviour can be overriden at declaration time of the signal as   *
 * well as during signal invocation.                                               *
 * ------------------------------------------------------------------------------- *

#include <cmath>
#include <iostream>

int main() {
    rocket::signal<int(int)> signal;

    // The library supports argument and return type transformation between the
    // signal and the slots. We show this by connecting the `float sqrtf(float)`
    // function to a signal with an `int` argument and `int` return value.

    signal.connect(std::sqrtf);

    std::cout << "Computed value: " << *signal(16);
}

// Output:
//     Computed value: 4

#include <cmath>
#include <iostream>
#include <iomanip>

int main() {
    // Because we set `rocket::range` as the value collector for this signal
    // calling operator() now returns the return values of all connected slots.

    rocket::signal<float(float), rocket::range<float>> signal;

    // Lets connect a couple more functions to our signal and print all the
    // return values.

    signal.connect(std::sinf);
    signal.connect(std::cosf);

    std::cout << std::fixed << std::setprecision(2);

    for (auto result : signal(3.14159)) {
        std::cout << result << std::endl;
    }

    // We can also override the return value collector at invocation time

    std::cout << "First return value: " << signal.invoke<rocket::first<float>>(3.14159);
    std::cout << std::endl;
    std::cout << "Last return value: " << signal.invoke<rocket::last<float>>(3.14159);
}

// Output:
//     0.00
//     -1.00
//     First return value: 0.00
//     Last return value: -1.00

 * ------------------------------------------------------------------------------- *
 * 6. Accessing the current connection object inside a slot                        *
 *                                                                                 *
 * Sometimes it is desirable to get an instance to the current connection object   *
 * inside of a slot function. An example would be if you want to make a callback   *
 * that only fires once and then disconnects itself from the signal that called it *
 * ------------------------------------------------------------------------------- *

#include <iostream>

int main() {
    rocket::signal<void()> signal;

    signal.connect([] {
        std::cout << "Slot called. Now disconnecting..." << std::endl;

        // `current_connection` is stored in thread-local-storage.
        rocket::current_connection().disconnect();
    });

    signal();
    signal();
    signal();
}

// Output:
//     Slot called. Now disconnecting...

 * ------------------------------------------------------------------------------- *
 * 7. Preemtively aborting the emission of a signal                                *
 *                                                                                 *
 * A slot can preemtively abort the emission of a signal if it needs to.           *
 * This is useful in scenarios where your slot functions try to find some value    *
 * and you just want the result of the first slot that found one and stop other    *
 * slots from running.                                                             *
 * ------------------------------------------------------------------------------- *

#include <iostream>

int main() {
    rocket::signal<void()> signal;

    signal.connect([] {
        std::cout << "First slot called. Aborting emission of other slots." << std::endl;

        rocket::abort_emission();

        // Notice that this doesn't disconnect the other slots. It just breaks out of the
        // signal emitting loop.
    });

    signal.connect([] {
        std::cout << "Second slot called. Should never happen." << std::endl;
    });

    signal();
}

// Output:
//     First slot called. Aborting emission of other slots.














 ***********************************************************************************
 * BEGIN IMPLEMENTATION                                                            *
 * ------------------------------------------------------------------------------- *
 * Do not change anything below this line                                          *
 * ------------------------------------------------------------------------------- *
 ***********************************************************************************/

#include <iterator>
#include <exception>
#include <type_traits>
#include <cassert>
#include <utility>
#include <memory>
#include <functional>
#include <list>
#include <forward_list>
#include <initializer_list>
#include <atomic>
#include <limits>
#include <mutex>
#include <future>
#include <unordered_map>
#include <deque>
#include <chrono>
#include <tuple>

#ifndef ROCKET_NO_STD_OPTIONAL
#include <optional>
#endif

#if __has_cpp_attribute(likely)
#define ROCKET_LIKELY [[likely]]
#else
#define ROCKET_LIKELY
#endif

#if __has_cpp_attribute(unlikely)
#define ROCKET_UNLIKELY [[unlikely]]
#else
#define ROCKET_UNLIKELY
#endif

#if __has_cpp_attribute(no_unique_address)
#define ROCKET_NO_UNIQUE_ADDRESS [[no_unique_address]]
#else
#define ROCKET_NO_UNIQUE_ADDRESS
#endif

namespace rocket
{
template <class T>
struct minimum
{
    using value_type = T;
    using result_type = T;

    template <class U>
    void operator() (U&& value)
    {
        if (! has_value || value < current)
        {
            current = std::forward<U> (value);
            has_value = true;
        }
    }

    result_type result()
    {
        return std::move (current);
    }

private:
    value_type current {};
    bool has_value { false };
};

template <class T>
struct maximum
{
    using value_type = T;
    using result_type = T;

    template <class U>
    void operator() (U&& value)
    {
        if (! has_value || value > current)
        {
            current = std::forward<U> (value);
            has_value = true;
        }
    }

    result_type result()
    {
        return std::move (current);
    }

private:
    value_type current {};
    bool has_value { false };
};

template <class T>
struct first
{
    using value_type = T;
    using result_type = T;

    template <class U>
    void operator() (U&& value)
    {
        if (! has_value)
        {
            current = std::forward<U> (value);
            has_value = true;
        }
    }

    result_type result()
    {
        return std::move (current);
    }

private:
    value_type current {};
    bool has_value { false };
};

template <class T>
struct last
{
    using value_type = T;
    using result_type = T;

    template <class U>
    void operator() (U&& value)
    {
        current = std::forward<U> (value);
    }

    result_type result()
    {
        return std::move (current);
    }

private:
    value_type current {};
};

template <class T>
struct range
{
    using value_type = T;
    using result_type = std::list<T>;

    template <class U>
    void operator() (U&& value)
    {
        values.emplace_back (std::forward<U> (value));
    }

    result_type result()
    {
        return std::move (values);
    }

private:
    std::list<value_type> values;
};

#ifndef ROCKET_NO_EXCEPTIONS
struct error : std::exception
{
};

struct bad_optional_access final : error
{
    const char* what() const ROCKET_NOEXCEPT override
    {
        return "rocket: Bad optional access.";
    }
};

struct invocation_slot_error final : error
{
    const char* what() const ROCKET_NOEXCEPT override
    {
        return "rocket: One of the slots has raised an exception during the signal invocation.";
    }
};
#endif

#ifdef ROCKET_NO_STD_OPTIONAL
template <class T>
struct optional final
{
    using value_type = T;

    optional() ROCKET_NOEXCEPT = default;

    ~optional() ROCKET_NOEXCEPT
    {
        if (engaged())
        {
            disengage();
        }
    }

    template <class... Args>
    explicit optional (Args&&... args)
    {
        engage (std::forward<Args> (args)...);
    }

    optional (optional const& opt)
    {
        if (opt.engaged())
        {
            engage (*opt.object());
        }
    }

    optional (optional&& opt)
    {
        if (opt.engaged())
        {
            engage (std::move (*opt.object()));
            opt.disengage();
        }
    }

    template <class U>
    explicit optional (optional<U> const& opt)
    {
        if (opt.engaged())
        {
            engage (*opt.object());
        }
    }

    template <class U>
    explicit optional (optional<U>&& opt)
    {
        if (opt.engaged())
        {
            engage (std::move (*opt.object()));
            opt.disengage();
        }
    }

    template <class U>
    optional& operator= (U&& rhs)
    {
        if (engaged())
        {
            disengage();
        }
        engage (std::forward<U> (rhs));
        return *this;
    }

    optional& operator= (optional const& rhs)
    {
        if (this != &rhs)
        {
            if (engaged())
            {
                disengage();
            }
            if (rhs.engaged())
            {
                engage (*rhs.object());
            }
        }
        return *this;
    }

    template <class U>
    optional& operator= (optional<U> const& rhs)
    {
        if (this != &rhs)
        {
            if (engaged())
            {
                disengage();
            }
            if (rhs.engaged())
            {
                engage (*rhs.object());
            }
        }
        return *this;
    }

    optional& operator= (optional&& rhs)
    {
        if (engaged())
        {
            disengage();
        }
        if (rhs.engaged())
        {
            engage (std::move (*rhs.object()));
            rhs.disengage();
        }
        return *this;
    }

    template <class U>
    optional& operator= (optional<U>&& rhs)
    {
        if (engaged())
        {
            disengage();
        }
        if (rhs.engaged())
        {
            engage (std::move (*rhs.object()));
            rhs.disengage();
        }
        return *this;
    }

    void reset() ROCKET_NOEXCEPT
    {
        if (engaged())
        {
            disengage();
        }
    }

    template <class... Args>
    value_type& emplace (Args&&... args)
    {
        if (engaged())
        {
            disengage();
        }
        engage (std::forward<Args> (args)...);
        return value();
    }

    bool engaged() const ROCKET_NOEXCEPT
    {
        return initialized;
    }

    bool has_value() const ROCKET_NOEXCEPT
    {
        return initialized;
    }

    explicit operator bool() const ROCKET_NOEXCEPT
    {
        return engaged();
    }

    value_type& operator*() ROCKET_NOEXCEPT
    {
        return value();
    }

    value_type const& operator*() const ROCKET_NOEXCEPT
    {
        return value();
    }

    value_type* operator->() ROCKET_NOEXCEPT
    {
        return object();
    }

    value_type const* operator->() const ROCKET_NOEXCEPT
    {
        return object();
    }

    value_type& value()
    {
#ifndef ROCKET_NO_EXCEPTIONS
        if (! engaged())
        {
            throw bad_optional_access {};
        }
#endif
        return *object();
    }

    value_type const& value() const
    {
#ifndef ROCKET_NO_EXCEPTIONS
        if (! engaged())
        {
            throw bad_optional_access {};
        }
#endif
        return *object();
    }

    template <class U>
    value_type value_or (U&& val) const
    {
        return engaged() ? *object() : value_type { std::forward<U> (val) };
    }

    void swap (optional& other)
    {
        if (this != &other)
        {
            auto t { std::move (*this) };
            *this = std::move (other);
            other = std::move (t);
        }
    }

private:
    void* storage() ROCKET_NOEXCEPT
    {
        return static_cast<void*> (&buffer);
    }

    void const* storage() const ROCKET_NOEXCEPT
    {
        return static_cast<void const*> (&buffer);
    }

    value_type* object() ROCKET_NOEXCEPT
    {
        assert (initialized == true);
        return static_cast<value_type*> (storage());
    }

    value_type const* object() const ROCKET_NOEXCEPT
    {
        assert (initialized == true);
        return static_cast<value_type const*> (storage());
    }

    template <class... Args>
    void engage (Args&&... args)
    {
        assert (initialized == false);
        new (storage()) value_type { std::forward<Args> (args)... };
        initialized = true;
    }

    void disengage() ROCKET_NOEXCEPT
    {
        assert (initialized == true);
        object()->~value_type();
        initialized = false;
    }

    bool initialized = false;
    std::aligned_storage_t<sizeof (value_type), alignof (value_type)> buffer;
};
#else
template <class T>
using optional = std::optional<T>;
#endif

template <class T>
struct intrusive_ptr final
{
    using value_type = T;
    using element_type = T;
    using pointer = T*;
    using reference = T&;

    template <class U>
    friend struct intrusive_ptr;

    constexpr intrusive_ptr() ROCKET_NOEXCEPT
        : ptr { nullptr }
    {
    }

    constexpr intrusive_ptr (std::nullptr_t) ROCKET_NOEXCEPT
        : ptr { nullptr }
    {
    }

    explicit intrusive_ptr (pointer p) ROCKET_NOEXCEPT
        : ptr { p }
    {
        if (ptr)
        {
            ptr->addref();
        }
    }

    intrusive_ptr (intrusive_ptr const& p) ROCKET_NOEXCEPT
        : ptr { p.ptr }
    {
        if (ptr)
        {
            ptr->addref();
        }
    }

    intrusive_ptr (intrusive_ptr&& p) ROCKET_NOEXCEPT
        : ptr { p.ptr }
    {
        p.ptr = nullptr;
    }

    template <class U>
    explicit intrusive_ptr (intrusive_ptr<U> const& p) ROCKET_NOEXCEPT
        : ptr { p.ptr }
    {
        if (ptr)
        {
            ptr->addref();
        }
    }

    template <class U>
    explicit intrusive_ptr (intrusive_ptr<U>&& p) ROCKET_NOEXCEPT
        : ptr { p.ptr }
    {
        p.ptr = nullptr;
    }

    ~intrusive_ptr() ROCKET_NOEXCEPT
    {
        if (ptr)
        {
            ptr->release();
        }
    }

    pointer get() const ROCKET_NOEXCEPT
    {
        return ptr;
    }

    pointer get() const volatile ROCKET_NOEXCEPT
    {
        return ptr;
    }

    pointer detach() ROCKET_NOEXCEPT
    {
        pointer p = ptr;
        ptr = nullptr;
        return p;
    }

    operator pointer() const ROCKET_NOEXCEPT
    {
        return ptr;
    }

    operator pointer() const volatile ROCKET_NOEXCEPT
    {
        return ptr;
    }

    pointer operator->() const ROCKET_NOEXCEPT
    {
        assert (ptr != nullptr);
        return ptr;
    }

    reference operator*() const ROCKET_NOEXCEPT
    {
        assert (ptr != nullptr);
        return *ptr;
    }

    pointer* operator&() ROCKET_NOEXCEPT
    {
        assert (ptr == nullptr);
        return &ptr;
    }

    pointer const* operator&() const ROCKET_NOEXCEPT
    {
        return &ptr;
    }

    intrusive_ptr& operator= (pointer p) ROCKET_NOEXCEPT
    {
        if (p)
        {
            p->addref();
        }
        pointer o = ptr;
        ptr = p;
        if (o)
        {
            o->release();
        }
        return *this;
    }

    intrusive_ptr& operator= (std::nullptr_t) ROCKET_NOEXCEPT
    {
        if (ptr)
        {
            ptr->release();
            ptr = nullptr;
        }
        return *this;
    }

    intrusive_ptr volatile& operator= (std::nullptr_t) volatile ROCKET_NOEXCEPT
    {
        if (ptr)
        {
            ptr->release();
            ptr = nullptr;
        }
        return *this;
    }

    intrusive_ptr& operator= (intrusive_ptr const& p) ROCKET_NOEXCEPT
    {
        return (*this = p.ptr);
    }

    intrusive_ptr& operator= (intrusive_ptr&& p) ROCKET_NOEXCEPT
    {
        if (ptr)
        {
            ptr->release();
        }
        ptr = p.ptr;
        p.ptr = nullptr;
        return *this;
    }

    template <class U>
    intrusive_ptr& operator= (intrusive_ptr<U> const& p) ROCKET_NOEXCEPT
    {
        return (*this = p.ptr);
    }

    template <class U>
    intrusive_ptr& operator= (intrusive_ptr<U>&& p) ROCKET_NOEXCEPT
    {
        if (ptr)
        {
            ptr->release();
        }
        ptr = p.ptr;
        p.ptr = nullptr;
        return *this;
    }

    void swap (pointer* pp) ROCKET_NOEXCEPT
    {
        pointer p = ptr;
        ptr = *pp;
        *pp = p;
    }

    void swap (intrusive_ptr& p) ROCKET_NOEXCEPT
    {
        swap (&p.ptr);
    }

private:
    pointer ptr;
};

template <class T, class U>
inline bool operator== (intrusive_ptr<T> const& a, intrusive_ptr<U> const& b) ROCKET_NOEXCEPT
{
    return a.get() == b.get();
}

template <class T, class U>
inline bool operator== (intrusive_ptr<T> const& a, U* b) ROCKET_NOEXCEPT
{
    return a.get() == b;
}

template <class T, class U>
inline bool operator== (T* a, intrusive_ptr<U> const& b) ROCKET_NOEXCEPT
{
    return a == b.get();
}

template <class T, class U>
inline bool operator!= (intrusive_ptr<T> const& a, intrusive_ptr<U> const& b) ROCKET_NOEXCEPT
{
    return a.get() != b.get();
}

template <class T, class U>
inline bool operator!= (intrusive_ptr<T> const& a, U* b) ROCKET_NOEXCEPT
{
    return a.get() != b;
}

template <class T, class U>
inline bool operator!= (T* a, intrusive_ptr<U> const& b) ROCKET_NOEXCEPT
{
    return a != b.get();
}

template <class T, class U>
inline bool operator< (intrusive_ptr<T> const& a, intrusive_ptr<U> const& b) ROCKET_NOEXCEPT
{
    return a.get() < b.get();
}

template <class T, class U>
inline bool operator< (intrusive_ptr<T> const& a, U* b) ROCKET_NOEXCEPT
{
    return a.get() < b;
}

template <class T, class U>
inline bool operator< (T* a, intrusive_ptr<U> const& b) ROCKET_NOEXCEPT
{
    return a < b.get();
}

template <class T, class U>
inline bool operator<= (intrusive_ptr<T> const& a, intrusive_ptr<U> const& b) ROCKET_NOEXCEPT
{
    return a.get() <= b.get();
}

template <class T, class U>
inline bool operator<= (intrusive_ptr<T> const& a, U* b) ROCKET_NOEXCEPT
{
    return a.get() <= b;
}

template <class T, class U>
inline bool operator<= (T* a, intrusive_ptr<U> const& b) ROCKET_NOEXCEPT
{
    return a <= b.get();
}

template <class T, class U>
inline bool operator> (intrusive_ptr<T> const& a, intrusive_ptr<U> const& b) ROCKET_NOEXCEPT
{
    return a.get() > b.get();
}

template <class T, class U>
inline bool operator> (intrusive_ptr<T> const& a, U* b) ROCKET_NOEXCEPT
{
    return a.get() > b;
}

template <class T, class U>
inline bool operator> (T* a, intrusive_ptr<U> const& b) ROCKET_NOEXCEPT
{
    return a > b.get();
}

template <class T, class U>
inline bool operator>= (intrusive_ptr<T> const& a, intrusive_ptr<U> const& b) ROCKET_NOEXCEPT
{
    return a.get() >= b.get();
}

template <class T, class U>
inline bool operator>= (intrusive_ptr<T> const& a, U* b) ROCKET_NOEXCEPT
{
    return a.get() >= b;
}

template <class T, class U>
inline bool operator>= (T* a, intrusive_ptr<U> const& b) ROCKET_NOEXCEPT
{
    return a >= b.get();
}

template <class T>
inline bool operator== (intrusive_ptr<T> const& a, std::nullptr_t) ROCKET_NOEXCEPT
{
    return a.get() == nullptr;
}

template <class T>
inline bool operator== (std::nullptr_t, intrusive_ptr<T> const& b) ROCKET_NOEXCEPT
{
    return nullptr == b.get();
}

template <class T>
inline bool operator!= (intrusive_ptr<T> const& a, std::nullptr_t) ROCKET_NOEXCEPT
{
    return a.get() != nullptr;
}

template <class T>
inline bool operator!= (std::nullptr_t, intrusive_ptr<T> const& b) ROCKET_NOEXCEPT
{
    return nullptr != b.get();
}

template <class T>
inline T* get_pointer (intrusive_ptr<T> const& p) ROCKET_NOEXCEPT
{
    return p.get();
}

template <class T, class U>
inline intrusive_ptr<U> static_pointer_cast (intrusive_ptr<T> const& p) ROCKET_NOEXCEPT
{
    return intrusive_ptr<U> { static_cast<U*> (p.get()) };
}

template <class T, class U>
inline intrusive_ptr<U> const_pointer_cast (intrusive_ptr<T> const& p) ROCKET_NOEXCEPT
{
    return intrusive_ptr<U> { const_cast<U*> (p.get()) };
}

template <class T, class U>
inline intrusive_ptr<U> dynamic_pointer_cast (intrusive_ptr<T> const& p) ROCKET_NOEXCEPT
{
    return intrusive_ptr<U> { dynamic_cast<U*> (p.get()) };
}

struct ref_count final
{
    unsigned long addref() ROCKET_NOEXCEPT
    {
        return ++count;
    }

    unsigned long release() ROCKET_NOEXCEPT
    {
        return --count;
    }

    unsigned long get() const ROCKET_NOEXCEPT
    {
        return count;
    }

private:
    unsigned long count { 0 };
};

struct ref_count_atomic final
{
    unsigned long addref() ROCKET_NOEXCEPT
    {
        return ++count;
    }

    unsigned long release() ROCKET_NOEXCEPT
    {
        return --count;
    }

    unsigned long get() const ROCKET_NOEXCEPT
    {
        return count.load (std::memory_order_relaxed);
    }

private:
    std::atomic<unsigned long> count { 0 };
};

template <class Class, class RefCount = ref_count>
struct ref_counted
{
    ref_counted() ROCKET_NOEXCEPT = default;

    ref_counted (ref_counted const&) ROCKET_NOEXCEPT
    {
    }

    ref_counted& operator= (ref_counted const&) ROCKET_NOEXCEPT
    {
        return *this;
    }

    void addref() ROCKET_NOEXCEPT
    {
        count.addref();
    }

    void release() ROCKET_NOEXCEPT
    {
        if (count.release() == 0)
        {
            delete static_cast<Class*> (this);
        }
    }

protected:
    ~ref_counted() ROCKET_NOEXCEPT = default;

private:
    RefCount count {};
};

template <class T>
class stable_list final
{
    struct link_element final : ref_counted<link_element>
    {
        link_element() ROCKET_NOEXCEPT = default;

        ~link_element() ROCKET_NOEXCEPT
        {
            if (next)
            { // If we have a next element upon destruction
                value()->~T(); // then this link is used, else it's a dummy
            }
        }

        template <class... Args>
        void construct (Args&&... args)
        {
            new (storage()) T { std::forward<Args> (args)... };
        }

        T* value() ROCKET_NOEXCEPT
        {
            return static_cast<T*> (storage());
        }

        void* storage() ROCKET_NOEXCEPT
        {
            return static_cast<void*> (&buffer);
        }

        intrusive_ptr<link_element> next;
        intrusive_ptr<link_element> prev;

        std::aligned_storage_t<sizeof (T), alignof (T)> buffer;
    };

    intrusive_ptr<link_element> head;
    intrusive_ptr<link_element> tail;

    std::size_t elements;

public:
    template <class U>
    struct iterator_base final
    {
        using iterator_category = std::bidirectional_iterator_tag;
        using value_type = std::remove_const_t<U>;
        using difference_type = ptrdiff_t;
        using reference = U&;
        using pointer = U*;

        template <class V>
        friend class stable_list;

        iterator_base() ROCKET_NOEXCEPT = default;
        ~iterator_base() ROCKET_NOEXCEPT = default;

        iterator_base (iterator_base const& i) ROCKET_NOEXCEPT
            : element { i.element }
        {
        }

        iterator_base (iterator_base&& i) ROCKET_NOEXCEPT
            : element { std::move (i.element) }
        {
        }

        template <class V>
        explicit iterator_base (iterator_base<V> const& i) ROCKET_NOEXCEPT
            : element { i.element }
        {
        }

        template <class V>
        explicit iterator_base (iterator_base<V>&& i) ROCKET_NOEXCEPT
            : element { std::move (i.element) }
        {
        }

        iterator_base& operator= (iterator_base const& i) ROCKET_NOEXCEPT
        {
            element = i.element;
            return *this;
        }

        iterator_base& operator= (iterator_base&& i) ROCKET_NOEXCEPT
        {
            element = std::move (i.element);
            return *this;
        }

        template <class V>
        iterator_base& operator= (iterator_base<V> const& i) ROCKET_NOEXCEPT
        {
            element = i.element;
            return *this;
        }

        template <class V>
        iterator_base& operator= (iterator_base<V>&& i) ROCKET_NOEXCEPT
        {
            element = std::move (i.element);
            return *this;
        }

        iterator_base& operator++() ROCKET_NOEXCEPT
        {
            element = element->next;
            return *this;
        }

        iterator_base operator++ (int) ROCKET_NOEXCEPT
        {
            iterator_base i { *this };
            ++(*this);
            return i;
        }

        iterator_base& operator--() ROCKET_NOEXCEPT
        {
            element = element->prev;
            return *this;
        }

        iterator_base operator-- (int) ROCKET_NOEXCEPT
        {
            iterator_base i { *this };
            --(*this);
            return i;
        }

        reference operator*() const ROCKET_NOEXCEPT
        {
            return *element->value();
        }

        pointer operator->() const ROCKET_NOEXCEPT
        {
            return element->value();
        }

        template <class V>
        bool operator== (iterator_base<V> const& i) const ROCKET_NOEXCEPT
        {
            return element == i.element;
        }

        template <class V>
        bool operator!= (iterator_base<V> const& i) const ROCKET_NOEXCEPT
        {
            return element != i.element;
        }

    private:
        intrusive_ptr<link_element> element;

        iterator_base (link_element* p) ROCKET_NOEXCEPT
            : element { p }
        {
        }
    };

    using value_type = T;
    using reference = T&;
    using pointer = T*;
    using const_pointer = const T*;

    using size_type = std::size_t;
    using difference_type = std::ptrdiff_t;

    using iterator = iterator_base<T>;
    using const_iterator = iterator_base<T const>;
    using reverse_iterator = std::reverse_iterator<iterator>;
    using const_reverse_iterator = std::reverse_iterator<const_iterator>;

    stable_list()
    {
        init();
    }

    ~stable_list()
    {
        destroy();
    }

    stable_list (stable_list const& l)
    {
        init();
        insert (end(), l.begin(), l.end());
    }

    stable_list (stable_list&& l)
        : head { std::move (l.head) }, tail { std::move (l.tail) }, elements { l.elements }
    {
        l.init();
    }

    stable_list (std::initializer_list<value_type> l)
    {
        init();
        insert (end(), l.begin(), l.end());
    }

    template <class Iterator>
    stable_list (Iterator ibegin, Iterator iend)
    {
        init();
        insert (end(), ibegin, iend);
    }

    explicit stable_list (size_type count, value_type const& value)
    {
        init();
        insert (end(), count, value);
    }

    explicit stable_list (size_type count)
    {
        init();
        insert (end(), count, value_type {});
    }

    stable_list& operator= (stable_list const& l)
    {
        if (this != &l)
        {
            clear();
            insert (end(), l.begin(), l.end());
        }
        return *this;
    }

    stable_list& operator= (stable_list&& l)
    {
        destroy();
        head = std::move (l.head);
        tail = std::move (l.tail);
        elements = l.elements;
        l.init();
        return *this;
    }

    iterator begin() ROCKET_NOEXCEPT
    {
        return iterator { head->next };
    }

    iterator end() ROCKET_NOEXCEPT
    {
        return iterator { tail };
    }

    const_iterator begin() const ROCKET_NOEXCEPT
    {
        return const_iterator { head->next };
    }

    const_iterator end() const ROCKET_NOEXCEPT
    {
        return const_iterator { tail };
    }

    const_iterator cbegin() const ROCKET_NOEXCEPT
    {
        return const_iterator { head->next };
    }

    const_iterator cend() const ROCKET_NOEXCEPT
    {
        return const_iterator { tail };
    }

    reverse_iterator rbegin() ROCKET_NOEXCEPT
    {
        return reverse_iterator { end() };
    }

    reverse_iterator rend() ROCKET_NOEXCEPT
    {
        return reverse_iterator { begin() };
    }

    const_reverse_iterator rbegin() const ROCKET_NOEXCEPT
    {
        return const_reverse_iterator { cend() };
    }

    const_reverse_iterator rend() const ROCKET_NOEXCEPT
    {
        return const_reverse_iterator { cbegin() };
    }

    const_reverse_iterator crbegin() const ROCKET_NOEXCEPT
    {
        return const_reverse_iterator { cend() };
    }

    const_reverse_iterator crend() const ROCKET_NOEXCEPT
    {
        return const_reverse_iterator { cbegin() };
    }

    reference front() ROCKET_NOEXCEPT
    {
        return *begin();
    }

    reference back() ROCKET_NOEXCEPT
    {
        return *rbegin();
    }

    value_type const& front() const ROCKET_NOEXCEPT
    {
        return *cbegin();
    }

    value_type const& back() const ROCKET_NOEXCEPT
    {
        return *crbegin();
    }

    bool empty() const ROCKET_NOEXCEPT
    {
        return cbegin() == cend();
    }

    void clear() ROCKET_NOEXCEPT
    {
        erase (begin(), end());
    }

    void push_front (value_type const& value)
    {
        insert (begin(), value);
    }

    void push_front (value_type&& value)
    {
        insert (begin(), std::move (value));
    }

    void push_back (value_type const& value)
    {
        insert (end(), value);
    }

    void push_back (value_type&& value)
    {
        insert (end(), std::move (value));
    }

    template <class... Args>
    reference emplace_front (Args&&... args)
    {
        return *emplace (begin(), std::forward<Args> (args)...);
    }

    template <class... Args>
    reference emplace_back (Args&&... args)
    {
        return *emplace (end(), std::forward<Args> (args)...);
    }

    void pop_front() ROCKET_NOEXCEPT
    {
        head->next = head->next->next;
        head->next->prev = head;
        --elements;
    }

    void pop_back() ROCKET_NOEXCEPT
    {
        tail->prev = tail->prev->prev;
        tail->prev->next = tail;
        --elements;
    }

    iterator insert (iterator const& pos, value_type const& value)
    {
        return iterator { make_link (pos.element, value) };
    }

    iterator insert (iterator const& pos, value_type&& value)
    {
        return iterator { make_link (pos.element, std::move (value)) };
    }

    template <class Iterator>
    iterator insert (iterator const& pos, Iterator ibegin, Iterator iend)
    {
        iterator iter { end() };
        while (ibegin != iend)
        {
            iterator tmp { insert (pos, *ibegin++) };
            if (iter == end())
            {
                iter = std::move (tmp);
            }
        }
        return iter;
    }

    iterator insert (iterator const& pos, std::initializer_list<value_type> l)
    {
        return insert (pos, l.begin(), l.end());
    }

    iterator insert (iterator const& pos, size_type count, value_type const& value)
    {
        iterator iter { end() };
        for (size_type i = 0; i < count; ++i)
        {
            iterator tmp { insert (pos, value) };
            if (iter == end())
            {
                iter = std::move (tmp);
            }
        }
        return iter;
    }

    template <class... Args>
    iterator emplace (iterator const& pos, Args&&... args)
    {
        return iterator { make_link (pos.element, std::forward<Args> (args)...) };
    }

    void append (value_type const& value)
    {
        insert (end(), value);
    }

    void append (value_type&& value)
    {
        insert (end(), std::move (value));
    }

    template <class Iterator>
    void append (Iterator ibegin, Iterator iend)
    {
        insert (end(), ibegin, iend);
    }

    void append (std::initializer_list<value_type> l)
    {
        insert (end(), std::move (l));
    }

    void append (size_type count, value_type const& value)
    {
        insert (end(), count, value);
    }

    void assign (size_type count, value_type const& value)
    {
        clear();
        append (count, value);
    }

    template <class Iterator>
    void assign (Iterator ibegin, Iterator iend)
    {
        clear();
        append (ibegin, iend);
    }

    void assign (std::initializer_list<value_type> l)
    {
        clear();
        append (std::move (l));
    }

    void resize (size_type count)
    {
        resize (count, value_type {});
    }

    void resize (size_type count, value_type const& value)
    {
        size_type cursize = size();
        if (count > cursize)
        {
            for (size_type i = cursize; i < count; ++i)
            {
                push_back (value);
            }
        }
        else
        {
            for (size_type i = count; i < cursize; ++i)
            {
                pop_back();
            }
        }
    }

    size_type size() const ROCKET_NOEXCEPT
    {
        return elements;
    }

    size_type max_size() const ROCKET_NOEXCEPT
    {
        return std::numeric_limits<size_type>::max();
    }

    iterator erase (iterator const& pos) ROCKET_NOEXCEPT
    {
        pos.element->prev->next = pos.element->next;
        pos.element->next->prev = pos.element->prev;
        --elements;
        return iterator { pos.element->next };
    }

    iterator erase (iterator const& first, iterator const& last) ROCKET_NOEXCEPT
    {
        auto link = first.element;
        while (link != last.element)
        {
            auto next = link->next;
            link->prev = first.element->prev;
            link->next = last.element;
            --elements;
            link = std::move (next);
        }

        first.element->prev->next = last.element;
        last.element->prev = first.element->prev;
        return last;
    }

    void remove (value_type const& value) ROCKET_NOEXCEPT
    {
        for (auto itr = begin(); itr != end(); ++itr)
        {
            if (*itr == value)
            {
                erase (itr);
            }
        }
    }

    template <class Predicate>
    void remove_if (Predicate const& pred)
    {
        for (auto itr = begin(); itr != end(); ++itr)
        {
            if (pred (*itr))
            {
                erase (itr);
            }
        }
    }

    void swap (stable_list& other) ROCKET_NOEXCEPT
    {
        if (this != &other)
        {
            head.swap (other.head);
            tail.swap (other.tail);
            std::swap (elements, other.elements);
        }
    }

private:
    void init()
    {
        head = new link_element;
        tail = new link_element;
        head->next = tail;
        tail->prev = head;
        elements = 0;
    }

    void destroy()
    {
        clear();
        head->next = nullptr;
        tail->prev = nullptr;
    }

    template <class... Args>
    link_element* make_link (link_element* l, Args&&... args)
    {
        intrusive_ptr<link_element> link { new link_element };
        link->construct (std::forward<Args> (args)...);
        link->prev = l->prev;
        link->next = l;
        link->prev->next = link;
        link->next->prev = link;
        ++elements;
        return link;
    }
};

template <bool ThreadSafe>
struct threading_policy
{
    const bool is_thread_safe { ThreadSafe };
};

struct thread_safe_policy : threading_policy<true>
{
};
struct thread_unsafe_policy : threading_policy<false>
{
};

namespace detail
{
    template <class>
    struct expand_signature;

    template <class R, class... Args>
    struct expand_signature<R (Args...)> final
    {
        using return_type = R;
        using signature_type = R (Args...);
    };

    template <class Signature>
    using get_return_type = typename expand_signature<Signature>::return_type;

    struct shared_lock final : ref_counted<shared_lock, ref_count_atomic>
    {
        std::mutex mutex;
    };

    template <class ThreadingPolicy>
    struct shared_lock_state;

    template <>
    struct shared_lock_state<thread_unsafe_policy> final
    {
        constexpr void lock() ROCKET_NOEXCEPT
        {
        }

        constexpr bool try_lock() ROCKET_NOEXCEPT
        {
            return true;
        }

        constexpr void unlock() ROCKET_NOEXCEPT
        {
        }

        constexpr void swap (shared_lock_state& s) ROCKET_NOEXCEPT
        {
        }
    };

    template <>
    struct shared_lock_state<thread_safe_policy> final
    {
        shared_lock_state()
            : lock_primitive { new shared_lock }
        {
        }

        ~shared_lock_state() = default;

        shared_lock_state (shared_lock_state const& s)
            : lock_primitive { s.lock_primitive }
        {
        }

        shared_lock_state (shared_lock_state&& s)
            : lock_primitive { std::move (s.lock_primitive) }
        {
            s.lock_primitive = new shared_lock;
        }

        shared_lock_state& operator= (shared_lock_state const& rhs)
        {
            lock_primitive = rhs.lock_primitive;
            return *this;
        }

        shared_lock_state& operator= (shared_lock_state&& rhs)
        {
            lock_primitive = std::move (rhs.lock_primitive);
            rhs.lock_primitive = new shared_lock;
            return *this;
        }

        void lock()
        {
            lock_primitive->mutex.lock();
        }

        bool try_lock()
        {
            return lock_primitive->mutex.try_lock();
        }

        void unlock()
        {
            lock_primitive->mutex.unlock();
        }

        void swap (shared_lock_state& s) ROCKET_NOEXCEPT
        {
            lock_primitive.swap (s.lock_primitive);
        }

        intrusive_ptr<shared_lock> lock_primitive;
    };

    template <class ThreadingPolicy>
    struct connection_base;

    template <>
    struct connection_base<thread_unsafe_policy>
        : thread_unsafe_policy, ref_counted<connection_base<thread_unsafe_policy>>
    {
        virtual ~connection_base() ROCKET_NOEXCEPT = default;

        bool is_connected() const ROCKET_NOEXCEPT
        {
            return prev != nullptr;
        }

        void disconnect() ROCKET_NOEXCEPT
        {
            if (prev != nullptr)
            {
                next->prev = prev;
                prev->next = next;

                // To mark a connection as disconnected, just set its prev-link to null but
                // leave the next link alive so we can still traverse through the connections
                // if the slot gets disconnected during signal emit.
                prev = nullptr;
            }
        }

        std::thread::id get_tid() const ROCKET_NOEXCEPT
        {
            return std::thread::id {};
        }

        constexpr bool is_queued() const ROCKET_NOEXCEPT
        {
            return false;
        }

#ifndef ROCKET_NO_BLOCKING_CONNECTIONS
        void block() ROCKET_NOEXCEPT
        {
            ++block_count;
        }

        void unblock() ROCKET_NOEXCEPT
        {
            if (block_count > 0)
            {
                --block_count;
            }
        }

        bool is_blocked() const ROCKET_NOEXCEPT
        {
            return block_count > 0;
        }

        unsigned long block_count { 0 };
#endif //~ ROCKET_NO_BLOCKING_CONNECTIONS

        intrusive_ptr<connection_base> next;
        intrusive_ptr<connection_base> prev;
    };

    template <>
    struct connection_base<thread_safe_policy>
        : thread_safe_policy, ref_counted<connection_base<thread_safe_policy>, ref_count_atomic>
    {
        virtual ~connection_base() ROCKET_NOEXCEPT = default;

        bool is_connected() const ROCKET_NOEXCEPT
        {
            return static_cast<intrusive_ptr<connection_base> volatile const&> (prev).get() != nullptr;
        }

        void disconnect() ROCKET_NOEXCEPT
        {
            std::scoped_lock<std::mutex> guard { lock->mutex };

            if (prev != nullptr)
            {
                next->prev = prev;
                prev->next = next;

                // To mark a connection as disconnected, just set its prev-link to null but
                // leave the next link alive so we can still traverse through the connections
                // if the slot gets disconnected during signal emit.
                auto& prev_cast = static_cast<intrusive_ptr<connection_base> volatile&> (prev);
                (intrusive_ptr<connection_base>&) prev_cast = nullptr;
            }
        }

        std::thread::id const& get_tid() const ROCKET_NOEXCEPT
        {
            return thread_id;
        }

        bool is_queued() const ROCKET_NOEXCEPT
        {
            return thread_id != std::thread::id {}
                   && thread_id != std::this_thread::get_id();
        }

#ifndef ROCKET_NO_BLOCKING_CONNECTIONS
        void block() ROCKET_NOEXCEPT
        {
            std::scoped_lock<std::mutex> guard { lock->mutex };
            ++block_count;
        }

        void unblock() ROCKET_NOEXCEPT
        {
            std::scoped_lock<std::mutex> guard { lock->mutex };
            if (block_count > 0)
            {
                --block_count;
            }
        }

        bool is_blocked() const ROCKET_NOEXCEPT
        {
            return (*static_cast<unsigned long const volatile*> (&block_count)) > 0;
        }

        unsigned long block_count { 0 };
#endif //~ ROCKET_NO_BLOCKING_CONNECTIONS

        intrusive_ptr<connection_base> next;
        intrusive_ptr<connection_base> prev;

        intrusive_ptr<shared_lock> lock;

        std::thread::id thread_id;
    };

    template <class ThreadingPolicy, class T>
    struct functional_connection : connection_base<ThreadingPolicy>
    {
        std::function<T> slot;
    };

#ifndef ROCKET_NO_TIMERS
    struct timed_connection final : functional_connection<thread_unsafe_policy, void()>
    {
        std::chrono::time_point<std::chrono::steady_clock> expires_at;
        std::chrono::microseconds interval;
    };
#endif
    // Should make sure that this is POD
    struct thread_local_data final
    {
        void* current_connection;
        bool emission_aborted;
    };

    inline thread_local_data* get_thread_local_data() ROCKET_NOEXCEPT
    {
        static ROCKET_THREAD_LOCAL thread_local_data th;
        return &th;
    }

    struct connection_scope final
    {
        connection_scope (void* base, thread_local_data* th) ROCKET_NOEXCEPT
            : th { th },
              prev { th->current_connection }
        {
            th->current_connection = base;
        }

        ~connection_scope() ROCKET_NOEXCEPT
        {
            th->current_connection = prev;
        }

        thread_local_data* th;
        void* prev;
    };

    struct abort_scope final
    {
        abort_scope (thread_local_data* th) ROCKET_NOEXCEPT
            : th { th },
              prev { th->emission_aborted }
        {
            th->emission_aborted = false;
        }

        ~abort_scope() ROCKET_NOEXCEPT
        {
            th->emission_aborted = prev;
        }

        thread_local_data* th;
        bool prev;
    };

    template <class Instance, class Class, class R, class... Args>
    struct weak_mem_fn final
    {
        explicit weak_mem_fn (std::weak_ptr<Instance> c, R (Class::*method) (Args...))
            : weak { std::move (c) }, method { method }
        {
        }

        template <class... Args1>
        auto operator() (Args1&&... args) const
        {
            if constexpr (std::is_void_v<R>)
            {
                if (auto strong = weak.lock())
                {
                    (strong.get()->*method) (std::forward<Args1> (args)...);
                }
            }
            else
            {
                if (auto strong = weak.lock())
                {
                    return optional<R> {
                        (strong.get()->*method) (std::forward<Args1> (args)...)
                    };
                }
                return optional<R> {};
            }
        }

    private:
        std::weak_ptr<Instance> weak;
        R (Class::*method)
        (Args...);
    };

    template <class Instance, class Class, class R, class... Args>
    struct shared_mem_fn final
    {
        explicit shared_mem_fn (std::shared_ptr<Instance> c, R (Class::*method) (Args...))
            : shared { std::move (c) }, method { method }
        {
        }

        template <class... Args1>
        R operator() (Args1&&... args) const
        {
            return (shared.get()->*method) (std::forward<Args1> (args)...);
        }

    private:
        std::shared_ptr<Instance> shared;
        R (Class::*method)
        (Args...);
    };
} // namespace detail

template <class Instance, class Class, class R, class... Args>
inline auto bind_weak_ptr (std::weak_ptr<Instance> c, R (Class::*method) (Args...))
{
    return detail::weak_mem_fn<Instance, Class, R, Args...> { std::move (c), method };
}

template <class Instance, class Class, class R, class... Args>
inline auto bind_weak_ptr (std::shared_ptr<Instance> c, R (Class::*method) (Args...))
{
    return detail::weak_mem_fn<Instance, Class, R, Args...> { std::move (c), method };
}

template <class Instance, class Class, class R, class... Args>
inline auto bind_shared_ptr (std::shared_ptr<Instance> c, R (Class::*method) (Args...))
{
    return detail::shared_mem_fn<Instance, Class, R, Args...> { std::move (c), method };
}

struct connection
{
    connection() ROCKET_NOEXCEPT
        : base { nullptr }
    {
    }

    ~connection() ROCKET_NOEXCEPT
    {
        release();
    }

    connection (connection&& rhs) ROCKET_NOEXCEPT
        : base { rhs.base }
    {
        rhs.base = nullptr;
    }

    connection (connection const& rhs) ROCKET_NOEXCEPT
        : base { rhs.base }
    {
        addref();
    }

    explicit connection (void* base) ROCKET_NOEXCEPT
        : base { base }
    {
        addref();
    }

    connection& operator= (connection&& rhs) ROCKET_NOEXCEPT
    {
        release();
        base = rhs.base;
        rhs.base = nullptr;
        return *this;
    }

    connection& operator= (connection const& rhs) ROCKET_NOEXCEPT
    {
        if (this != &rhs)
        {
            release();
            base = rhs.base;
            addref();
        }
        return *this;
    }

    bool operator== (connection const& rhs) const ROCKET_NOEXCEPT
    {
        return base == rhs.base;
    }

    bool operator!= (connection const& rhs) const ROCKET_NOEXCEPT
    {
        return base != rhs.base;
    }

    explicit operator bool() const ROCKET_NOEXCEPT
    {
        return is_connected();
    }

    bool is_connected() const ROCKET_NOEXCEPT
    {
        if (base != nullptr)
        {
            auto safe { static_cast<detail::connection_base<thread_safe_policy>*> (base) };
            auto unsafe { static_cast<detail::connection_base<thread_unsafe_policy>*> (base) };

            assert (&safe->is_thread_safe == &unsafe->is_thread_safe);

            if (! unsafe->is_thread_safe)
                ROCKET_LIKELY
                {
                    return unsafe->is_connected();
                }
            else
            {
                return safe->is_connected();
            }
        }
        return false;
    }

#ifndef ROCKET_NO_BLOCKING_CONNECTIONS
    bool is_blocked() const ROCKET_NOEXCEPT
    {
        if (base != nullptr)
        {
            auto safe { static_cast<detail::connection_base<thread_safe_policy>*> (base) };
            auto unsafe { static_cast<detail::connection_base<thread_unsafe_policy>*> (base) };

            assert (&safe->is_thread_safe == &unsafe->is_thread_safe);

            if (! unsafe->is_thread_safe)
                ROCKET_LIKELY
                {
                    return unsafe->is_blocked();
                }
            else
            {
                return safe->is_blocked();
            }
        }
        return false;
    }

    void block() ROCKET_NOEXCEPT
    {
        if (base != nullptr)
        {
            auto safe { static_cast<detail::connection_base<thread_safe_policy>*> (base) };
            auto unsafe { static_cast<detail::connection_base<thread_unsafe_policy>*> (base) };

            assert (&safe->is_thread_safe == &unsafe->is_thread_safe);

            if (! unsafe->is_thread_safe)
                ROCKET_LIKELY
                {
                    unsafe->block();
                }
            else
            {
                safe->block();
            }
        }
    }

    void unblock() ROCKET_NOEXCEPT
    {
        if (base != nullptr)
        {
            auto safe { static_cast<detail::connection_base<thread_safe_policy>*> (base) };
            auto unsafe { static_cast<detail::connection_base<thread_unsafe_policy>*> (base) };

            assert (&safe->is_thread_safe == &unsafe->is_thread_safe);

            if (! unsafe->is_thread_safe)
                ROCKET_LIKELY
                {
                    unsafe->unblock();
                }
            else
            {
                safe->unblock();
            }
        }
    }
#endif //~ ROCKET_NO_BLOCKING_CONNECTIONS

    void disconnect() ROCKET_NOEXCEPT
    {
        if (base != nullptr)
        {
            auto safe { static_cast<detail::connection_base<thread_safe_policy>*> (base) };
            auto unsafe { static_cast<detail::connection_base<thread_unsafe_policy>*> (base) };

            assert (&safe->is_thread_safe == &unsafe->is_thread_safe);

            if (! unsafe->is_thread_safe)
                ROCKET_LIKELY
                {
                    unsafe->disconnect();
                }
            else
            {
                safe->disconnect();
            }

            release();
        }
    }

    void swap (connection& other) ROCKET_NOEXCEPT
    {
        if (this != &other)
        {
            void* tmp_base { base };
            base = other.base;
            other.base = tmp_base;
        }
    }

private:
    void* base;

    void addref() ROCKET_NOEXCEPT
    {
        if (base != nullptr)
        {
            auto safe { static_cast<detail::connection_base<thread_safe_policy>*> (base) };
            auto unsafe { static_cast<detail::connection_base<thread_unsafe_policy>*> (base) };

            assert (&safe->is_thread_safe == &unsafe->is_thread_safe);

            if (! unsafe->is_thread_safe)
                ROCKET_LIKELY
                {
                    unsafe->addref();
                }
            else
            {
                safe->addref();
            }
        }
    }

    void release() ROCKET_NOEXCEPT
    {
        if (base != nullptr)
        {
            auto safe { static_cast<detail::connection_base<thread_safe_policy>*> (base) };
            auto unsafe { static_cast<detail::connection_base<thread_unsafe_policy>*> (base) };

            assert (&safe->is_thread_safe == &unsafe->is_thread_safe);

            if (! unsafe->is_thread_safe)
                ROCKET_LIKELY
                {
                    unsafe->release();
                }
            else
            {
                safe->release();
            }

            base = nullptr;
        }
    }
};

struct scoped_connection final : connection
{
    scoped_connection() ROCKET_NOEXCEPT = default;

    ~scoped_connection() ROCKET_NOEXCEPT
    {
        disconnect();
    }

    scoped_connection (connection const& rhs) ROCKET_NOEXCEPT
        : connection { rhs }
    {
    }

    scoped_connection (connection&& rhs) ROCKET_NOEXCEPT
        : connection { std::move (rhs) }
    {
    }

    scoped_connection (scoped_connection&& rhs) ROCKET_NOEXCEPT
        : connection { std::move (rhs) }
    {
    }

    scoped_connection& operator= (connection&& rhs) ROCKET_NOEXCEPT
    {
        disconnect();

        connection::operator= (std::move (rhs));
        return *this;
    }

    scoped_connection& operator= (scoped_connection&& rhs) ROCKET_NOEXCEPT
    {
        disconnect();

        connection::operator= (std::move (rhs));
        return *this;
    }

    scoped_connection& operator= (connection const& rhs) ROCKET_NOEXCEPT
    {
        disconnect();

        connection::operator= (rhs);
        return *this;
    }

private:
    scoped_connection (scoped_connection const&) = delete;

    scoped_connection& operator= (scoped_connection const&) = delete;
};

struct scoped_connection_container final
{
    scoped_connection_container() = default;
    ~scoped_connection_container() = default;

    scoped_connection_container (scoped_connection_container&& s)
        : connections { std::move (s.connections) }
    {
    }

    scoped_connection_container& operator= (scoped_connection_container&& rhs)
    {
        connections = std::move (rhs.connections);
        return *this;
    }

    scoped_connection_container (std::initializer_list<connection> list)
    {
        append (list);
    }

    void append (connection const& conn)
    {
        connections.push_front (scoped_connection { conn });
    }

    void append (std::initializer_list<connection> list)
    {
        for (auto const& connection : list)
        {
            append (connection);
        }
    }

    scoped_connection_container& operator+= (connection const& conn)
    {
        append (conn);
        return *this;
    }

    scoped_connection_container& operator+= (std::initializer_list<connection> list)
    {
        for (auto const& connection : list)
        {
            append (connection);
        }
        return *this;
    }

    void disconnect() ROCKET_NOEXCEPT
    {
        connections.clear();
    }

private:
    scoped_connection_container (scoped_connection_container const&) = delete;
    scoped_connection_container& operator= (scoped_connection_container const&) = delete;

    std::forward_list<scoped_connection> connections;
};

struct trackable
{
    void add_tracked_connection (connection const& conn)
    {
        container.append (conn);
    }

    void disconnect_tracked_connections() ROCKET_NOEXCEPT
    {
        container.disconnect();
    }

private:
    scoped_connection_container container;
};

inline connection current_connection() ROCKET_NOEXCEPT
{
    return connection { detail::get_thread_local_data()->current_connection };
}

inline void abort_emission() ROCKET_NOEXCEPT
{
    detail::get_thread_local_data()->emission_aborted = true;
}

#ifndef ROCKET_NO_BLOCKING_CONNECTIONS
struct scoped_connection_blocker final
{
    scoped_connection_blocker (connection c) ROCKET_NOEXCEPT
        : conn { std::move (c) }
    {
        conn.block();
    }

    ~scoped_connection_blocker() ROCKET_NOEXCEPT
    {
        conn.unblock();
    }

private:
    scoped_connection_blocker (scoped_connection_blocker const&) = delete;
    scoped_connection_blocker& operator= (scoped_connection_blocker const&) = delete;

    connection conn;
};
#endif //~ ROCKET_NO_BLOCKING_CONNECTIONS

namespace detail
{
#ifndef ROCKET_NO_TIMERS
    struct timer_queue final
    {
        using slot_type = std::function<void()>;

        timer_queue()
        {
            init();
        }

        ~timer_queue() ROCKET_NOEXCEPT
        {
            destroy();
        }

        timer_queue (timer_queue&& q)
            : head { std::move (q.head) }, tail { std::move (q.tail) }
        {
            q.init();
        }

        timer_queue (timer_queue const& q)
        {
            init();
            copy (q);
        }

        timer_queue& operator= (timer_queue&& rhs)
        {
            destroy();
            head = std::move (rhs.head);
            tail = std::move (rhs.tail);
            rhs.init();
            return *this;
        }

        timer_queue& operator= (timer_queue const& rhs)
        {
            if (this != &rhs)
            {
                clear();
                copy (rhs);
            }
            return *this;
        }

        template <class Rep = unsigned long, class Period = std::milli>
        connection set_interval (slot_type slot, std::chrono::duration<Rep, Period> const& interval)
        {
            assert (slot != nullptr);

            auto expires_at = std::chrono::steady_clock::now() + interval;
            auto interval_microsecs = std::chrono::duration_cast<std::chrono::microseconds> (interval);
            connection_base* base = make_link (tail, std::move (slot), std::move (expires_at), std::move (interval_microsecs));
            return connection { static_cast<void*> (base) };
        }

        template <auto Method, class Rep = unsigned long, class Period = std::milli>
        connection set_interval (std::chrono::duration<Rep, Period> const& interval)
        {
            return set_interval<Rep, Period> ([]
                                              { (*Method)(); },
                                              interval);
        }

        template <class Instance, class Class, class R, class Rep = unsigned long, class Period = std::milli>
        connection set_interval (Instance& object, R (Class::*method)(), std::chrono::duration<Rep, Period> const& interval)
        {
            connection c {
                set_interval<Rep, Period> ([&object, method]
                                           { (object.*method)(); },
                                           interval)
            };
            if constexpr (std::is_convertible_v<Instance*, trackable*>)
            {
                static_cast<trackable&> (object).add_tracked_connection (c);
            }
            return c;
        }

        template <auto Method, class Instance, class Rep = unsigned long, class Period = std::milli>
        connection set_interval (Instance& object, std::chrono::duration<Rep, Period> const& interval)
        {
            connection c {
                set_interval<Rep, Period> ([&object]
                                           { (object.*Method)(); },
                                           interval)
            };
            if constexpr (std::is_convertible_v<Instance*, trackable*>)
            {
                static_cast<trackable&> (object).add_tracked_connection (c);
            }
            return c;
        }

        template <class Instance, class Class, class R, class Rep = unsigned long, class Period = std::milli>
        connection set_interval (Instance* object, R (Class::*method)(), std::chrono::duration<Rep, Period> const& interval)
        {
            connection c {
                set_interval<Rep, Period> ([object, method]
                                           { (object->*method)(); },
                                           interval)
            };
            if constexpr (std::is_convertible_v<Instance*, trackable*>)
            {
                static_cast<trackable*> (object)->add_tracked_connection (c);
            }
            return c;
        }

        template <auto Method, class Instance, class Rep = unsigned long, class Period = std::milli>
        connection set_interval (Instance* object, std::chrono::duration<Rep, Period> const& interval)
        {
            connection c {
                set_interval<Rep, Period> ([object]
                                           { (object->*Method)(); },
                                           interval)
            };
            if constexpr (std::is_convertible_v<Instance*, trackable*>)
            {
                static_cast<trackable*> (object)->add_tracked_connection (c);
            }
            return c;
        }

        template <class Rep = unsigned long, class Period = std::milli>
        connection set_timeout (slot_type slot, std::chrono::duration<Rep, Period> const& timeout)
        {
            assert (slot != nullptr);

            auto expires_at = std::chrono::steady_clock::now() + timeout;
            connection_base* base = make_link (tail, std::move (slot), std::move (expires_at), std::chrono::microseconds (-1));
            return connection { static_cast<void*> (base) };
        }

        template <auto Method, class Rep = unsigned long, class Period = std::milli>
        connection set_timeout (std::chrono::duration<Rep, Period> const& timeout)
        {
            return set_timeout<Rep, Period> ([]
                                             { (*Method)(); },
                                             timeout);
        }

        template <class Instance, class Class, class R, class Rep = unsigned long, class Period = std::milli>
        connection set_timeout (Instance& object, R (Class::*method)(), std::chrono::duration<Rep, Period> const& timeout)
        {
            connection c {
                set_timeout<Rep, Period> ([&object, method]
                                          { (object.*method)(); },
                                          timeout)
            };
            if constexpr (std::is_convertible_v<Instance*, trackable*>)
            {
                static_cast<trackable&> (object).add_tracked_connection (c);
            }
            return c;
        }

        template <auto Method, class Instance, class Rep = unsigned long, class Period = std::milli>
        connection set_timeout (Instance& object, std::chrono::duration<Rep, Period> const& timeout)
        {
            connection c {
                set_timeout<Rep, Period> ([&object]
                                          { (object.*Method)(); },
                                          timeout)
            };
            if constexpr (std::is_convertible_v<Instance*, trackable*>)
            {
                static_cast<trackable&> (object).add_tracked_connection (c);
            }
            return c;
        }

        template <class Instance, class Class, class R, class Rep = unsigned long, class Period = std::milli>
        connection set_timeout (Instance* object, R (Class::*method)(), std::chrono::duration<Rep, Period> const& timeout)
        {
            connection c {
                set_timeout<Rep, Period> ([object, method]
                                          { (object->*method)(); },
                                          timeout)
            };
            if constexpr (std::is_convertible_v<Instance*, trackable*>)
            {
                static_cast<trackable*> (object)->add_tracked_connection (c);
            }
            return c;
        }

        template <auto Method, class Instance, class Rep = unsigned long, class Period = std::milli>
        connection set_timeout (Instance* object, std::chrono::duration<Rep, Period> const& timeout)
        {
            connection c {
                set_timeout<Rep, Period> ([object]
                                          { (object->*Method)(); },
                                          timeout)
            };
            if constexpr (std::is_convertible_v<Instance*, trackable*>)
            {
                static_cast<trackable*> (object)->add_tracked_connection (c);
            }
            return c;
        }

        void clear() ROCKET_NOEXCEPT
        {
            intrusive_ptr<connection_base> current { head->next };
            while (current != tail)
            {
                intrusive_ptr<connection_base> next { current->next };
                current->next = tail;
                current->prev = nullptr;
                current = std::move (next);
            }

            head->next = tail;
            tail->prev = head;
        }

        void swap (timer_queue& other) ROCKET_NOEXCEPT
        {
            if (this != &other)
            {
                head.swap (other.head);
                tail.swap (other.tail);
            }
        }

        bool dispatch (std::chrono::time_point<std::chrono::steady_clock> execute_until)
        {
#ifndef ROCKET_NO_EXCEPTIONS
            bool error { false };
#endif
            bool not_enough_time { false };
            std::chrono::time_point<std::chrono::steady_clock> now = std::chrono::steady_clock::now();
            {
                detail::thread_local_data* th { detail::get_thread_local_data() };
                detail::abort_scope ascope { th };

                intrusive_ptr<connection_base> current { head->next };
                intrusive_ptr<connection_base> end { tail };

                while (current != end)
                {
                    assert (current != nullptr);

                    if (current->prev != nullptr
#ifndef ROCKET_NO_BLOCKING_CONNECTIONS
                        && current->block_count == 0
#endif
                    )
                        ROCKET_LIKELY
                        {
                            detail::connection_scope cscope { current, th };

                            timed_connection* conn = static_cast<
                                timed_connection*> (static_cast<void*> (current));

                            if (conn->expires_at <= now)
                            {
                                if (conn->interval.count() < 0)
                                {
                                    conn->disconnect();
                                }
                                else
                                {
                                    conn->expires_at = now + conn->interval;
                                }
#ifndef ROCKET_NO_EXCEPTIONS
                                try
                                {
#endif
                                    conn->slot();
#ifndef ROCKET_NO_EXCEPTIONS
                                }
                                catch (...)
                                {
                                    error = true;
                                }
#endif
                                if (execute_until != std::chrono::time_point<std::chrono::steady_clock> {})
                                    ROCKET_UNLIKELY
                                    {
                                        // Check if we already spent the maximum allowed time executing callbacks
                                        if (execute_until <= std::chrono::steady_clock::now())
                                        {
                                            not_enough_time = true;
                                            break;
                                        }
                                    }

                                if (th->emission_aborted)
                                    ROCKET_UNLIKELY
                                    {
                                        break;
                                    }
                            }
                        }

                    current = current->next;
                }
            }

#ifndef ROCKET_NO_EXCEPTIONS
            if (error)
                ROCKET_UNLIKELY
                {
                    throw invocation_slot_error {};
                }
#endif
            return not_enough_time;
        }

    private:
        using connection_base = detail::connection_base<thread_unsafe_policy>;

        void init()
        {
            head = new connection_base;
            tail = new connection_base;
            head->next = tail;
            tail->prev = head;
        }

        void destroy() ROCKET_NOEXCEPT
        {
            clear();
            head->next = nullptr;
            tail->prev = nullptr;
        }

        void copy (timer_queue const& q)
        {
            intrusive_ptr<connection_base> current { q.head->next };
            intrusive_ptr<connection_base> end { q.tail };

            while (current != end)
            {
                timed_connection* conn = static_cast<
                    timed_connection*> (static_cast<void*> (current));

                make_link (tail, conn->slot, conn->expires_at, conn->interval);
                current = current->next;
            }
        }

        timed_connection* make_link (connection_base* l, slot_type slot, std::chrono::time_point<std::chrono::steady_clock> expires_at, std::chrono::microseconds interval)
        {
            intrusive_ptr<timed_connection> link { new timed_connection };
            link->slot = std::move (slot);
            link->expires_at = std::move (expires_at);
            link->interval = std::move (interval);
            link->prev = l->prev;
            link->next = l;
            link->prev->next = link;
            link->next->prev = link;
            return link;
        }

        intrusive_ptr<connection_base> head;
        intrusive_ptr<connection_base> tail;
    };

    inline timer_queue* get_timer_queue() ROCKET_NOEXCEPT
    {
        static ROCKET_THREAD_LOCAL timer_queue queue;
        return &queue;
    }

#endif //~ ROCKET_NO_TIMERS

    struct call_queue final
    {
        void put (std::thread::id tid, std::packaged_task<void()> task)
        {
            std::scoped_lock<std::mutex> guard { mutex };
            queue[tid].push_back (std::move (task));
        }

        bool dispatch (std::chrono::time_point<std::chrono::steady_clock> execute_until)
        {
            std::thread::id tid = std::this_thread::get_id();
            std::deque<std::packaged_task<void()>> thread_queue;

            {
                std::scoped_lock<std::mutex> guard { mutex };

                auto iterator = queue.find (tid);
                if (iterator != queue.end())
                {
                    thread_queue.swap (iterator->second);
                    queue.erase (iterator);
                }
            }

            auto itr = thread_queue.begin();
            auto end = thread_queue.end();

            while (itr != end)
            {
                (itr++)->operator()();

                if (execute_until != std::chrono::time_point<std::chrono::steady_clock> {})
                    ROCKET_UNLIKELY
                    {
                        // check if we already spent the maximum allowed time executing callbacks
                        if (execute_until <= std::chrono::steady_clock::now())
                        {
                            break;
                        }
                    }
            }

            if (itr != end)
                ROCKET_UNLIKELY
                {
                    // readd unfinished work to the queue
                    auto rbegin = std::make_reverse_iterator (end);
                    auto rend = std::make_reverse_iterator (itr);

                    std::scoped_lock<std::mutex> guard { mutex };
                    std::deque<std::packaged_task<void()>>& original_queue = queue[tid];

                    for (auto it = rbegin; it != rend; ++it)
                    {
                        original_queue.push_front (std::move (*it));
                    }
                }
            return itr != end;
        }

    private:
        std::mutex mutex;
        std::unordered_map<std::thread::id, std::deque<std::packaged_task<void()>>> queue;
    };

    inline call_queue* get_call_queue() ROCKET_NOEXCEPT
    {
        static call_queue queue;
        return &queue;
    }

    template <class T>
    struct decay
    {
        typedef typename std::remove_reference<T>::type U;
        typedef typename std::conditional<
            std::is_array<U>::value,
            typename std::remove_extent<U>::type*,
            typename std::conditional<
                std::is_function<U>::value,
                typename std::add_pointer<U>::type,
                typename std::conditional<
                    std::is_const<U>::value || ! std::is_reference<T>::value,
                    typename std::remove_cv<U>::type,
                    T>::type>::type>::type type;
    };

    template <class T>
    struct unwrap_refwrapper
    {
        using type = T;
    };

    template <class T>
    struct unwrap_refwrapper<std::reference_wrapper<T>>
    {
        using type = T&;
    };

    template <class T>
    using special_decay_t = typename unwrap_refwrapper<typename decay<T>::type>::type;

    // This make_tuple implementation is different from std::make_tuple.
    // This one preserves non-const references as actual reference values.
    // However const references will be stored by value.

    // make_tuple(int const&) => tuple<int>
    // make_tuple(int&) => tuple<int&>

    template <class... Types>
    auto make_tuple (Types&&... args)
    {
        return std::tuple<special_decay_t<Types>...> (std::forward<Types> (args)...);
    }
} // namespace detail

template <class Rep, class Period>
inline void dispatch_queued_calls (std::chrono::duration<Rep, Period> const& max_time_to_execute)
{
    std::chrono::time_point<std::chrono::steady_clock> execute_until {};
    if (max_time_to_execute.count() > 0)
        ROCKET_UNLIKELY
        {
            execute_until = std::chrono::steady_clock::now() + max_time_to_execute;
        }
#ifndef ROCKET_NO_TIMERS
    bool not_enough_time = detail::get_timer_queue()->dispatch (execute_until);
    if (not_enough_time)
        ROCKET_UNLIKELY
        {
            return;
        }
#endif
    detail::get_call_queue()->dispatch (execute_until);
}

inline void dispatch_queued_calls()
{
    dispatch_queued_calls (std::chrono::microseconds::zero());
}

#ifndef ROCKET_NO_TIMERS
template <class Rep = unsigned long, class Period = std::milli>
inline connection set_interval (std::function<void()> slot, std::chrono::duration<Rep, Period> const& interval)
{
    return detail::get_timer_queue()->template set_interval<Rep, Period> (std::move (slot), interval);
}

template <auto Method, class Rep = unsigned long, class Period = std::milli>
inline connection set_interval (std::chrono::duration<Rep, Period> const& interval)
{
    return detail::get_timer_queue()->template set_interval<Method, Rep, Period> (interval);
}

template <class Instance, class Class, class R, class Rep = unsigned long, class Period = std::milli>
inline connection set_interval (Instance& object, R (Class::*method)(), std::chrono::duration<Rep, Period> const& interval)
{
    return detail::get_timer_queue()->template set_interval<Instance, Class, R, Rep, Period> (object, method, interval);
}

template <auto Method, class Instance, class Rep = unsigned long, class Period = std::milli>
inline connection set_interval (Instance& object, std::chrono::duration<Rep, Period> const& interval)
{
    return detail::get_timer_queue()->template set_interval<Method, Instance, Rep, Period> (object, interval);
}

template <class Instance, class Class, class R, class Rep = unsigned long, class Period = std::milli>
inline connection set_interval (Instance* object, R (Class::*method)(), std::chrono::duration<Rep, Period> const& interval)
{
    return detail::get_timer_queue()->template set_interval<Instance, Class, R, Rep, Period> (object, method, interval);
}

template <auto Method, class Instance, class Rep = unsigned long, class Period = std::milli>
inline connection set_interval (Instance* object, std::chrono::duration<Rep, Period> const& interval)
{
    return detail::get_timer_queue()->template set_interval<Method, Instance, Rep, Period> (object, interval);
}

template <class Rep = unsigned long, class Period = std::milli>
inline connection set_timeout (std::function<void()> slot, std::chrono::duration<Rep, Period> const& timeout)
{
    return detail::get_timer_queue()->template set_timeout<Rep, Period> (std::move (slot), timeout);
}

template <auto Method, class Rep = unsigned long, class Period = std::milli>
inline connection set_timeout (std::chrono::duration<Rep, Period> const& timeout)
{
    return detail::get_timer_queue()->template set_timeout<Method, Rep, Period> (timeout);
}

template <class Instance, class Class, class R, class Rep = unsigned long, class Period = std::milli>
inline connection set_timeout (Instance& object, R (Class::*method)(), std::chrono::duration<Rep, Period> const& timeout)
{
    return detail::get_timer_queue()->template set_timeout<Instance, Class, R, Rep, Period> (object, method, timeout);
}

template <auto Method, class Instance, class Rep = unsigned long, class Period = std::milli>
inline connection set_timeout (Instance& object, std::chrono::duration<Rep, Period> const& timeout)
{
    return detail::get_timer_queue()->template set_timeout<Method, Instance, Rep, Period> (object, timeout);
}

template <class Instance, class Class, class R, class Rep = unsigned long, class Period = std::milli>
inline connection set_timeout (Instance* object, R (Class::*method)(), std::chrono::duration<Rep, Period> const& timeout)
{
    return detail::get_timer_queue()->template set_timeout<Instance, Class, R, Rep, Period> (object, method, timeout);
}

template <auto Method, class Instance, class Rep = unsigned long, class Period = std::milli>
inline connection set_timeout (Instance* object, std::chrono::duration<Rep, Period> const& timeout)
{
    return detail::get_timer_queue()->template set_timeout<Method, Instance, Rep, Period> (object, timeout);
}

// Overloads for milliseconds
inline connection set_interval (std::function<void()> slot, unsigned long interval_ms)
{
    return detail::get_timer_queue()->template set_interval<> (std::move (slot), std::chrono::milliseconds (interval_ms));
}

template <auto Method>
inline connection set_interval (unsigned long interval_ms)
{
    return detail::get_timer_queue()->template set_interval<Method> (std::chrono::milliseconds (interval_ms));
}

template <class Instance, class Class, class R>
inline connection set_interval (Instance& object, R (Class::*method)(), unsigned long interval_ms)
{
    return detail::get_timer_queue()->template set_interval<Instance, Class, R> (object, method, std::chrono::milliseconds (interval_ms));
}

template <auto Method, class Instance>
inline connection set_interval (Instance& object, unsigned long interval_ms)
{
    return detail::get_timer_queue()->template set_interval<Method, Instance> (object, std::chrono::milliseconds (interval_ms));
}

template <class Instance, class Class, class R>
inline connection set_interval (Instance* object, R (Class::*method)(), unsigned long interval_ms)
{
    return detail::get_timer_queue()->template set_interval<Instance, Class, R> (object, method, std::chrono::milliseconds (interval_ms));
}

template <auto Method, class Instance>
inline connection set_interval (Instance* object, unsigned long interval_ms)
{
    return detail::get_timer_queue()->template set_interval<Method, Instance> (object, std::chrono::milliseconds (interval_ms));
}

inline connection set_timeout (std::function<void()> slot, unsigned long timeout_ms)
{
    return detail::get_timer_queue()->template set_timeout<> (std::move (slot), std::chrono::milliseconds (timeout_ms));
}

template <auto Method>
inline connection set_timeout (unsigned long timeout_ms)
{
    return detail::get_timer_queue()->template set_timeout<Method> (std::chrono::milliseconds (timeout_ms));
}

template <class Instance, class Class, class R>
inline connection set_timeout (Instance& object, R (Class::*method)(), unsigned long timeout_ms)
{
    return detail::get_timer_queue()->template set_timeout<Instance, Class, R> (object, method, std::chrono::milliseconds (timeout_ms));
}

template <auto Method, class Instance>
inline connection set_timeout (Instance& object, unsigned long timeout_ms)
{
    return detail::get_timer_queue()->template set_timeout<Method, Instance> (object, std::chrono::milliseconds (timeout_ms));
}

template <class Instance, class Class, class R>
inline connection set_timeout (Instance* object, R (Class::*method)(), unsigned long timeout_ms)
{
    return detail::get_timer_queue()->template set_timeout<Instance, Class, R> (object, method, std::chrono::milliseconds (timeout_ms));
}

template <auto Method, class Instance>
inline connection set_timeout (Instance* object, unsigned long timeout_ms)
{
    return detail::get_timer_queue()->template set_timeout<Method, Instance> (object, std::chrono::milliseconds (timeout_ms));
}

inline void clear_timers() ROCKET_NOEXCEPT
{
    detail::get_timer_queue()->clear();
}
#endif //~ ROCKET_NO_TIMERS

template <class T>
struct default_collector final : last<optional<T>>
{
};

template <>
struct default_collector<void>
{
    using value_type = void;
    using result_type = void;

    void operator()() ROCKET_NOEXCEPT
    {
        /* do nothing for void types */
    }

    void result() ROCKET_NOEXCEPT
    {
        /* do nothing for void types */
    }
};

enum connection_flags : unsigned int
{
    direct_connection = 0,
    queued_connection = 1 << 0,
    connect_as_first_slot = 1 << 1,
};

template <class Signature, class Collector = default_collector<detail::get_return_type<Signature>>, class ThreadingPolicy = thread_unsafe_policy>
struct signal;

template <class Collector, class ThreadingPolicy, class R, class... Args>
struct signal<R (Args...), Collector, ThreadingPolicy> final
{
    using signature_type = R (Args...);
    using slot_type = std::function<signature_type>;

    signal()
    {
        init();
    }

    ~signal() ROCKET_NOEXCEPT
    {
        std::scoped_lock<shared_lock_state> guard { lock_state };
        destroy();
    }

    signal (signal&& s)
    {
        static_assert (std::is_same_v<ThreadingPolicy, thread_unsafe_policy>, "Thread safe signals can't be moved or swapped.");

        head = std::move (s.head);
        tail = std::move (s.tail);
        s.init();
    }

    signal (signal const& s)
    {
        init();

        std::scoped_lock<shared_lock_state> guard { s.lock_state };
        copy (s);
    }

    signal& operator= (signal&& rhs)
    {
        static_assert (std::is_same_v<ThreadingPolicy, thread_unsafe_policy>, "Thread safe signals can't be moved or swapped.");

        destroy();
        head = std::move (rhs.head);
        tail = std::move (rhs.tail);
        rhs.init();
        return *this;
    }

    signal& operator= (signal const& rhs)
    {
        if (this != &rhs)
        {
            std::scoped_lock<shared_lock_state, shared_lock_state> guard { lock_state, rhs.lock_state };
            clear_without_lock();
            copy (rhs);
        }
        return *this;
    }

    connection connect (slot_type slot, connection_flags flags = direct_connection)
    {
        assert (slot != nullptr);

        std::thread::id tid {};

        bool first = (flags & connect_as_first_slot) != 0;

        if constexpr (std::is_same_v<ThreadingPolicy, thread_safe_policy>)
        {
            if ((flags & queued_connection) != 0)
                ROCKET_UNLIKELY
                {
                    tid = std::this_thread::get_id();
                }
        }
        else
        {
            assert ((flags & queued_connection) == 0);
        }

        std::scoped_lock<shared_lock_state> guard { lock_state };
        connection_base* base = make_link (first ? head->next : tail, std::move (slot), tid);

        return connection { static_cast<void*> (base) };
    }

    template <class R1, class... Args1>
    connection connect (R1 (*method) (Args1...), connection_flags flags = direct_connection)
    {
        return connect ([method] (Args const&... args)
                        { return R ((*method) (Args1 (args)...)); },
                        flags);
    }

    template <auto Method>
    connection connect (connection_flags flags = direct_connection)
    {
        return connect ([] (Args const&... args)
                        { return R ((*Method) (args...)); },
                        flags);
    }

    template <class Instance, class Class, class R1, class... Args1>
    connection connect (Instance& object, R1 (Class::*method) (Args1...), connection_flags flags = direct_connection)
    {
        connection c {
            connect ([&object, method] (Args const&... args)
                     { return R ((object.*method) (Args1 (args)...)); },
                     flags)
        };
        if constexpr (std::is_convertible_v<Instance*, trackable*>)
        {
            static_cast<trackable&> (object).add_tracked_connection (c);
        }
        return c;
    }

    template <auto Method, class Instance>
    connection connect (Instance& object, connection_flags flags = direct_connection)
    {
        connection c {
            connect ([&object] (Args const&... args)
                     { return R ((object.*Method) (args...)); },
                     flags)
        };
        if constexpr (std::is_convertible_v<Instance*, trackable*>)
        {
            static_cast<trackable&> (object).add_tracked_connection (c);
        }
        return c;
    }

    template <class Instance, class Class, class R1, class... Args1>
    connection connect (Instance* object, R1 (Class::*method) (Args1...), connection_flags flags = direct_connection)
    {
        connection c {
            connect ([object, method] (Args const&... args)
                     { return R ((object->*method) (Args1 (args)...)); },
                     flags)
        };
        if constexpr (std::is_convertible_v<Instance*, trackable*>)
        {
            static_cast<trackable*> (object)->add_tracked_connection (c);
        }
        return c;
    }

    template <auto Method, class Instance>
    connection connect (Instance* object, connection_flags flags = direct_connection)
    {
        connection c {
            connect ([object] (Args const&... args)
                     { return R ((object->*Method) (args...)); },
                     flags)
        };
        if constexpr (std::is_convertible_v<Instance*, trackable*>)
        {
            static_cast<trackable*> (object)->add_tracked_connection (c);
        }
        return c;
    }

    connection operator+= (slot_type slot)
    {
        return connect (std::move (slot));
    }

    void clear() ROCKET_NOEXCEPT
    {
        std::scoped_lock<shared_lock_state> guard { lock_state };
        clear_without_lock();
    }

    void swap (signal& other) ROCKET_NOEXCEPT
    {
        static_assert (std::is_same_v<ThreadingPolicy, thread_unsafe_policy>, "Thread safe signals can't be moved or swapped.");

        if (this != &other)
        {
            head.swap (other.head);
            tail.swap (other.tail);
        }
    }

    template <class ValueCollector = Collector>
    auto invoke (Args const&... args) const
    {
#ifndef ROCKET_NO_EXCEPTIONS
        bool error { false };
#endif
        ValueCollector collector {};
        {
            detail::thread_local_data* th { detail::get_thread_local_data() };
            detail::abort_scope ascope { th };

            lock_state.lock();

            intrusive_ptr<connection_base> current { head->next };
            intrusive_ptr<connection_base> end { tail };

            while (current != end)
            {
                assert (current != nullptr);

                if (current->prev != nullptr
#ifndef ROCKET_NO_BLOCKING_CONNECTIONS
                    && current->block_count == 0
#endif
                )
                    ROCKET_LIKELY
                    {
                        detail::connection_scope cscope { current, th };

                        lock_state.unlock();

                        functional_connection* conn = static_cast<
                            functional_connection*> (static_cast<void*> (current));

                        if constexpr (std::is_same_v<ThreadingPolicy, thread_unsafe_policy>)
                        {
#ifndef ROCKET_NO_EXCEPTIONS
                            try
                            {
#endif
                                if constexpr (std::is_void_v<R>)
                                {
                                    conn->slot (args...);
                                    collector();
                                }
                                else
                                {
                                    collector (conn->slot (args...));
                                }
#ifndef ROCKET_NO_EXCEPTIONS
                            }
                            catch (...)
                            {
                                error = true;
                            }
#endif
                        }
                        else
                        {
                            if (current->is_queued())
                                ROCKET_UNLIKELY
                                {
                                    if constexpr (std::is_void_v<R>)
                                    {
                                        std::packaged_task<void()> task ([current, args = detail::make_tuple (args...)]
                                                                         {
                                        if (current->is_connected()) ROCKET_LIKELY {
                                            detail::thread_local_data* th{ detail::get_thread_local_data() };
                                            detail::connection_scope cscope{ current, th };

                                            functional_connection* conn = static_cast<
                                                functional_connection*>(static_cast<void*>(current));

                                            std::apply(conn->slot, args);
                                        } });

                                        detail::get_call_queue()->put (current->get_tid(), std::move (task));
                                    }
                                    else
                                    {
                                        // If we are calling a queued slot, and our signal requires a return value
                                        // we actually have to block the thread until the slot was dispatched
                                        std::packaged_task<void()> task ([current, &collector, args = std::forward_as_tuple (args...)]
                                                                         {
                                        if (current->is_connected()) ROCKET_LIKELY {
                                            detail::thread_local_data* th{ detail::get_thread_local_data() };
                                            detail::connection_scope cscope{ current, th };

                                            functional_connection* conn = static_cast<
                                                functional_connection*>(static_cast<void*>(current));

                                            collector(std::apply(conn->slot, args));
                                        } });

                                        std::future<void> future { task.get_future() };
                                        detail::get_call_queue()->put (current->get_tid(), std::move (task));
#ifndef ROCKET_NO_EXCEPTIONS
                                        try
                                        {
#endif
                                            future.get();
#ifndef ROCKET_NO_EXCEPTIONS
                                        }
                                        catch (...)
                                        {
                                            error = true;
                                        }
#endif
                                    }
                                }
                            else
                            {
#ifndef ROCKET_NO_EXCEPTIONS
                                try
                                {
#endif
                                    if constexpr (std::is_void_v<R>)
                                    {
                                        conn->slot (args...);
                                        collector();
                                    }
                                    else
                                    {
                                        collector (conn->slot (args...));
                                    }
#ifndef ROCKET_NO_EXCEPTIONS
                                }
                                catch (...)
                                {
                                    error = true;
                                }
#endif
                            }
                        }

                        lock_state.lock();

                        if (th->emission_aborted)
                            ROCKET_UNLIKELY
                            {
                                break;
                            }
                    }

                current = current->next;
            }

            lock_state.unlock();
        }

#ifndef ROCKET_NO_EXCEPTIONS
        if (error)
            ROCKET_UNLIKELY
            {
                throw invocation_slot_error {};
            }
#endif
        return collector.result();
    }

    auto operator() (Args const&... args) const
    {
        return invoke (args...);
    }

private:
    using shared_lock_state = detail::shared_lock_state<ThreadingPolicy>;
    using connection_base = detail::connection_base<ThreadingPolicy>;
    using functional_connection = detail::functional_connection<ThreadingPolicy, signature_type>;

    void init()
    {
        head = new connection_base;
        tail = new connection_base;
        head->next = tail;
        tail->prev = head;
    }

    void destroy() ROCKET_NOEXCEPT
    {
        clear_without_lock();
        head->next = nullptr;
        tail->prev = nullptr;
    }

    void clear_without_lock() ROCKET_NOEXCEPT
    {
        intrusive_ptr<connection_base> current { head->next };
        while (current != tail)
        {
            intrusive_ptr<connection_base> next { current->next };
            current->next = tail;
            current->prev = nullptr;
            current = std::move (next);
        }

        head->next = tail;
        tail->prev = head;
    }

    void copy (signal const& s)
    {
        intrusive_ptr<connection_base> current { s.head->next };
        intrusive_ptr<connection_base> end { s.tail };

        while (current != end)
        {
            functional_connection* conn = static_cast<
                functional_connection*> (static_cast<void*> (current));

            make_link (tail, conn->slot, conn->get_tid());
            current = current->next;
        }
    }

    functional_connection* make_link (connection_base* l, slot_type slot, std::thread::id tid)
    {
        intrusive_ptr<functional_connection> link { new functional_connection };

        if constexpr (std::is_same_v<ThreadingPolicy, thread_safe_policy>)
        {
            link->lock = lock_state.lock_primitive;
            link->thread_id = std::move (tid);
        }

        link->slot = std::move (slot);
        link->prev = l->prev;
        link->next = l;
        link->prev->next = link;
        link->next->prev = link;
        return link;
    }

    intrusive_ptr<connection_base> head;
    intrusive_ptr<connection_base> tail;

    ROCKET_NO_UNIQUE_ADDRESS mutable shared_lock_state lock_state;
};

template <class Signature, class Collector = default_collector<detail::get_return_type<Signature>>>
using thread_safe_signal = signal<Signature, Collector, thread_safe_policy>;

template <class Instance, class Class, class R, class... Args>
inline std::function<R (Args...)> slot (Instance& object, R (Class::*method) (Args...))
{
    return [&object, method] (Args const&... args)
    {
        return (object.*method) (args...);
    };
}

template <class Instance, class Class, class R, class... Args>
inline std::function<R (Args...)> slot (Instance* object, R (Class::*method) (Args...))
{
    return [object, method] (Args const&... args)
    {
        return (object->*method) (args...);
    };
}

template <class T>
inline void swap (intrusive_ptr<T>& p1, intrusive_ptr<T>& p2) ROCKET_NOEXCEPT
{
    p1.swap (p2);
}

template <class T>
inline void swap (stable_list<T>& l1, stable_list<T>& l2) ROCKET_NOEXCEPT
{
    l1.swap (l2);
}

inline void swap (connection& c1, connection& c2) ROCKET_NOEXCEPT
{
    c1.swap (c2);
}

template <class Signature, class Collector, class ThreadingPolicy>
inline void swap (signal<Signature, Collector, ThreadingPolicy>& s1, signal<Signature, Collector, ThreadingPolicy>& s2) ROCKET_NOEXCEPT
{
    s1.swap (s2);
}
} // namespace rocket

#endif
