dive into sobjectizer 5.5. second part. states

52
Dive into SObjectizer-5.5 SObjectizer Team, May 2015 Second Part: Agent’s States

Upload: yauheni-akhotnikau

Post on 16-Aug-2015

48 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Dive into SObjectizer 5.5. Second part. States

Dive into SObjectizer-5.5

SObjectizer Team, May 2015

Second Part: Agent’s States

Page 2: Dive into SObjectizer 5.5. Second part. States

The main features of SObjectizer-5.5, like agents, cooperations, messages/signal, mboxes, dispatchers and delayed delivery, were described in the previous part.

The next important feature is agent’s state.

SObjectizer Team, May 2015

Page 3: Dive into SObjectizer 5.5. Second part. States

Agent in SObjectizer is a finite-state machine.

The behaviour of an agent depends on the current state of the agent and the received message.

SObjectizer Team, May 2015

Page 4: Dive into SObjectizer 5.5. Second part. States

An agent can receive and process different messages in each state. In other words an agent can receive a message in one state but ignore it in another state.

Or, if an agent receives the same message in several states, it can handle the message differently in each state.

SObjectizer Team, May 2015

Page 6: Dive into SObjectizer 5.5. Second part. States

Let’s imagine an agent which works as a handler of HTTP GET requests.

It receives messages with HTTP requests parameters and makes appropriate HTTP response.

There could be several states for request processor with different reaction on messages with HTTP requests...

SObjectizer Team, May 2015

Page 7: Dive into SObjectizer 5.5. Second part. States

There is a normal state in which the agent fully handles every request and sends a normal response back.

There could be a “pause” state in which the agent ignores all incoming requests (so the request initiator will receive HTTP 408 status)

And...

SObjectizer Team, May 2015

Page 8: Dive into SObjectizer 5.5. Second part. States

...there could be an “overloaded” state in which the agent will process requests differently: instead of making full processing the agent will quickly generate a response with HTTP 503 status and special text.

The agent could switch from normal state to “overloaded” when a peak of requests is detected. And the agent will switch back to its normal state after the peak of incoming requests is processed.

SObjectizer Team, May 2015

Page 9: Dive into SObjectizer 5.5. Second part. States

There is a special class for representing agent’s states: so_5::rt::state_t.

The definition of new state for an agent means creation of new instance of state_t.

SObjectizer Team, May 2015

Page 10: Dive into SObjectizer 5.5. Second part. States

States are usually represented as a constant members of agent’s class. Those members are usually initialized by so_make_state() method:

class connection_t : public so_5::rt::agent_t{ const so_5::rt::state_t st_not_connected = so_make_state(); const so_5::rt::state_t st_connecting = so_make_state(); const so_5::rt::state_t st_connected = so_make_state(); ...};

SObjectizer Team, May 2015

Page 11: Dive into SObjectizer 5.5. Second part. States

A state can have a textual name:

class connection_t : public so_5::rt::agent_t{ const so_5::rt::state_t st_not_connected = so_make_state("not_connected"); const so_5::rt::state_t st_connecting = so_make_state("connecting"); const so_5::rt::state_t st_connected = so_make_state("connected"); ...};

It could be useful for debugging and logging.

SObjectizer Team, May 2015

Page 12: Dive into SObjectizer 5.5. Second part. States

There are several ways of changing agent’s state:// Very old and basic way.so_change_state( st_connecting );

// More modern and short way.st_connecting.activate();

// Yet more modern way.this >>= st_connecting;

The current state can be obtained via so_current_state() method:

if( st_connected == so_current_state() ) …

SObjectizer Team, May 2015

Page 13: Dive into SObjectizer 5.5. Second part. States

Every agent already has one state: the default one.

The default state can be accessed via so_default_state() method:

// Returning agent to the default state.this >>= so_default_state();

SObjectizer Team, May 2015

Page 14: Dive into SObjectizer 5.5. Second part. States

Even ad-hoc agents have the default state.

But it is the only one state they have.

Because there is no user-defined class for an ad-hoc agent then there is no possibility to define new states for ad-hoc agent. Thus there is no possibility to change state of ad-hoc agent.

SObjectizer Team, May 2015

Page 15: Dive into SObjectizer 5.5. Second part. States

The most important part of usage of agent’s states is subscription to a message with respect to a specific state...

SObjectizer Team, May 2015

Page 16: Dive into SObjectizer 5.5. Second part. States

The simple usage of so_subscribe() and so_subscribe_self() methods leads to subscription only for the default agent’s state.

It means that:

so_subscribe_self().event(…);

is the equivalent of:

so_subscribe_self().in( so_default_state() ).event(…);

