Monday, January 7, 2013

Actor Companions

In the previous post about actors with Qt, we have already briefly discussed the concept of actor companions, but this post presents the general approach.

What is it Good For?

libcppa automatically converts threads to actors if needed (using thread-local storage), whenever a thread uses a message passing primitive such as send(). However, sometimes this conversion is too coarse-grained. Simply put, whenever several independent entities share a thread, but should be addressed individually as actors, we need actor companions. The classical example is a widget. The thread is owned by the event loop, which manages any number of widgets, why we cannot rely on libcppa's automatic thread conversion. If you need to support a library wich internally schedules its entities (perhaps based on green threads or coroutines) - or if you just want to learn more about libcppa, this post is for you.

Background

If you want to mix libcppa with an other framework, multiple inheritance is not an option. Usually, each framework manages its entities in an incompatible way. For example, Qt uses hierarchical object ownership by default. This obviously interferes with the reference counting strategy of libcppa. Even if the framework uses reference counts, you will end up having two reference counts in your object. Instead of using multiple inheritance, we use co-existing objects (companions), which serve as gateway. The companion implements the actor interface of libcppa but does not have a mailbox. It forwards all messages it receives to its parent.

Mixing in a Solution

Actor companions are enabled by the mixin actor_companion_mixin. The mixin provides the member functions actor_ptr as_actor(), void set_message_handler(...), and void handle_message(const message_pointer&). It also has one pure virtual member function: void new_message(message_pointer). This function must be implemented in a thread-safe way. The type message_pointer is a smart pointer holding a tuple along with meta data such as sender information. There is no need to interact with a message_pointer, as the message handler does everything necessary.

A Working Example

template<typename Base, int EventId = static_cast<int>(QEvent::User + 31337)>
class actor_widget_mixin : public actor_companion_mixin<Base> {

  typedef actor_companion_mixin<Base> super;

  typedef typename super::message_pointer message_pointer;

 public:

  template<typename... Args>
  actor_widget_mixin(Args&&... args) : super(std::forward<Args>(args)...) { }

  struct event_type : public QEvent {

    message_pointer msg;

    event_type(message_pointer mptr)
    : QEvent(static_cast<QEvent::Type>(EventId)), msg(std::move(mptr)) { }

  };

  virtual bool event(QEvent* event) {
    if (event->type() == static_cast<QEvent::Type>(EventId)) {
      auto ptr = dynamic_cast<event_type*>(event);
      if (ptr) {
        this->handle_message(ptr->msg);
        return true;
      }
    }
    return super::event(event);
  }

 protected:

  virtual void new_message(message_pointer ptr) {
    qApp->postEvent(this, new event_type(std::move(ptr)));
  }

};

The code shown above is the actual code from cppa/qtsupport/actor_widget_mixin.hpp. As you can see, it is Qt specific, but straightforward. Our "derived mixin" has no constructor arguments and only forwards all arguments to the base constructor (line 11).

The crucial point of this implementation is to convert libcppa messages to Qt events in the implementation of new_message (line 35). The runtime system of Qt will then call the event member function (line 22) later on from inside the event loop. All our implementation has to do is to unwrap the message_pointer and pass it to handle_message.

Once we have called handle_message, our message handler gets invoked – with self pointing to the companion. This way, we can use send and reply as usual in the handler. Even though the code is Qt specific, it should be straightforward to implement a comparable mixin to support your framework of choice.

Version 0.5.5 released

Version 0.5.5 of libcppa has just been released. This release brings several bugfixes and performance improvements as well as aout – A Thread-Safe Wrapper for std::cout.

Wednesday, November 14, 2012

Using Actors with Qt

Actor programming is nice and makes our lives easier, but at some point, we have to display the output of our program to the user. Not everything is a command line tool. Hence, we have to use some sort of GUI library, which raises the question how we pass messages from actors - safely! - to the GUI at runtime. Well, what if we could send an ordinary message to the GUI as if it's an actor? What if the GUI can send us ordinary messages whenever the user pushes some buttons? Briefly speaking, can we treat GUI elements as actors? Luckily, the answer is yes! We have to write some gluecode, but in fact, you can treat everything as an actor with libcppa.

Getting Started

In this article, we will focus on the Qt library, as it is the open source GUI library of choice for C++ developers. In the next article, we will have a look at the general concept of actor companions. An actor companion is an actor that co-exists with another object. In our case, this object is a QWidget-base class. The companion is used to receive and send messages, but it does not have any behavior itself. All it takes to enable a class to have such a companion is to use the actor_widget_mixin.

The following class implements a simple group-based chat widget with a QTextEdit for text output and a QLineEdit for user input (chat messages). The full source code can be found in the examples folder in the Git repository.
#include <QWidget>
#include "cppa/qtsupport/actor_widget_mixin.hpp"

class ChatWidget : public cppa::actor_widget_mixin<QWidget> {

  Q_OBJECT

  typedef cppa::actor_widget_mixin<QWidget> super;

  // ...

 public:

  ChatWidget(QWidget* parent = nullptr, Qt::WindowFlags f = 0);

  // ...

 private:

  std::string m_name;
  cppa::group_ptr m_chatroom;

};
The mixin adds the following member functions to ChatWidget:
  • actor_ptr as_actor()
    returns the companion of this widget
  • set_message_handler
    sets the partial function for message processing

Handle Messages to the Widget

