mirror of
https://github.com/barkeser2002/flower.git
synced 2026-09-25 22:18:44 +03:00
261 lines
6.5 KiB
C++
261 lines
6.5 KiB
C++
#pragma once
|
|
|
|
#include "declarations.hpp"
|
|
#include "tsq.hpp"
|
|
#include "notifier.hpp"
|
|
|
|
/**
|
|
@file worker.hpp
|
|
@brief worker include file
|
|
*/
|
|
|
|
namespace tf {
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Class Definition: Worker
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/**
|
|
@class Worker
|
|
|
|
@brief class to create a worker in an executor
|
|
|
|
The class is primarily used by the executor to perform work-stealing algorithm.
|
|
Users can access a worker object and alter its property
|
|
(e.g., changing the thread affinity in a POSIX-like system)
|
|
using tf::WorkerInterface.
|
|
*/
|
|
class Worker {
|
|
|
|
friend class Executor;
|
|
friend class WorkerView;
|
|
|
|
public:
|
|
|
|
/**
|
|
@brief queries the worker id associated with its parent executor
|
|
|
|
A worker id is a unsigned integer in the range <tt>[0, N)</tt>,
|
|
where @c N is the number of workers spawned at the construction
|
|
time of the executor.
|
|
*/
|
|
inline size_t id() const { return _id; }
|
|
|
|
/**
|
|
@brief acquires a pointer access to the underlying thread
|
|
*/
|
|
inline std::thread* thread() const { return _thread; }
|
|
|
|
/**
|
|
@brief queries the size of the queue (i.e., number of enqueued tasks to
|
|
run) associated with the worker
|
|
*/
|
|
inline size_t queue_size() const { return _wsq.size(); }
|
|
|
|
/**
|
|
@brief queries the current capacity of the queue
|
|
*/
|
|
inline size_t queue_capacity() const { return static_cast<size_t>(_wsq.capacity()); }
|
|
|
|
private:
|
|
|
|
size_t _id;
|
|
size_t _vtm;
|
|
Executor* _executor;
|
|
std::thread* _thread;
|
|
Notifier::Waiter* _waiter;
|
|
std::default_random_engine _rdgen { std::random_device{}() };
|
|
TaskQueue<Node*> _wsq;
|
|
Node* _cache;
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Class Definition: PerThreadWorker
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/**
|
|
@private
|
|
*/
|
|
//struct PerThreadWorker {
|
|
//
|
|
// Worker* worker;
|
|
//
|
|
// PerThreadWorker() : worker {nullptr} {}
|
|
//
|
|
// PerThreadWorker(const PerThreadWorker&) = delete;
|
|
// PerThreadWorker(PerThreadWorker&&) = delete;
|
|
//
|
|
// PerThreadWorker& operator = (const PerThreadWorker&) = delete;
|
|
// PerThreadWorker& operator = (PerThreadWorker&&) = delete;
|
|
//};
|
|
|
|
/**
|
|
@private
|
|
*/
|
|
//inline PerThreadWorker& this_worker() {
|
|
// thread_local PerThreadWorker worker;
|
|
// return worker;
|
|
//}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Class Definition: WorkerView
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/**
|
|
@class WorkerView
|
|
|
|
@brief class to create an immutable view of a worker in an executor
|
|
|
|
An executor keeps a set of internal worker threads to run tasks.
|
|
A worker view provides users an immutable interface to observe
|
|
when a worker runs a task, and the view object is only accessible
|
|
from an observer derived from tf::ObserverInterface.
|
|
*/
|
|
class WorkerView {
|
|
|
|
friend class Executor;
|
|
|
|
public:
|
|
|
|
/**
|
|
@brief queries the worker id associated with its parent executor
|
|
|
|
A worker id is a unsigned integer in the range <tt>[0, N)</tt>,
|
|
where @c N is the number of workers spawned at the construction
|
|
time of the executor.
|
|
*/
|
|
size_t id() const;
|
|
|
|
/**
|
|
@brief queries the size of the queue (i.e., number of pending tasks to
|
|
run) associated with the worker
|
|
*/
|
|
size_t queue_size() const;
|
|
|
|
/**
|
|
@brief queries the current capacity of the queue
|
|
*/
|
|
size_t queue_capacity() const;
|
|
|
|
private:
|
|
|
|
WorkerView(const Worker&);
|
|
WorkerView(const WorkerView&) = default;
|
|
|
|
const Worker& _worker;
|
|
|
|
};
|
|
|
|
// Constructor
|
|
inline WorkerView::WorkerView(const Worker& w) : _worker{w} {
|
|
}
|
|
|
|
// function: id
|
|
inline size_t WorkerView::id() const {
|
|
return _worker._id;
|
|
}
|
|
|
|
// Function: queue_size
|
|
inline size_t WorkerView::queue_size() const {
|
|
return _worker._wsq.size();
|
|
}
|
|
|
|
// Function: queue_capacity
|
|
inline size_t WorkerView::queue_capacity() const {
|
|
return static_cast<size_t>(_worker._wsq.capacity());
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Class Definition: WorkerInterface
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/**
|
|
@class WorkerInterface
|
|
|
|
@brief class to configure worker behavior in an executor
|
|
|
|
The tf::WorkerInterface class lets users interact with the executor
|
|
to customize the worker behavior,
|
|
such as calling custom methods before and after a worker enters and leaves
|
|
the loop.
|
|
When you create an executor, it spawns a set of workers to run tasks.
|
|
The interaction between the executor and its spawned workers looks like
|
|
the following:
|
|
|
|
for(size_t n=0; n<num_workers; n++) {
|
|
create_thread([](Worker& worker)
|
|
|
|
// pre-processing executor-specific worker information
|
|
// ...
|
|
|
|
// enter the scheduling loop
|
|
// Here, WorkerInterface::scheduler_prologue is invoked, if any
|
|
|
|
while(1) {
|
|
perform_work_stealing_algorithm();
|
|
if(stop) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// leaves the scheduling loop and joins this worker thread
|
|
// Here, WorkerInterface::scheduler_epilogue is invoked, if any
|
|
);
|
|
}
|
|
|
|
@note
|
|
Methods defined in tf::WorkerInterface are not thread-safe and may be
|
|
be invoked by multiple workers concurrently.
|
|
|
|
*/
|
|
class WorkerInterface {
|
|
|
|
public:
|
|
|
|
/**
|
|
@brief default destructor
|
|
*/
|
|
virtual ~WorkerInterface() = default;
|
|
|
|
/**
|
|
@brief method to call before a worker enters the scheduling loop
|
|
@param worker a reference to the worker
|
|
|
|
The method is called by the constructor of an executor.
|
|
*/
|
|
virtual void scheduler_prologue(Worker& worker) = 0;
|
|
|
|
/**
|
|
@brief method to call after a worker leaves the scheduling loop
|
|
@param worker a reference to the worker
|
|
@param ptr an pointer to the exception thrown by the scheduling loop
|
|
|
|
The method is called by the constructor of an executor.
|
|
*/
|
|
virtual void scheduler_epilogue(Worker& worker, std::exception_ptr ptr) = 0;
|
|
|
|
};
|
|
|
|
/**
|
|
@brief helper function to create an instance derived from tf::WorkerInterface
|
|
|
|
@tparam T type derived from tf::WorkerInterface
|
|
@tparam ArgsT argument types to construct @c T
|
|
|
|
@param args arguments to forward to the constructor of @c T
|
|
*/
|
|
template <typename T, typename... ArgsT>
|
|
std::shared_ptr<T> make_worker_interface(ArgsT&&... args) {
|
|
static_assert(
|
|
std::is_base_of_v<WorkerInterface, T>,
|
|
"T must be derived from WorkerInterface"
|
|
);
|
|
return std::make_shared<T>(std::forward<ArgsT>(args)...);
|
|
}
|
|
|
|
} // end of namespact tf -----------------------------------------------------
|
|
|
|
|