/* * 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 #include #include #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 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_; }; }