Introduce concept of Worker tasks

A Worker::Task is an object that can be sent to a worker for
execution. The task is sent to the worker using the messaging
mechanism where the `execute` function of the task will be
called in the thread context of the worker.

There are two kinds of tasks; regular tasks and disposable tasks.
The former are just sent to the worker for execution while the
latter are sent and subsequently disposed of, once the task has
been executed.

A disposable task can be sent to either one worker or to all
workers. In the latter case, the task will be deleted once it
has been executed by all workers.

A semaphore can be associated with a regular task. Once the task
has been executed by the worker, the semaphore will automatically
be posted. That way, it is trivial to send a task for execution
to a worker and wait until the task has been executed. For instance:

    Semaphore sem;
    MyTask task;

    pWorker->execute(&task, &sem);
    sem.wait();

    const MyResult& result = task.result();

The low level mechanism for posting and broadcasting messages will
be removed.
This commit is contained in:
Johan Wikman
2017-04-24 14:38:32 +03:00
parent 172cdbc5a3
commit 8174690f77
5 changed files with 289 additions and 0 deletions

View File

@ -13,14 +13,18 @@
*/
#include <maxscale/cppdefs.hh>
#include <memory>
#include <maxscale/platform.h>
#include "messagequeue.hh"
#include "poll.h"
#include "worker.h"
#include "workertask.hh"
namespace maxscale
{
class Semaphore;
struct WORKER_STATISTICS
{
WORKER_STATISTICS()
@ -61,6 +65,8 @@ class Worker : public MXS_WORKER
public:
typedef WORKER_STATISTICS STATISTICS;
typedef WorkerTask Task;
typedef WorkerDisposableTask DisposableTask;
enum state_t
{
@ -244,6 +250,76 @@ public:
return m_should_shutdown;
}
/**
* Executes a task in the context of a Worker.
*
* @param pTask The task to be executed.
* @param pSem If non-NULL, will be posted once the task's `execute` return.
*
* @return True if the task could be *posted*, false otherwise.
*
* @attention The instance must remain valid for as long as it takes for the
* task to be transferred to the worker and its `execute` function
* to be called.
*
* The semaphore can be used for waiting for the task to be finished.
*
* @code
* Semaphore sem;
* MyTask task;
*
* pWorker->execute(&task, &sem);
* sem.wait();
*
* MyResult& result = task.result();
* @endcode
*/
bool execute(Task* pTask, Semaphore* pSem = NULL);
/**
* Executes a disposable task in the context of a Worker.
*
* @param pTask The task to be executed.
*
* @return True if the task could be *posted*, false otherwise.
*
* @attention Once the task has been executed, it will be deleted.
*/
bool execute(std::auto_ptr<DisposableTask> sTask);
/**
* Executes a task on all workers.
*
* @param pTask The task to be executed.
* @param pSem If non-NULL, will be posted once per worker when the task's
* `execute` return.
*
* @return How many workers the task was posted to.
*
* @attention The very same task will be posted to all workers. The task
* should either not have any sharable data or then it should
* have data specific to each worker that can be accessed
* without locks.
*/
static size_t execute_on_all(Task* pTask, Semaphore* pSem = NULL);
/**
* Executes a task on all workers.
*
* @param pTask The task to be executed.
*
* @return How many workers the task was posted to.
*
* @attention The very same task will be posted to all workers. The task
* should either not have any sharable data or then it should
* have data specific to each worker that can be accessed
* without locks.
*
* @attention Once the task has been executed by all workers, it will
* be deleted.
*/
static size_t execute_on_all(std::auto_ptr<DisposableTask> sTask);
/**
* Post a message to a worker.
*
@ -337,6 +413,8 @@ private:
static Worker* create(int id, int epoll_listener_fd);
bool execute_disposable(DisposableTask* pTask);
void handle_message(MessageQueue& queue, const MessageQueue::Message& msg); // override
static void thread_main(void* arg);