170 lines
5.1 KiB
C++
170 lines
5.1 KiB
C++
/*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file is part of zmqpp.
|
|
* Copyright (c) 2011-2015 Contributors as noted in the AUTHORS file.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <thread>
|
|
#include <functional>
|
|
#include <mutex>
|
|
#include "context.hpp"
|
|
#include "socket.hpp"
|
|
|
|
namespace zmqpp
|
|
{
|
|
|
|
/**
|
|
* An actor is a thread with a pair socket connected to its parent.
|
|
* It aims to be similar to CMZQ's zactor.
|
|
*
|
|
* From the parent thread, instancing an actor will spawn a new thread, and
|
|
* install
|
|
* a pipe between those two threads.
|
|
* 1. The parent's end of the pipe can be retrieved by calling pipe() on
|
|
* the actor.
|
|
* 2. The child's end is passed as a parameter to the routine executed in
|
|
* the child's thread.
|
|
*
|
|
* You don't have to manage the 2 PAIR sockets. The parent's one will be
|
|
* destroyed when the actor dies, and the child's end is taken care
|
|
* of when the user routine ends.
|
|
*
|
|
* @note
|
|
* About user-supplied routine return value:
|
|
* 1. If the supplied routine returns true, signal::ok will be send to the
|
|
* parent.
|
|
* 2. If it returns false, signal::ko is send instead.
|
|
*
|
|
* @note There is a simple protocol between actor and parent to avoid
|
|
* synchronization problem:
|
|
* 1. The actor constructor expect to receive either signal::ok or
|
|
* signal::ko before it returns.
|
|
* 2. When sending signal::stop (actor destruction or by calling stop()),
|
|
* we expect a response: (either signal::ko or signal::ok).
|
|
* This response is used to determine if stop() will return true or
|
|
* false when in blocking mode.
|
|
*/
|
|
class actor
|
|
{
|
|
public:
|
|
/**
|
|
* The user defined function type.
|
|
*/
|
|
typedef std::function<bool(socket *pipe)> ActorStartRoutine;
|
|
|
|
/**
|
|
* Create a new actor. This will effectively create a new thread and runs
|
|
* the user supplied routine.
|
|
* The constructor expect a signal from the routine before returning.
|
|
*
|
|
* Expect to receive either signal::ko or signal::ok before returning.
|
|
* If it receives signal::ko, it will throw.
|
|
* @param routine to be executed.
|
|
*/
|
|
actor(ActorStartRoutine routine);
|
|
actor(const actor &) = delete;
|
|
|
|
/**
|
|
* Move constructor. The other actor will not be usable anymore.
|
|
* Its pipe() method shall return null, and stop() will do nothing.
|
|
*/
|
|
actor(actor &&o);
|
|
|
|
/**
|
|
* Move-assignment operator.
|
|
* @see move constructor.
|
|
*/
|
|
actor &operator=(actor &&o);
|
|
|
|
virtual ~actor();
|
|
|
|
/**
|
|
* @return pointer to the parent's end of the pipe
|
|
*/
|
|
socket *pipe();
|
|
|
|
/**
|
|
* @return const pointer to the parent's end of the pipe
|
|
*/
|
|
const socket *pipe() const;
|
|
|
|
/**
|
|
* Sends signal::stop to the actor thread.
|
|
* The actor thread shall stop as soon as possible.
|
|
* The return value is only relevant when block is true. If block is false
|
|
* (the default), this method
|
|
* will return true.
|
|
*
|
|
* It is safe to call stop() multiple time (to ask the actor to shutdown,
|
|
* and then a bit
|
|
* later call stop(true) to make sure it is really stopped)
|
|
*
|
|
* @note calling this method on an "empty" actor (after it was moved) will
|
|
* return
|
|
* false and nothing will happen.
|
|
* @param block whether or not we wait until the actor thread stops.
|
|
* @return a boolean indicating whether or not the actor successfully
|
|
* shutdown.
|
|
*
|
|
* @note Exception thrown during initialization (before sending ko/ok) are
|
|
* rethrown in the actor's constructor.
|
|
*/
|
|
bool stop(bool block = false);
|
|
|
|
private:
|
|
/**
|
|
* Call a user defined function and performs cleanup once it returns.
|
|
* We use a copy of child_pipe_ here; this is to avoid a race condition
|
|
* where the
|
|
* destructor would be called before start_routine finishes but after it
|
|
* sent signal::ok / signal::ko.
|
|
* The actor object would be invalid (because already destroyed).
|
|
*
|
|
* @param child a copy of child_pipe_.
|
|
* @param routine user routine that will be called
|
|
*/
|
|
void start_routine(socket *child, ActorStartRoutine routine);
|
|
|
|
/**
|
|
* Bind the parent socket and return the endpoint used.
|
|
* Since endpoint are generated and have to be tested for availability
|
|
* this method is responsible for finding a valid endpoint to bind to.
|
|
*/
|
|
std::string bind_parent();
|
|
|
|
/**
|
|
* The parent thread socket.
|
|
* This socket will be closed and freed by the actor destructor.
|
|
*/
|
|
socket *parent_pipe_;
|
|
|
|
/**
|
|
* The child end of the pipe.
|
|
* It is closed and freed when the routine ran by the actor ends.
|
|
*/
|
|
socket *child_pipe_;
|
|
|
|
/**
|
|
* This static, per process zmqpp::context, is used to connect PAIR socket
|
|
* between Actor and their parent thread.
|
|
*/
|
|
static context actor_pipe_ctx_;
|
|
|
|
mutable std::mutex mutex_;
|
|
|
|
std::exception_ptr eptr_;
|
|
|
|
/**
|
|
* Keeps track of the status of the actor thread.
|
|
*/
|
|
bool stopped_;
|
|
|
|
bool retval_;
|
|
};
|
|
}
|