/**
@page GRAS_tut_tour_explicitwait Lesson 11: Explicitely waiting for messages
\section GRAS_tut_tour_explicitwait_toc Table of Contents
- \ref GRAS_tut_tour_explicitwait_intro
- \ref GRAS_tut_tour_explicitwait_use
- \ref GRAS_tut_tour_explicitwait_algo
- \ref GRAS_tut_tour_explicitwait_code
- \ref GRAS_tut_tour_explicitwait_code_msg
- \ref GRAS_tut_tour_explicitwait_code_cb
- \ref GRAS_tut_tour_explicitwait_code_api
- \ref GRAS_tut_tour_explicitwait_code_smain
- \ref GRAS_tut_tour_explicitwait_code_cmain
- \ref GRAS_tut_tour_explicitwait_recap
\section GRAS_tut_tour_explicitwait_intro Introduction
The messaging primitive we have seen so far are somehow limited on the
receiver side. You have to attach a callback to a message type, and then go
into an infinite loop. Sometimes, you want to block your execution until a
message of a given type arrives. This often occures when you want to deal
with synchronization problems.
As an example, we will study a simple centralized algorithm for mutual
exclusion. In short, when the client wants to enter the critical section
(CS), it sends a specific message to the server, and waits for the server to
answer back with another message. While the client is "locked" waiting for
the message, nothing else should occure for it (no callback or timer should
be served).
The simplest interface to explicit message waiting allows you to specify the
message type you are willing to accept (using gras_msg_wait()). But you can
also specify a list of accepted message types (using gras_msg_wait_or()), or
even provide your own message filters to decide precisly the kind of message
you are waiting for (for example depending on message content -- this is
done using gras_msg_wait_ext()).
Any message received not matching your expectation will be queued for
further use. Ie, they will be stored in memory and are candidates for the
next gras_msg_handle() or gras_msg_wait().
\section GRAS_tut_tour_explicitwait_use Example: mutual exclusion with centralized coordinator
\subsection GRAS_tut_tour_explicitwait_algo GRAS modelization of the algorithm
This section naturally provides an example of how to use gras_msg_wait(),
but it can also be seen as an example of the guidelines provided in
\ref GRAS_howto_design.
So, here are the caracteristics of our example:
There is two types of processes:
- several clients which want to enter a critical section
- a server, which grants clients to enter the CS when no other
process is already in it.
There is three kind of messages in the system:
- request (from clients to server) to ask for the permission to enter the CS
- release (from clients to server) to express that they exited the CS
- grant (from server to clients) to allow the given process to enter the CS
The server has 2 callbacks attached:
- When it gets a request, it checks whether their is already a process in
the CS. If yes, it adds the requester into a FIFO list. If not, it sends
a grant message to the asking client.
- When it gets a release, it checks whether there is some waiting
processes in the waiting queue. If yes, it dequeues the first one and
send a grant message to it. If no, it notes that the CS is free.
The server has two private data (for the callbacks to work):
- a boolean indicating whether there is a process in the CS already
- a waiting queue (#xbt_dynar_t is quite natural to code this).
The client interface is composed of two functions:
- lock(), which tries to get the grant from the server to enter the CS.
This is where explicit waiting comes into the game. This function sends a
request to the server, and blocks until the server issues a
grant back.
- unlock(), which informs the server that we exited the CS (by sending a
release message)
\subsection GRAS_tut_tour_explicitwait_code The code step-by-step
\subsubsection GRAS_tut_tour_explicitwait_code_msg a) Messages declaration
First of all, we should have a function declaring all used messages. As said
before, this should be in a separate function so that it can be shared
between all process kinds and avoid code dupplication which may result in
definition discrepency.
Here, there is no payload attached to the messages.
\dontinclude 11-explicitwait.c
\skip message_declaration
\until }
\subsubsection GRAS_tut_tour_explicitwait_code_cb b) Defining private data and callbacks of the server
Then, we define the callbacks that should be invoqued on the server side
when some messages are received, as previously. For this, we also have to
declare a structure for the private data of the server.
\skip typedef
\until end_of_release_callback
\subsubsection GRAS_tut_tour_explicitwait_code_api c) Client-side API
Now, we define the functions that the client must call to use the service.
Note that this is where the explicit wait feature is used.
\skip lock
\until end_of_unlock
\subsubsection GRAS_tut_tour_explicitwait_code_smain d) Server-side initialization
The core of our distributed service is implemented (protocol, actions on
server side, and accessing function on client side). We should now
initialize the server and let it wait for incomming messages.
Defining when to stop the server can become tricky. The simplest solution is
to never let the server stop. It simply runs forever. But the simulator will
raise an error at the end, so I won't do so here to keep the output clean.
Another solution would be to deal with client membership properly: clients
registers, use the service and quit afterward. When no client use the
service, the server stops. This would be a bit difficult to implement
(actually, there is an AMOK module to do so simply: \ref AMOK_pm).
Here, we will just hardcode that the clients ask 5 times for the token, and
that there is two clients. This clearly simplify the problem.
\dontinclude 11-explicitwait.c
\skip gras_userdata_new
\until gras_msg_handle
\subsubsection GRAS_tut_tour_explicitwait_code_cmain e) Client-side use
And now, the client is really simple to write:
\skip message_declaration
\until }
\section GRAS_tut_tour_explicitwait_recap Recapping everything together
The program now reads:
\include 11-explicitwait.c
Which produces the expected output:
\include 11-explicitwait.output
*/