Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding the stateful_actor

I've read the some usage of stateful_actor under examples/curl/curl_fuse.cpp and libcaf_core/test/stateful_actor.cpp. It looks like the stateful_actor<State> can bind some states for actor by declaring fields in State struct. It is very useful.

Can we declare the states as fields in class-based actor to get the same effects? Or there is some special handling in the stateful_actor (e.g. thread-safety access) ?

Do the actors in the following example provide the same functionality?

/* Class based actor */
struct ClassCounter : caf::event_based_actor
{
    caf::behavior make_behavior() override { 
        return { 
            [=](inc_atom){ i += 1; }
          , [=](ret_atom){ return i; }
        };
    }

    const char* name() const override { return "I am class-based"; }
    int i = 0;
};

/* Stateful actor */
struct CounterState
{
    caf::local_actor* self;
    int i = 0;
    const char* name = "I am stateful";
};

caf::behavior StatefulCounter(caf::stateful_actor<CounterState>* self)
{
    return {
        [=](inc_atom){ self->state.i += 1; }
      , [=](ret_atom){ return self->state.i; }
    };
};
like image 324
Chen OT Avatar asked Dec 21 '15 07:12

Chen OT


1 Answers

Can we declare the states as fields in class-based actor to get the same effects? Or there is some special handling in the stateful_actor (e.g. thread-safety access) ?

The runtime of CAF assumes actors to be isolated, i.e., only an actor itself is allowed to access its state. Hence, access to the state of an actor is never synchronized. Inter-actor communication uses message passing, thus avoiding race conditions by design.

Stateful actors allow programmers to write less classes (and thus less boilerplate code), but there is also one notable difference between ClassCounter and StatefulCounter: lifetime of variables. In ClassCounter, the member variable i lives until the destructor of ClassCounter is called. Since actors are reference counted, the destructor runs if no reference to it is left, i.e., no actor or actor_addr handle to the actor exists. In StatefulCounter, the CounterState member is constructed if the actor is initialized and destroyed if it terminates.

The state only exists as long as an actor is alive. This is particularly useful for breaking cycles. If you have the two actors A and B, you have a cycle if A holds a reference to B via a member variable and vice versa. Using stateful actors instead breaks this cycle. A will release its reference to B automatically on exit and vice versa.

like image 68
neverlord Avatar answered Nov 04 '22 22:11

neverlord