Saturday, November 9, 2013

Moving Forward: Type Safety & Actors

Type safety is a topic that comes up very frequently when talking to C++ developers about libcppa - and actors in general. Indeed, the major concern about libcppa at the C++Now 2013 conference was that it does not provide a type-safe messaging interface. I'm not going to discuss pros and cons of dynamic and static typing. Both approaches do have their benefits. In libcppa, you can use atoms to "create" new message types on the fly and then pattern match on the receiver side. In this way, it is very lightweight to introduce new message types to a system, as you don't have to maintain header files for your message types. On the flip side, "wrong" message types are detected at runtime. When sending arguments in the wrong order, your pattern on the receiver side will not match. Naturally, most C++ developers expect the compiler to detect those kind of bugs.

Version 0.8 introduced strongly-typed actors to give developers a choice. Unfortunately, libcppa is now a two-class society. Even worse, it is indeed rather easy to get an untyped reference to a strongly-typed actor. But let's step back and take a look at the issues individually.
  1. Second-class Typed Actors: Typed actors are not allowed to use guard expressions. This in itself is not a problem. The problem is how libcppa signals system events. For example, when monitoring an actor, you'll receive a "down" message consisting of the atom 'DOWN' and the exit reason as uint32. Since typed actors are not allowed to use guards, the only thing they could possibly do is to define a handler for all messages with an atom as first element followed by an uint32. There are 1064 possible atoms. As a consequence, libcppa needs to introduce message types for system messages. This will break code and I would prefer not to, but it's better to break some code that's easy to repair than to have a broken design.
  2. Separated APIs: To spawn a typed actor, we use functions that return the behavior. For untyped actors, we can use void functions that call 'become' internally. Even worse, 'become' now can throw - when called from a typed actor. It's probably for the best to move 'become' from the namespace cppa to the class event_based_actor and always use function that return a behavior.
  3. Type-safety Not Enforced: As you might know if you've ever used libcppa: An actor can get a handle to itself by using 'self' and a handle to any actor that sent a message to it by calling 'self->last_sender()'. This handle is of course untyped. So, not only can a typed actor get an untyped handle to itself, each actor it is communicating with has an untyped handle to it. The only way to enforce type safety is to either remove 'self' completely from the API or to change its type to something not convertible to actor_ptr, so that you can only access member functions like 'trap_exits'. The member function 'last_sender()' then also would have to go or to return only a logical address that you can use for monitoring and linking, but not for message passing.
  4. Missing Features: This is not a design issue, but libcppa 0.9 will of course implement proper remote communication for typed actors.
libcppa started as a DSL for Erlang-style actor programming in C++. With version 0.9, libcppa will (hopefully) evolve into a unique actor system that supports untyped and typed actors side by side. Stay tuned. :)

Tuesday, October 15, 2013

Version 0.8 released!

Version 0.8 of libcppa has just been released. The biggest change for existing code: The function reply is deprecated. Actors now do automatically reply to a message by returning a value from the message handler. This change does not only make your code cleaner, but this is also the only way a new kind of actors - strongly typed actors - are allowed to reply to incoming messages. Typed actors is the most frequently requested feature for libcppa - and have finally arrived. You can create typed actors using the function spawn_typed:

auto p0 = spawn_typed(
  on_arg_match >> [](int a, int b) {
   return static_cast<double>(a) * b;
  },
  on_arg_match >> [](double a, double b) {
    return make_cow_tuple(a * b, a / b);
  }
);


As you can see, the argument to spawn_typed is a match expression rather than a function. This is because a typed actor is not allowed to change its behavior. The messaging interface is burnt into its type. In the example above, the type of p0 is typed_actor_ptr<replies_to<int, int>::with<double>,replies_to<double, double>::with<double, double>>. In this way, the compiler is now able to type-check your messages:

send(p0, 42); // <- compiler error

send(p0, 42, 24); // <- ok

sync_send(p0, 1, 2, 3).then ... // <- compiler error

sync_send(p0, 1, 2).then(
  [](float) { ... } // <- compiler error: expected double
);

sync_send(p0, 1, 2).then(
  [](double d) { ... } // <- ok
);


Typed actors are not "feature complete" yet, i.e., typed actors cannot be published and it is not possible to use priorities when sending a message to a typed actor. However, this is just a matter of time. To learn more about typed actors, visit Section 15 of the 0.8 manual.

As if typed actors were not enough, version 0.8 includes yet another new kind of actors: brokers. A broker connects your actor system to any other network protocol. The new release includes an example featuring Google Protobuf: examples/remote_actors/protobuf_broker.cpp.

A small addition that is worth mentioning is the new exit reason exit_reason::user_shutdown. This reason can be used whenever you force actors to quit as part of application shutdown or for shutting down parts of your system that are no longer necessary.

Tuesday, May 21, 2013

Version 0.7.1 released

Version 0.7.1 of libcppa has just been released. This release fixes some bugs and improves compatibility with GCC 4.8.

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.