tf::WorkerInterface class

class to configure worker behavior in an executor

The tf::WorkerInterface class allows users to customize worker properties when creating an executor. Examples include binding workers to specific CPU cores or invoking custom methods before and after a worker enters or leaves the work-stealing loop. When you create an executor, it spawns a set of workers to execute tasks with the following logic:

for(size_t n=0; n<num_workers; n++) {
  create_thread([](Worker& worker)

    // enter the scheduling loop
    // Here, WorkerInterface::scheduler_prologue is invoked, if any
    worker_interface->scheduler_prologue(worker);
    
    try {
      while(1) {
        perform_work_stealing_algorithm();
        if(stop) {
          break;
        }
      }
    } catch(...) {
      exception_ptr = std::current_exception();
    }

    // leaves the scheduling loop and joins this worker thread
    // Here, WorkerInterface::scheduler_epilogue is invoked, if any
    worker_interface->scheduler_epilogue(worker, exception_ptr);
  );
}

The example below demonstrates the usage of tf::WorkerInterface to affine a worker to a specific CPU core equal to its id on a Linux platform:

// affine the given thread to the given core index (linux-specific)
bool affine(std::thread& thread, unsigned int core_id) {
  cpu_set_t cpuset;
  CPU_ZERO(&cpuset);
  CPU_SET(core_id, &cpuset);
  pthread_t native_handle = thread.native_handle();
  return pthread_setaffinity_np(native_handle, sizeof(cpu_set_t), &cpuset) == 0;
}

class CustomWorkerBehavior : public tf::WorkerInterface {

  public:
  
  // to call before the worker enters the scheduling loop
  void scheduler_prologue(tf::Worker& w) override {
    printf("worker %lu prepares to enter the work-stealing loop\n", w.id());
    
    // now affine the worker to a particular CPU core equal to its id
    if(affine(w.thread(), w.id())) {
      printf("successfully affines worker %lu to CPU core %lu\n", w.id(), w.id());
    }
    else {
      printf("failed to affine worker %lu to CPU core %lu\n", w.id(), w.id());
    }
  }

  // to call after the worker leaves the scheduling loop
  void scheduler_epilogue(tf::Worker& w, std::exception_ptr) override {
    printf("worker %lu left the work-stealing loop\n", w.id());
  }
};

int main() {
  tf::Executor executor(4, tf::make_worker_interface<CustomWorkerBehavior>());
  return 0;
}

When running the program, we see the following one possible output:

worker 3 prepares to enter the work-stealing loop
successfully affines worker 3 to CPU core 3
worker 3 left the work-stealing loop
worker 0 prepares to enter the work-stealing loop
successfully affines worker 0 to CPU core 0
worker 0 left the work-stealing loop
worker 1 prepares to enter the work-stealing loop
worker 2 prepares to enter the work-stealing loop
successfully affines worker 1 to CPU core 1
worker 1 left the work-stealing loop
successfully affines worker 2 to CPU core 2
worker 2 left the work-stealing loop

Constructors, destructors, conversion operators

~WorkerInterface() defaulted virtual
default destructor

Public functions

void scheduler_prologue(Worker& worker) pure virtual
method to call before a worker enters the scheduling loop
void scheduler_epilogue(Worker& worker, std::exception_ptr ptr) pure virtual
method to call after a worker leaves the scheduling loop

Function documentation

void tf::WorkerInterface::scheduler_prologue(Worker& worker) pure virtual

method to call before a worker enters the scheduling loop

Parameters
worker a reference to the worker

The method is called by the scheduler before entering the work-stealing loop.

void tf::WorkerInterface::scheduler_epilogue(Worker& worker, std::exception_ptr ptr) pure virtual

method to call after a worker leaves the scheduling loop

Parameters
worker a reference to the worker
ptr an pointer to the exception thrown by the scheduling loop

The method is called by the scheduler after leaving the work-stealing loop. Any uncaught exception during the worker's execution will be propagated through the given exception pointer.