SObjectizer Team, May 2015

Page 17: Dive into SObjectizer 5.5. Second part. States

To make subscription to a message for a specific state it is necessary to use in() method in a subscription chain:

so_subscribe_self().in( st_not_connected ).event(…);so_subscribe_self().in( st_connecting ).event(…);so_subscribe_self().in( st_connected ).event(…);

SObjectizer Team, May 2015

Page 18: Dive into SObjectizer 5.5. Second part. States

The in() methods can be chained if the same event handler is used in several states:

so_subscribe_self() .in( st_not_connected ) .in( st_connecting ) .event< get_status >( [] -> std::string { return "not connected"; } );

so_subscribe_self() .in( st_connected ) .event< get_status >( [] -> std::string { return "connected"; } );

SObjectizer Team, May 2015

Page 19: Dive into SObjectizer 5.5. Second part. States

There is another way to make subscription for a specific state:

st_not_connected.event(…);

st_connecting.event(…).event(…);

st_connected.event(…).event(…).event(…)….event(…);

SObjectizer Team, May 2015

Page 20: Dive into SObjectizer 5.5. Second part. States

Let’s see an example of usage of agent’s states.

The very simple implementation of well known task of Dining Philosophers will be used as demo.

SObjectizer Team, May 2015

Page 21: Dive into SObjectizer 5.5. Second part. States

The philosophers and forks will be represented by agents which interacts with each other by messages and signals.

We need the following messages/signals for interaction:

SObjectizer Team, May 2015

Page 22: Dive into SObjectizer 5.5. Second part. States

A philosopher will send msg_take message to a fork to get it. The message contains the philosopher’s mbox on which a reply will be sent:

struct msg_take : public so_5::rt::message_t{ const so_5::rt::mbox_t m_who;

msg_take( so_5::rt::mbox_t who ) : m_who( std::move(who) ) {}};

SObjectizer Team, May 2015

Page 23: Dive into SObjectizer 5.5. Second part. States

Signals msg_taken and msg_busy will be sent by a fork agent back to a philosopher agent:

struct msg_taken : public so_5::rt::signal_t {};struct msg_busy : public so_5::rt::signal_t {};

Signal msg_put will be sent by a philosopher agent when it doesn’t need the fork anymore:

struct msg_put : public so_5::rt::signal_t {};

SObjectizer Team, May 2015

Page 24: Dive into SObjectizer 5.5. Second part. States

A fork agent is the simplest one.

So, let’s start from definition of that agent…

SObjectizer Team, May 2015

Page 25: Dive into SObjectizer 5.5. Second part. States

A fork agent needs two states: free and taken by some philosopher:

class a_fork_t : public so_5::rt::agent_t{… private : const so_5::rt::state_t st_free = so_make_state( "free" ); const so_5::rt::state_t st_taken = so_make_state( "taken" );};

SObjectizer Team, May 2015

Page 26: Dive into SObjectizer 5.5. Second part. States

A fork agent must start its work in st_free state.

But its initial state is the default state.

So it is necessary to switch agent to st_free state in the beginning.

Usually it is done in so_define_agent() method.

SObjectizer Team, May 2015

Page 27: Dive into SObjectizer 5.5. Second part. States

Switching a fork agent to st_free state at the beginning of the agent definition:

