zeek/src/threading/BasicThread.h
Benjamin Bannier f5a76c1aed Reformat Zeek in Spicy style
This largely copies over Spicy's `.clang-format` configuration file. The
one place where we deviate is header include order since Zeek depends on
headers being included in a certain order.
2023-10-30 09:40:55 +01:00

217 lines
6.2 KiB
C++

#pragma once
#include "zeek/zeek-config.h"
#include <unistd.h>
#include <atomic>
#include <cstdint>
#include <iosfwd>
#include <thread>
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