// Copyright (c) 2020-now by the Zeek Project. See LICENSE for details. #pragma once #include #include #include #include #include #include namespace hilti::rt { /** * Thread execution context. One of these exists per virtual thread, plus one * for the main thread. * * The type's fields are considered implementation details and shouldn't be * accessed or modified by external code. */ struct Context { /** * @param vid virtual thread ID of the thread that will use the context */ explicit Context(vthread::ID vid); ~Context(); Context() = delete; Context(const Context&) = delete; Context(Context&&) = delete; Context& operator=(const Context&) = delete; Context& operator=(Context&&) = delete; /** * The ID of the virtual thread this context belongs to. `vthread::Main` * for the main thread. */ vthread::ID vid; /** * Current resumable if we're inside a fiber so that yielding is * supported. Ownership remains with original caller. */ resumable::Handle* resumable = nullptr; /** Context-specific state for fiber management. */ detail::FiberContext fiber; /** * Pointer to an array of (per thread) global variables allocated by the * linker code. Each array entry corresponds to the globals of one HILTI * module. */ std::vector> hilti_globals; /** A user-defined cookie value that's carried around with the context. */ void* cookie = nullptr; /** Current indent level for debug messages. */ uint64_t debug_indent{}; /** * Current location for user-visible diagnostic messages if we're running * outside of a fiber; null if not set. Considered valid only if * `resumable` is not set. */ const char* location = nullptr; }; namespace context { namespace detail { /** * Helper returning a reference to a thread-local variable storing the * current context. We can't access the pointer directly as that leads to * trouble with JITted code not resolving it correctly. * * Normally, this function should not be used; use `get()`/`set()` instead. */ extern Context*& current(); /** Returns the context for the main thread. */ extern Context* master(); /** Returns the context set for the current hardware thread. */ inline auto get(bool allow_missing_context = false) { auto* ctx = current(); if ( ! allow_missing_context ) assert(ctx); return ctx; } /** * Sets the current context. This will be visible to code inside the current * hardware thread. */ hilti::rt::Context* set(Context* ctx); /** * Utility class that sets the current context's `resumable` field during its life-time. */ class ResumableSetter { public: explicit ResumableSetter(resumable::Handle* r) { old = get()->resumable; get()->resumable = r; } ~ResumableSetter() { get()->resumable = old; } ResumableSetter(const ResumableSetter&) = delete; ResumableSetter(ResumableSetter&&) = delete; ResumableSetter& operator=(const ResumableSetter&) = delete; ResumableSetter& operator=(ResumableSetter&&) = delete; resumable::Handle* old; }; } // namespace detail /** Stores a user-defined cookie in the current context. */ inline void saveCookie(void* cookie) { detail::get()->cookie = cookie; } /** Returns the user-defined cookie currently set in the current context. */ inline void* cookie() { return detail::get()->cookie; } /** Clears the user-defined cookie in the current context. */ inline void clearCookie() { detail::get()->cookie = nullptr; } /** * Utility class that sets the current context's `cookie` field during its life-time. */ class CookieSetter { public: explicit CookieSetter(void* cookie) { _old = detail::get()->cookie; detail::get()->cookie = cookie; } ~CookieSetter() { detail::get()->cookie = _old; } CookieSetter() = delete; CookieSetter(const CookieSetter&) = delete; CookieSetter(CookieSetter&&) = delete; CookieSetter& operator=(const CookieSetter&) = delete; CookieSetter& operator=(CookieSetter&&) = delete; private: void* _old; }; /** * Executes a function inside the current context's fiber. * * @param f function to execute * @param params arguments to pass into function * @return resumable object to either retrieve result or resume if execution got postponed */ template Resumable execute(Function f, Params&&... params) { auto cb = [&](resumable::Handle* r) { auto _ = detail::ResumableSetter(r); return f(std::forward(params)...); }; Resumable r(std::move(cb)); r.run(); return r; } } // namespace context } // namespace hilti::rt