/**
@page GRAS_tut_tour_globals Lesson 5: Using globals in processes
\section GRAS_tut_tour_globals_toc Table of Contents
- \ref GRAS_tut_tour_globals_intro
- \ref GRAS_tut_tour_globals_use
- \ref GRAS_tut_tour_callback_pitfall
- \ref GRAS_tut_tour_callback_recap
\section GRAS_tut_tour_globals_intro Introduction
Callbacks are great to express your processes as state machines, but they
pose another problem: callbacks don't have acces to the variable declared
within the scope of the process' main function (of course). You should
however resist to the temptation to declare globals outside of the scope of
the functions, or you won't be able to use more than one process of each
type in the simulation. Remember, all gras processes run as thread
within the same naming space in SG so your globals will be shared between
the several instances of your process, leading to bad problems.
Instead, you you have to put all globals in a structure, and let GRAS handle
it with the gras_userdata_* functions (there is only 3 of them ;).
\section GRAS_tut_tour_globals_use Putting globals into action
We will now modify the example to add a "kill" message, and let the server
loop on incoming messages until it gets such a message. We only need a
boolean, so the structure is quite simple:
\dontinclude 05-globals.c
\skip struct
\until server_data
Then, we need to create this structure in the process main function. We
could use either gras_userdata_new() or gras_userdata_set(). The former is an
helper macro mallocing the space needed by the structure and passing it to
gras using the latter function. If you go for gras_userdata_set(), you
should pass it a pointer to your data you want to retrieve afterward.
\dontinclude 05-globals.c
\skip userdata_new
\until userdata_new
BEWARE, the gras_userdata_new expects the pointed type, not the
pointer type. As you can see, in our example, you should pass
server_data_t to the macro, even if the global variable is later
defined as being of type server_data_t*.
Once you declared a global that way, retriving this (for example in a
callback) is really easy:
\dontinclude 05-globals.c
\skip userdata_get
\until userdata_get
We can now write the callback, which simply retrive the globals and change
the value of the kileld field.
\dontinclude 05-globals.c
\skip kill_cb
\until end_of_kill_callback
And we replace the single gras_msg_handle() of the server main function by a
loop:
\skip while
\until }
Please note that in our example, only one process creates a global
structure. But this is naturally completely ok to have several
processes creating their globals this way. Each of these globals will
be separated, so process A cannot access globals defined by process B.
Maybe this implies that the name "globals" is a bit misleading. It
should be "process state" or something similar.
\section GRAS_tut_tour_callback_pitfall Common pitfall of globals
There is an error that I do myself every other day using globals in GRAS.
This is to write something like:
\verbatim int server(int argc, char *argv[]) {
server_data_t globals=gras_user_new(server_data_t);
/* other variable definition */
gras_init(&argc, argv);
/* rest of the code */
}\endverbatim
The problem is that you call gras_userdata_new() before gras_init(). Doing so,
embarass GRAS since it does not have its internal buffer initialized yet,
and cannot store your data anywhere. That is why doing so triggers an error
at run time.
Also, as noted above, the gras_userdata_new expects the pointed type,
not the pointer type. As you can see, in our example, you should pass
server_data_t to the macro, even if the global variable is later
defined as being of type server_data_t*.
\section GRAS_tut_tour_callback_recap Recaping everything together
The whole program now reads:
\include 05-globals.c
And here is the output (unchanged wrt previous version):
\include 05-globals.output
That's it, we're done. We have a server able to handle any number of
messages, which the client can stop remotely properly. That's already
something, hu?
Go to \ref GRAS_tut_tour_logs
*/