class a_fork_t : public so_5::rt::agent_t{public :… virtual void so_define_agent() override { this >>= st_free;…

SObjectizer Team, May 2015

Page 28: Dive into SObjectizer 5.5. Second part. States

A fork agent handles two messages: msg_take and msg_put. But an agent does it differently in each state.

On msg_take in st_free the agent must be switched to st_taken and msg_taken must be sent back.

Msg_put should not be received in st_free so there will not be a subscription to msg_put in st_free.

SObjectizer Team, May 2015

Page 29: Dive into SObjectizer 5.5. Second part. States

Subscription for st_free state:

class a_fork_t : public so_5::rt::agent_t{… virtual void so_define_agent() override { … st_free.event( [=]( const msg_take & evt ) { this >>= st_taken; so_5::send< msg_taken >( evt.m_who ); } );…

SObjectizer Team, May 2015

Page 30: Dive into SObjectizer 5.5. Second part. States

Two messages must be processed in st_taken state: msg_take and msg_put.

On msg_take the agent must reply with msg_busy.

On msg_put the agent must switch its state to st_free.

SObjectizer Team, May 2015

Page 31: Dive into SObjectizer 5.5. Second part. States

Subscription for st_taken state:

class a_fork_t : public so_5::rt::agent_t{… virtual void so_define_agent() override { … st_taken.event( []( const msg_take & evt ) { so_5::send< msg_busy >( evt.m_who ); } ) .event< msg_put >( [=] { this >>= st_free; } );…

SObjectizer Team, May 2015

Page 32: Dive into SObjectizer 5.5. Second part. States

The full a_fork_t code (1/2):class a_fork_t : public so_5::rt::agent_t{public : a_fork_t( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) {}

virtual void so_define_agent() override { this >>= st_free;

st_free.event( [=]( const msg_take & evt ) { this >>= st_taken; so_5::send< msg_taken >( evt.m_who ); } );

SObjectizer Team, May 2015

Page 33: Dive into SObjectizer 5.5. Second part. States

The full a_fork_t code (2/2): st_taken.event( []( const msg_take & evt ) { so_5::send< msg_busy >( evt.m_who ); } ) .event< msg_put >( [=] { this >>= st_free; } ); }

private : const so_5::rt::state_t st_free = so_make_state( "free" ); const so_5::rt::state_t st_taken = so_make_state( "taken" );};

SObjectizer Team, May 2015

Page 34: Dive into SObjectizer 5.5. Second part. States

A philosopher agent is more complex and requires more states:● initial state st_thinking from which agent switches to

st_wait_left;

● st_wait_left for waiting answer from the left fork. Then agent switches to st_wait_right or st_thinking;

● st_wait_right for waiting answer from the right fork. Then agent switches to st_eating or st_thinking.

● st_eating from which agent switches to st_thinking.

SObjectizer Team, May 2015

Page 35: Dive into SObjectizer 5.5. Second part. States

The graphical representation:

SObjectizer Team, May 2015

st_thinkingenter: send delayed msg_stop_thinking

st_wait_leftenter: send msg_take to

the left fork

st_wait_rightenter: send msg_take to

the right fork

st_eatingenter: send delayed

msg_stop_eating

msg_stop_thinking

msg_busy

msg_takenmsg_busy

msg_taken

msg_stop_eating

Page 36: Dive into SObjectizer 5.5. Second part. States

Definition of a philosopher agent states:class a_philosopher_t : public so_5::rt::agent_t{…private : const so_5::rt::state_t st_thinking = so_make_state( "thinking" ); const so_5::rt::state_t st_wait_left = so_make_state( "wait_left" ); const so_5::rt::state_t st_wait_right = so_make_state( "wait_right" ); const so_5::rt::state_t st_eating = so_make_state( "eating" );…};

SObjectizer Team, May 2015

Page 37: Dive into SObjectizer 5.5. Second part. States

Two additional signals are needed for a philosopher agent. They are defined as private types because no one except philosopher can use them:class a_philosopher_t : public so_5::rt::agent_t{ struct msg_stop_thinking : public so_5::rt::signal_t {}; struct msg_stop_eating : public so_5::rt::signal_t {};…

SObjectizer Team, May 2015

Page 38: Dive into SObjectizer 5.5. Second part. States

Now the subscriptions of a philosopher agent can be defined...class a_philosopher_t : public so_5::rt::agent_t{… virtual void so_define_agent() override {

SObjectizer Team, May 2015

Page 39: Dive into SObjectizer 5.5. Second part. States

The behaviour in st_thinking state is very simple.

Once msg_stop_thinking is received the agent must switch to st_wait_left and send msg_take to the left fork:st_thinking.event< msg_stop_thinking >( [=] { this >>= st_wait_left; so_5::send< msg_take >( m_left_fork, so_direct_mbox() ); } );

SObjectizer Team, May 2015

Page 40: Dive into SObjectizer 5.5. Second part. States

A philosopher must react on two messages in st_wait_left state:

● on msg_taken it must switch to st_wait_right and ask for the right fork;

● on msg_busy it must return to st_thinking.

st_wait_left.event< msg_taken >( [=] { this >>= st_wait_right; so_5::send< msg_take >( m_right_fork, so_direct_mbox() ); } ) .event< msg_busy >( [=] { think(); } );

SObjectizer Team, May 2015

Page 41: Dive into SObjectizer 5.5. Second part. States

A very similar logic is for st_wait_right state. But on msg_busy the left fork must be freed. And msg_taken means switching to st_eating.

st_wait_right.event< msg_taken >( [=] { this >>= st_eating; so_5::send_delayed_to_agent< msg_stop_eating >( *this, pause() ); } ) .event< msg_busy >( [=] { so_5::send< msg_put >( m_left_fork ); think(); } );

SObjectizer Team, May 2015

Page 42: Dive into SObjectizer 5.5. Second part. States

Only one signal must be handled in st_eating state: msg_stop_eating. Both forks must be freed and agent must be switched to st_thinking.

st_eating.event< msg_stop_eating >( [=] { so_5::send< msg_put >( m_right_fork ); so_5::send< msg_put >( m_left_fork ); think(); } );

SObjectizer Team, May 2015

Page 43: Dive into SObjectizer 5.5. Second part. States

In contradiction to a_fork_t there is no switching from the default state in the philosopher’s so_define_agent().

But how a philosopher starts its work in st_thinking?

SObjectizer Team, May 2015

Page 44: Dive into SObjectizer 5.5. Second part. States

It is done in so_evt_start() by calling think() helper method:class a_philosopher_t : public so_5::rt::agent_t{… virtual void so_evt_start() override { think(); }

private : void think() { this >>= st_thinking; so_5::send_delayed_to_agent< msg_stop_thinking >( *this, pause() ); }…

SObjectizer Team, May 2015

Page 45: Dive into SObjectizer 5.5. Second part. States

The full a_philosopher_t code (1/5):class a_philosopher_t : public so_5::rt::agent_t{ struct msg_stop_thinking : public so_5::rt::signal_t {}; struct msg_stop_eating : public so_5::rt::signal_t {};

public : a_philosopher_t( so_5::rt::environment_t & env, so_5::rt::mbox_t left_fork, so_5::rt::mbox_t right_fork ) : so_5::rt::agent_t( env ) , m_left_fork( std::move( left_fork ) ) , m_right_fork( std::move( right_fork ) ) {}

SObjectizer Team, May 2015

Page 46: Dive into SObjectizer 5.5. Second part. States

The full a_philosopher_t code (2/5): virtual void so_define_agent() override { st_thinking.event< msg_stop_thinking >( [=] { this >>= st_wait_left; so_5::send< msg_take >( m_left_fork, so_direct_mbox() ); } );

st_wait_left.event< msg_taken >( [=] { this >>= st_wait_right; so_5::send< msg_take >( m_right_fork, so_direct_mbox() ); } ) .event< msg_busy >( [=] { think(); } );

SObjectizer Team, May 2015

Page 47: Dive into SObjectizer 5.5. Second part. States

The full a_philosopher_t code (3/5): st_wait_right.event< msg_taken >( [=] { this >>= st_eating; so_5::send_delayed_to_agent< msg_stop_eating >( *this, pause() ); } ) .event< msg_busy >( [=] { so_5::send< msg_put >( m_left_fork ); think(); } );

st_eating.event< msg_stop_eating >( [=] { so_5::send< msg_put >( m_right_fork ); so_5::send< msg_put >( m_left_fork ); think(); } ); }

SObjectizer Team, May 2015

Page 48: Dive into SObjectizer 5.5. Second part. States

The full a_philosopher_t code (4/5): virtual void so_evt_start() override { think(); }

private : const so_5::rt::state_t st_thinking = so_make_state( "thinking" ); const so_5::rt::state_t st_wait_left = so_make_state( "wait_left" ); const so_5::rt::state_t st_wait_right = so_make_state( "wait_right" ); const so_5::rt::state_t st_eating = so_make_state( "eating" );

const so_5::rt::mbox_t m_left_fork; const so_5::rt::mbox_t m_right_fork;

SObjectizer Team, May 2015

Page 49: Dive into SObjectizer 5.5. Second part. States

The full a_philosopher_t code (5/5): void think() { this >>= st_thinking; so_5::send_delayed_to_agent< msg_stop_thinking >( *this, pause() ); }

static std::chrono::milliseconds pause() { return std::chrono::milliseconds( 250 + (std::rand() % 250) ); }};

SObjectizer Team, May 2015

Page 50: Dive into SObjectizer 5.5. Second part. States

It was a short description of such important part of SObjectizer-5.5 as agent’s states.

But there are two important questions:

1. Are agent’s states really useful?2. And how often they are used in solving real-world

problems?

SObjectizer Team, May 2015

Page 51: Dive into SObjectizer 5.5. Second part. States

There is one answer for both questions:

Simple agents almost always use the only default state. But the complex ones use agent’s states heavily. And for big agents with sophisticated behaviour states are very useful.

There are no other features which could replace agent’s states in such cases.

SObjectizer Team, May 2015

Page 52: Dive into SObjectizer 5.5. Second part. States

Additional Information:

Project’s home: http://sourceforge.net/projects/sobjectizer

Documentation: http://sourceforge.net/p/sobjectizer/wiki/

Forum: http://sourceforge.net/p/sobjectizer/discussion/

Google-group: https://groups.google.com/forum/#!forum/sobjectizer

GitHub mirror: https://github.com/masterspline/SObjectizer