In our example, the widget should handle 'join', 'setName', and 'quit' messages as well as display chat messages (received as std::string). We encode our message handling directly into the constructor of ChatWidget:
ChatWidget::ChatWidget(QWidget* parent, Qt::WindowFlags f) : super(parent, f) {
  set_message_handler (
    on(atom("join"), arg_match) >> [=](const group_ptr& what) {
      if (m_chatroom) {
        send(m_chatroom, m_name + " has left the chatroom");
        self->leave(m_chatroom);
      }
      self->join(what);
      print(("*** joined " + to_string(what)).c_str());
      m_chatroom = what;
      send(what, m_name + " has entered the chatroom");
    },
    on(atom("setName"), arg_match) >> [=](string& name) {
      send(m_chatroom, m_name + " is now known as " + name);
      m_name = std::move(name);
      print("*** changed name to "
            + QString::fromUtf8(m_name.c_str()));
    },
    on(atom("quit")) >> [=] {
      close(); // close widget
    },
    on<string>() >> [=](const string& txt) {
      // don't print own messages
      if (self != self->last_sender()) {
        print(QString::fromUtf8(txt.c_str()));
      }
    }
  );
}

Pitfalls & Things to Know

The code is pretty straight forward if you are already familiar with libcppa, but there is one important thing to note: Unhandled messages are lost. Although the widget is able to handle libcppa messages now, it does not have a mailbox. Messages are delivered to the widget as QEvent objects that are disposed afterwards.

The second thing to note is that you should never use self outside the message handler. This includes using functions such as send, because self is internally used to determine the sender of a message. The reason to this limitation is that libcppa's self "pointer" is thread-local. Using self would therefore convert the Qt thread your widget runs in to an actor, but it wouldn't address your widget's companion.

To send a message from outside of your handler, you have to tell libcppa who is the sender of the message by hand:
send_as(as_actor(), m_chatroom, "hello world!");


Have fun!

Friday, October 26, 2012

Version 0.5 released

Version 0.5 of libcppa has just been released. This release brings Log4j-like logfiles to libcppa developers (must be enabled at compile time), support for user-defined communication protocols, and actor companions.

Each class using the actor_companion_mixin has such an actor companion, which provides an easy way for active non-actor objects, i.e., objects with an own thread or event loop, to send messages to/as actor. The default use case for this feature is to treat GUI elements - widgets - as actors. A more specific mixin for Qt widgets is on its way.

By the way, libcppa now has more than a thousand commits on GitHub! :-)

Wednesday, August 22, 2012

Version 0.4 released

The main feature of version 0.4 is a streamlined and bugfixed network layer. Of course, there are some new features as well:
  • Support for User-Defined Network Layer
    The functions publish and remote_actor were overloaded to allow user-defined network layers such as OpenSSL; see this mailing list discussion and the doxygen documentation of util::input_stream, util::output_stream and util::acceptor for more details.
  • Shutdown Function
    The new shutdown() function closes all network connections, stops the scheduler and deletes all of libcppa's singletons. It is strongly recommended to call this function before returning from main(), especially if you are connected to remote actors.
  • Syntactic Sugar for Synchronous Messaging
    Synchronous message handling using futures is flexible but sometimes too verbose.
    auto future = sync_send(...);
    handle_response(future, ...);  // event-based API
    receive_response(future, ...); // blocking API
    Version 0.4 provides some syntactic sugar to make your code more compact. Whenever you send a message and immediately wait for the response, you can write the following instead.
    sync_send(...).then(...);  // event-based API
    sync_send(...).await(...); // blocking API
    Furthermore, there is a feature request for a continuation-passing style API to enable developers to easily encode "send X then receive Y then send Z" message flows (see Issue 58 on GitHub).
  • Local Groups & Remote Actors
    Local groups, as returned by calling group::get("local", ...); or group::anonymous(), are now not-so-local. It is possible to send a local group to a remote actor and let the remote actor join the group. Whenever an actor sends a message to the group, the message is send back to the owning process if needed and forwarded to all subscribers from there, including remote actors. This approach certainly does not scale for largely distributed groups, since it is N-times unicast*. However, it paves to path for more use cases of "local" groups and we are working on scalable group communication as well.

    * The N in this case is the number of hosts and not the number of remote actors.

Friday, August 10, 2012

New Functions in 0.3.3

Among some bugfixes, version 0.3.3 also includes a few new functions to add 'missing' features:
  • Forwarding of Messages
    libcppa lacked an easy and transparent way to forward messages. The new function forward_to finally adds this functionality. Furthermore, forwarding a synchronous messages is not possible without this function. Read more about forwarding in Section 5.4 of the manual.
  • Messaging with Tuples
    Matthias Vallentin pointed out that the API was somewhat inconsistent, since it did not provide functions to use a tuple as response message. We have added the following functions in version 0.3.3 to treat tuples as first-class citizen: send_tuple, sync_send_tuple, reply_tuple, delayed_send_tuple and delayed_reply_tuple.
  • Manual
    The manual is now included to the source distribution as manual.pdf and states the libcppa version.
  • Doxygen
    CMake checks whether doxygen is available on your system and adds an optional "doc" target to the Makefile. You can create your own local version of the doxygen documentation by running "make doc". Open the file html/index.html afterwards.

Thursday, July 26, 2012

libcppa on Mountain Lion

I have just upgraded my Mac and I have good news for all Apple users out there!

If you have switched to Mountain Lion or have plans to do so, you will find clang++ in version 4.0 after installing XCode. Hence, there is finally no need to get a recent compiler from an external source. libcppa 0.3 compiles and runs like a charm after installing CMake and Boost from MacPorts.