#pragma once #include "zeek/zeek-config.h" #include #include #include #include #include namespace zeek::threading { class Manager; /** * Base class for all threads. * * This class encapsulates all the OS-level thread handling. All thread * instances are automatically added to the threading::Manager for management. The * manager also takes care of deleting them (which must not be done * manually). */ class BasicThread { public: /** * Creates a new thread object. Instantiating the object does however * not yet start the actual OS thread, that requires calling Start(). * * Only Zeek's main thread may create new thread instances. * * @param name A descriptive name for thread the thread. This may * show up in messages to the user. */ BasicThread(); BasicThread(BasicThread const&) = delete; BasicThread& operator=(BasicThread const&) = delete; /** * Returns a descriptive name for the thread. If not set via * SetName(), a default name is chosen automatically. * * This method is safe to call from any thread. */ const char* Name() const { return name; } /** * Sets a descriptive name for the thread. This should be a string * that's useful in output presented to the user and uniquely * identifies the thread. * * This method must be called only from main thread at initialization * time. */ void SetName(const char* name); /** * Set the name shown by the OS as the thread's description. Not * supported on all OSs. * * Must be called only from the child thread. */ void SetOSName(const char* name); /** * Starts the thread. Calling this methods will spawn a new OS thread * executing Run(). Note that one can't restart a thread after a * Stop(), doing so will be ignored. * * Only Zeek's main thread must call this method. */ void Start(); /** * Signals the thread to prepare for stopping, but doesn't block to * wait for that to happen. Use WaitForStop() for that. * * The method lets Terminating() now return true, it does however not * force the thread to terminate. It's up to the Run() method to to * query Terminating() and exit eventually. * * Calling this method has no effect if Start() hasn't been executed * yet. * * Only Zeek's main thread must call this method. */ void SignalStop(); /** * Waits until a thread has stopped after receiving SignalStop(). * * Calling this method has no effect if Start() hasn't been executed * yet. If this is executed without calling SignalStop() first, * results are undefined. * * Only Zeek's main thread must call this method. */ void WaitForStop(); /** * Returns true if WaitForStop() has been called and finished. * * This method is safe to call from any thread. */ bool Terminating() const { return terminating; } /** * Returns true if Kill() has been called. * * This method is safe to call from any thread. */ bool Killed() const { return killed; } /** * A version of zeek::util::fmt() that the thread can safely use. * * This is safe to call from Run() but must not be used from any * other thread than the current one. */ const char* Fmt(const char* format, ...) __attribute__((format(printf, 2, 3))); ; /** * A version of strerror() that the thread can safely use. This is * essentially a wrapper around strerror_r(). Note that it keeps a * single buffer per thread internally so the result remains valid * only until the next call. */ const char* Strerror(int err); protected: friend class Manager; /** * Entry point for the thread. This must be overridden by derived * classes and will execute in a separate thread once Start() is * called. The thread will not terminate before this method finishes. * An implementation should regularly check Terminating() to see if * exiting has been requested. */ virtual void Run() = 0; /** * Executed with Start(). This is a hook into starting the thread. It * will be called from Zeek's main thread after the OS thread has been * started. */ virtual void OnStart() {} /** * Executed with SignalStop(). This is a hook into preparing the * thread for stopping. It will be called from Zeek's main thread * before the thread has been signaled to stop. */ virtual void OnSignalStop() {} /** * Executed with WaitForStop(). This is a hook into waiting for the * thread to stop. It must be overridden by derived classes and only * return once the thread has indeed finished processing. The method * will be called from Zeek's main thread. */ virtual void OnWaitForStop() = 0; /** * Executed with Kill(). This is a hook into killing the thread. */ virtual void OnKill() {} /** * Destructor. This will be called by the manager. * * Only Zeek's main thread may delete thread instances. * */ virtual ~BasicThread(); /** * Waits until the thread's Run() method has finished and then joins * it. This is called from the threading::Manager. */ void Join(); /** * Kills the thread immediately. One still needs to call Join() * afterwards. * * This is called from the threading::Manager and safe to execute * during a signal handler. */ void Kill(); /** Called by child thread's launcher when it's done processing. */ ZEEK_DISABLE_TSAN void Done(); private: // thread entry function. static void* launcher(void* arg); const char* name; std::thread thread; bool started; // Set to to true once running. std::atomic_bool terminating; // Set to to true to signal termination. std::atomic_bool killed; // Set to true once forcefully killed. // For implementing Fmt(). uint32_t buf_len; char* buf; // For implementing Strerror(). char* strerr_buffer; static uint64_t thread_counter; }; } // namespace zeek::threading