Monday, May 13, 2013

Version 0.7 released

Version 0.7 of libcppa has just been released. For the first time in an official release, this version breaks code compatibility with earlier versions (this was not an easy step and caused some discussion). When calling spawn with a function, the new default implementation is event-based. Hence, your actor should set its behavior using become and not use functions like receive. However, it is pretty easy to port your existing code. To opt-in to the "old" behavior, use spawn<blocking_api>(fun_name).

Among improvements and optimizations, this version includes a few new functions as well:
  • Priority-aware messaging (opt-in feature)
  • OpenCL-based actors (must be enabled using '--with-opencl')

Please note that the manual has received a huge update due to the changed default behavior of spawn and has not caught up to the new features yet.

Friday, February 22, 2013

Version 0.6 released

Version 0.6 of libcppa has just been released. This release brings several improvements to the synchronous messaging API. Please read more about features like functor-only usage and continuations in the revamped Section 7 of the manual.

Among several smaller improvements, this release also includes:

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.