emsApplication/sdk/include/zmqpp/actor.hpp

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_;
};
}