SimGrid  3.8.1
Scalable Simulation of Distributed Systems - User Guide
Using SimGrid

SimGrid comes with many examples provided in the examples/ directory. Those examples are described in section MSG examples . Those examples are commented and should be easy to understand. for a first step into SimGrid we also provide some more detailed examples in the sections below.

You should also check our online tutorial section that contains a generic tutorial about using SimGrid.

Using MSG

You should also check our online tutorial section that contains a dedicated tutorial.

Here are some examples on how to use MSG, the most used API.

tr MSG comes with an extensive set of examples. It is sometimes difficult to find the one you need. This list aims at helping you finding the example from which you can learn what you want to.

Basic examples and features

Asynchronous communications

Simulation of asynchronous communications between a sender and a receiver using a realistic platform and an external description of the deployment.


Code of the application

Preliminary declarations

#include <stdio.h>
#include "msg/msg.h" /* Yeah! If you want to use msg, you need to include msg/msg.h */
#include "xbt/sysdep.h" /* calloc, printf */
/* Create a log channel to have nice outputs. */
#include "xbt/log.h"
#include "xbt/asserts.h"
"Messages specific for this msg example");
int sender(int argc, char *argv[]);
int receiver(int argc, char *argv[]);
msg_error_t test_all(const char *platform_file,
const char *application_file);

Sender function

The sender send to a receiver an asynchronous message with the function "MSG_task_isend()". Cause this function is non-blocking we have to make "MSG_comm_test()" to know if the communication is finished for finally destroy it with function "MSG_comm_destroy()". It also available to "make MSG_comm_wait()" which make both of them.

C style arguments (argc/argv) are interpreted as:

  • the number of tasks to distribute
  • the computation size of each task
  • the size of the files associated to each task
  • a list of host that will accept those tasks.
  • the time to sleep at the beginning of the function
  • This time defined the process sleep time if time = 0 use of MSG_comm_wait() if time > 0 use of MSG_comm_test()

int sender(int argc, char *argv[])
{
long number_of_tasks = atol(argv[1]);
double task_comp_size = atof(argv[2]);
double task_comm_size = atof(argv[3]);
long receivers_count = atol(argv[4]);
double sleep_start_time = atof(argv[5]);
double sleep_test_time = atof(argv[6]);
XBT_INFO("sleep_start_time : %f , sleep_test_time : %f", sleep_start_time,
sleep_test_time);
msg_comm_t comm = NULL;
int i;
msg_task_t task = NULL;
MSG_process_sleep(sleep_start_time);
for (i = 0; i < number_of_tasks; i++) {
char mailbox[256];
char sprintf_buffer[256];
sprintf(mailbox, "receiver-%ld", i % receivers_count);
sprintf(sprintf_buffer, "Task_%d", i);
task =
MSG_task_create(sprintf_buffer, task_comp_size, task_comm_size,
NULL);
comm = MSG_task_isend(task, mailbox);
XBT_INFO("Send to receiver-%ld Task_%d", i % receivers_count, i);
if (sleep_test_time == 0) {
MSG_comm_wait(comm, -1);
} else {
while (MSG_comm_test(comm) == 0) {
MSG_process_sleep(sleep_test_time);
};
}
}
for (i = 0; i < receivers_count; i++) {
char mailbox[80];
sprintf(mailbox, "receiver-%ld", i % receivers_count);
task = MSG_task_create("finalize", 0, 0, 0);
comm = MSG_task_isend(task, mailbox);
XBT_INFO("Send to receiver-%ld finalize", i % receivers_count);
if (sleep_test_time == 0) {
MSG_comm_wait(comm, -1);
} else {
while (MSG_comm_test(comm) == 0) {
MSG_process_sleep(sleep_test_time);
};
}
}
XBT_INFO("Goodbye now!");
return 0;
} /* end_of_sender */

Receiver function

This function executes tasks when it receives them. As the receiving is asynchronous we have to test the communication to know if it is completed or not with "MSG_comm_test()" or wait for the completion "MSG_comm_wait()".

C style arguments (argc/argv) are interpreted as:

  • the id to use for received the communication.
  • the time to sleep at the beginning of the function
  • This time defined the process sleep time if time = 0 use of MSG_comm_wait() if time > 0 use of MSG_comm_test()

int receiver(int argc, char *argv[])
{
msg_task_t task = NULL;
_XBT_GNUC_UNUSED msg_error_t res;
int id = -1;
char mailbox[80];
msg_comm_t res_irecv;
double sleep_start_time = atof(argv[2]);
double sleep_test_time = atof(argv[3]);
XBT_INFO("sleep_start_time : %f , sleep_test_time : %f", sleep_start_time,
sleep_test_time);
_XBT_GNUC_UNUSED int read;
read = sscanf(argv[1], "%d", &id);
xbt_assert(read,
"Invalid argument %s\n", argv[1]);
MSG_process_sleep(sleep_start_time);
sprintf(mailbox, "receiver-%d", id);
while (1) {
res_irecv = MSG_task_irecv(&(task), mailbox);
XBT_INFO("Wait to receive a task");
if (sleep_test_time == 0) {
res = MSG_comm_wait(res_irecv, -1);
xbt_assert(res == MSG_OK, "MSG_task_get failed");
} else {
while (MSG_comm_test(res_irecv) == 0) {
MSG_process_sleep(sleep_test_time);
};
MSG_comm_destroy(res_irecv);
}
XBT_INFO("Received \"%s\"", MSG_task_get_name(task));
if (!strcmp(MSG_task_get_name(task), "finalize")) {
break;
}
XBT_INFO("Processing \"%s\"", MSG_task_get_name(task));
XBT_INFO("\"%s\" done", MSG_task_get_name(task));
task = NULL;
}
XBT_INFO("I'm done. See you!");
return 0;
} /* end_of_receiver */

Simulation core

This function is the core of the simulation and is divided only into 3 parts thanks to MSG_create_environment() and MSG_launch_application().

  1. Simulation settings : MSG_create_environment() creates a realistic environment
  2. Application deployment : create the processes on the right locations with MSG_launch_application()
  3. The simulation is run with MSG_main()

Its arguments are:

  • platform_file: the name of a file containing an valid surfxml platform description.
  • application_file: the name of a file containing a valid surfxml application description

msg_error_t test_all(const char *platform_file,
const char *application_file)
{
/* MSG_config("workstation/model","KCCFLN05"); */
{ /* Simulation setting */
MSG_create_environment(platform_file);
}
{ /* Application deployment */
MSG_function_register("sender", sender);
MSG_function_register("receiver", receiver);
MSG_launch_application(application_file);
}
res = MSG_main();
XBT_INFO("Simulation time %g", MSG_get_clock());
return res;
} /* end_of_test_all */

Main function

This initializes MSG, runs a simulation, and free all data-structures created by MSG.

int main(int argc, char *argv[])
{
MSG_init(&argc, argv);
if (argc < 3) {
printf("Usage: %s platform_file deployment_file\n", argv[0]);
printf("example: %s msg_platform.xml msg_deployment.xml\n", argv[0]);
exit(1);
}
res = test_all(argv[1], argv[2]);
if (res == MSG_OK)
return 0;
else
return 1;
} /* end_of_main */

Waitall function for sender

The use of this function permit to send all messages and wait for the completion of all in one time.

int sender(int argc, char *argv[])
{
long number_of_tasks = atol(argv[1]);
double task_comp_size = atof(argv[2]);
double task_comm_size = atof(argv[3]);
long receivers_count = atol(argv[4]);
msg_comm_t *comm = xbt_new(msg_comm_t, number_of_tasks + receivers_count);
int i;
msg_task_t task = NULL;
for (i = 0; i < number_of_tasks; i++) {
char mailbox[256];
char sprintf_buffer[256];
sprintf(mailbox, "receiver-%ld", i % receivers_count);
sprintf(sprintf_buffer, "Task_%d", i);
task =
MSG_task_create(sprintf_buffer, task_comp_size, task_comm_size,
NULL);
comm[i] = MSG_task_isend(task, mailbox);
XBT_INFO("Send to receiver-%ld Task_%d", i % receivers_count, i);
}
for (i = 0; i < receivers_count; i++) {
char mailbox[80];
sprintf(mailbox, "receiver-%ld", i % receivers_count);
task = MSG_task_create("finalize", 0, 0, 0);
comm[i + number_of_tasks] = MSG_task_isend(task, mailbox);
XBT_INFO("Send to receiver-%ld finalize", i % receivers_count);
}
/* Here we are waiting for the completion of all communications */
MSG_comm_waitall(comm, (number_of_tasks + receivers_count), -1);
XBT_INFO("Goodbye now!");
xbt_free(comm);
return 0;
} /* end_of_sender */

Waitany function

The MSG_comm_waitany() function return the place of the first message send or receive from a xbt_dynar_t table.

From a sender

We can use this function to wait all sent messages.

int sender(int argc, char *argv[])
{
long number_of_tasks = atol(argv[1]);
double task_comp_size = atof(argv[2]);
double task_comm_size = atof(argv[3]);
long receivers_count = atol(argv[4]);
int diff_com = atol(argv[5]);
double coef = 0;
xbt_dynar_t d = xbt_dynar_new(sizeof(msg_comm_t), NULL);
int i;
msg_task_t task;
char mailbox[256];
char sprintf_buffer[256];
msg_comm_t comm;
for (i = 0; i < number_of_tasks; i++) {
if (diff_com == 0)
coef = 1;
else
coef = (i + 1);
sprintf(mailbox, "receiver-%ld", (i % receivers_count));
sprintf(sprintf_buffer, "Task_%d", i);
task =
MSG_task_create(sprintf_buffer, task_comp_size,
task_comm_size / coef, NULL);
comm = MSG_task_isend(task, mailbox);
XBT_INFO("Send to receiver-%ld %s comm_size %f", i % receivers_count,
sprintf_buffer, task_comm_size / coef);
}
/* Here we are waiting for the completion of all communications */
while (!xbt_dynar_is_empty(d)) {
}
/* Here we are waiting for the completion of all tasks */
sprintf(mailbox, "finalize");
msg_comm_t res_irecv;
_XBT_GNUC_UNUSED msg_error_t res_wait;
for (i = 0; i < receivers_count; i++) {
task = NULL;
res_irecv = MSG_task_irecv(&(task), mailbox);
res_wait = MSG_comm_wait(res_irecv, -1);
xbt_assert(res_wait == MSG_OK, "MSG_comm_wait failed");
MSG_comm_destroy(res_irecv);
}
XBT_INFO("Goodbye now!");
return 0;
} /* end_of_sender */

From a receiver

We can also wait for the arrival of all messages.

int receiver(int argc, char *argv[])
{
int id = -1;
int i;
char mailbox[80];
xbt_dynar_t comms = xbt_dynar_new(sizeof(msg_comm_t), NULL);
int tasks = atof(argv[2]);
msg_task_t *task = xbt_new(msg_task_t, tasks);
_XBT_GNUC_UNUSED int read;
read = sscanf(argv[1], "%d", &id);
xbt_assert(read, "Invalid argument %s\n", argv[1]);
sprintf(mailbox, "receiver-%d", id);
msg_comm_t res_irecv;
for (i = 0; i < tasks; i++) {
XBT_INFO("Wait to receive task %d", i);
task[i] = NULL;
res_irecv = MSG_task_irecv(&task[i], mailbox);
xbt_dynar_push_as(comms, msg_comm_t, res_irecv);
}
/* Here we are waiting for the receiving of all communications */
msg_task_t task_com;
while (!xbt_dynar_is_empty(comms)) {
_XBT_GNUC_UNUSED msg_error_t err;
xbt_dynar_remove_at(comms, MSG_comm_waitany(comms), &res_irecv);
task_com = MSG_comm_get_task(res_irecv);
MSG_comm_destroy(res_irecv);
XBT_INFO("Processing \"%s\"", MSG_task_get_name(task_com));
MSG_task_execute(task_com);
XBT_INFO("\"%s\" done", MSG_task_get_name(task_com));
err = MSG_task_destroy(task_com);
xbt_assert(err == MSG_OK, "MSG_task_destroy failed");
}
xbt_dynar_free(&comms);
xbt_free(task);
/* Here we tell to sender that all tasks are done */
sprintf(mailbox, "finalize");
res_irecv = MSG_task_isend(MSG_task_create(NULL, 0, 0, NULL), mailbox);
MSG_comm_wait(res_irecv, -1);
MSG_comm_destroy(res_irecv);
XBT_INFO("I'm done. See you!");
return 0;
} /* end_of_receiver */

Basic Master/Slaves

Simulation of a master-slave application using a realistic platform and an external description of the deployment.

Table of contents:


Preliminary declarations

#include <stdio.h>
#include "msg/msg.h" /* Yeah! If you want to use msg, you need to include msg/msg.h */
#include "xbt/sysdep.h" /* calloc, printf */
/* Create a log channel to have nice outputs. */
#include "xbt/log.h"
#include "xbt/asserts.h"
"Messages specific for this msg example");
int master(int argc, char *argv[]);
int slave(int argc, char *argv[]);
int forwarder(int argc, char *argv[]);
msg_error_t test_all(const char *platform_file,
const char *application_file);
#define FINALIZE ((void*)221297) /* a magic number to tell people to stop working */
int master(int argc, char *argv[])
{
int slaves_count = 0;
msg_host_t *slaves = NULL;
msg_task_t *todo = NULL;
int number_of_tasks = 0;
double task_comp_size = 0;
double task_comm_size = 0;
int i;
_XBT_GNUC_UNUSED int res = sscanf(argv[1], "%d", &number_of_tasks);
xbt_assert(res,"Invalid argument %s\n", argv[1]);
res = sscanf(argv[2], "%lg", &task_comp_size);
xbt_assert(res, "Invalid argument %s\n", argv[2]);
res = sscanf(argv[3], "%lg", &task_comm_size);
xbt_assert(res, "Invalid argument %s\n", argv[3]);
{ /* Task creation */
char sprintf_buffer[64];
todo = xbt_new0(msg_task_t, number_of_tasks);
for (i = 0; i < number_of_tasks; i++) {
sprintf(sprintf_buffer, "Task_%d", i);
todo[i] =
MSG_task_create(sprintf_buffer, task_comp_size, task_comm_size,
NULL);
}

Master code

This function has to be assigned to a msg_process_t that will behave as the master. It should not be called directly but either given as a parameter to MSG_process_create() or registered as a public function through MSG_function_register() and then automatically assigned to a process through MSG_launch_application().

C style arguments (argc/argv) are interpreted as:

  • the number of tasks to distribute
  • the computation size of each task
  • the size of the files associated to each task
  • a list of host that will accept those tasks.

Tasks are dumbly sent in a round-robin style.

}
{ /* Process organisation */
slaves_count = argc - 4;
slaves = xbt_new0(msg_host_t, slaves_count);
for (i = 4; i < argc; i++) {
slaves[i - 4] = MSG_get_host_by_name(argv[i]);
xbt_assert(slaves[i - 4] != NULL, "Unknown host %s. Stopping Now! ",
argv[i]);
}
}
XBT_INFO("Got %d slaves and %d tasks to process", slaves_count,
number_of_tasks);
for (i = 0; i < slaves_count; i++)
XBT_DEBUG("%s", MSG_host_get_name(slaves[i]));
for (i = 0; i < number_of_tasks; i++) {
XBT_INFO("Sending \"%s\" to \"%s\"",
todo[i]->name, MSG_host_get_name(slaves[i % slaves_count]));
if (MSG_host_self() == slaves[i % slaves_count]) {
XBT_INFO("Hey ! It's me ! :)");
}
MSG_task_send(todo[i], MSG_host_get_name(slaves[i % slaves_count]));
XBT_INFO("Sent");
}
("All tasks have been dispatched. Let's tell everybody the computation is over.");
for (i = 0; i < slaves_count; i++) {
msg_task_t finalize = MSG_task_create("finalize", 0, 0, FINALIZE);
MSG_task_send(finalize, MSG_host_get_name(slaves[i]));
}
XBT_INFO("Goodbye now!");
free(slaves);
free(todo);
return 0;
} /* end_of_master */

Slave code

This function has to be assigned to a msg_process_t that has to behave as a slave. Just like the master fuction (described in Master code), it should not be called directly.

This function keeps waiting for tasks and executes them as it receives them.

int slave(int argc, char *argv[])
{
msg_task_t task = NULL;
_XBT_GNUC_UNUSED int res;
while (1) {
xbt_assert(res == MSG_OK, "MSG_task_get failed");
XBT_INFO("Received \"%s\"", MSG_task_get_name(task));
if (!strcmp(MSG_task_get_name(task), "finalize")) {
break;
}
XBT_INFO("Processing \"%s\"", MSG_task_get_name(task));
XBT_INFO("\"%s\" done", MSG_task_get_name(task));
task = NULL;
}
XBT_INFO("I'm done. See you!");
return 0;
} /* end_of_slave */

Forwarder code

This function has to be assigned to a msg_process_t that has to behave as a forwarder. Just like the master function (described in Master code), it should not be called directly.

C style arguments (argc/argv) are interpreted as a list of host that will accept those tasks.

This function keeps waiting for tasks and dispathes them to its slaves.

int forwarder(int argc, char *argv[])
{
int i;
int slaves_count;
msg_host_t *slaves;
{ /* Process organisation */
slaves_count = argc - 1;
slaves = xbt_new0(msg_host_t, slaves_count);
for (i = 1; i < argc; i++) {
slaves[i - 1] = MSG_get_host_by_name(argv[i]);
if (slaves[i - 1] == NULL) {
XBT_INFO("Unknown host %s. Stopping Now! ", argv[i]);
abort();
}
}
}
i = 0;
while (1) {
msg_task_t task = NULL;
int a;
if (a == MSG_OK) {
XBT_INFO("Received \"%s\"", MSG_task_get_name(task));
if (MSG_task_get_data(task) == FINALIZE) {
("All tasks have been dispatched. Let's tell everybody the computation is over.");
for (i = 0; i < slaves_count; i++)
MSG_task_send(MSG_task_create("finalize", 0, 0, FINALIZE),
MSG_host_get_name(slaves[i]));
break;
}
XBT_INFO("Sending \"%s\" to \"%s\"",
MSG_task_get_name(task), MSG_host_get_name(slaves[i % slaves_count]));
MSG_task_send(task, MSG_host_get_name(slaves[i % slaves_count]));
i++;
} else {
XBT_INFO("Hey ?! What's up ? ");
xbt_die("Unexpected behavior");
}
}
xbt_free(slaves);
XBT_INFO("I'm done. See you!");
return 0;
} /* end_of_forwarder */

Simulation core

This function is the core of the simulation and is divided only into 3 parts thanks to MSG_create_environment() and MSG_launch_application().

  1. Simulation settings : MSG_create_environment() creates a realistic environment
  2. Application deployment : create the processes on the right locations with MSG_launch_application()
  3. The simulation is run with MSG_main()

Its arguments are:

  • platform_file: the name of a file containing an valid surfxml platform description.
  • application_file: the name of a file containing a valid surfxml application description

msg_error_t test_all(const char *platform_file,
const char *application_file)
{
/* MSG_config("workstation/model","KCCFLN05"); */
{ /* Simulation setting */
MSG_create_environment(platform_file);
}
{ /* Application deployment */
MSG_function_register("master", master);
MSG_function_register("slave", slave);
MSG_function_register("forwarder", forwarder);
MSG_launch_application(application_file);
}
res = MSG_main();
XBT_INFO("Simulation time %g", MSG_get_clock());
return res;
} /* end_of_test_all */

Main() function

This initializes MSG, runs a simulation, and free all data-structures created by MSG.

int main(int argc, char *argv[])
{
MSG_init(&argc, argv);
if (argc < 3) {
printf("Usage: %s platform_file deployment_file\n", argv[0]);
printf("example: %s msg_platform.xml msg_deployment.xml\n", argv[0]);
exit(1);
}
res = test_all(argv[1], argv[2]);
if (res == MSG_OK)
return 0;
else
return 1;
} /* end_of_main */

Helping files

Example of application file

<?xml version='1.0'?>
<!DOCTYPE platform SYSTEM "http://simgrid.gforge.inria.fr/simgrid.dtd">
<platform version="3">
<!-- The master process (with some arguments) -->
<process host="Tremblay" function="master">
<argument value="20"/> <!-- Number of tasks -->
<argument value="50000000"/> <!-- Computation size of tasks -->
<argument value="1000000"/> <!-- Communication size of tasks -->
<argument value="Jupiter"/> <!-- First slave -->
<argument value="Fafard"/> <!-- Second slave -->
<argument value="Ginette"/> <!-- Third slave -->
<argument value="Bourassa"/> <!-- Last slave -->
<argument value="Tremblay"/> <!-- Me! I can work too! -->
</process>
<!-- The slave process (with no argument) -->
<process host="Tremblay" function="slave" on_failure="RESTART"/>
<process host="Jupiter" function="slave" on_failure="RESTART"/>
<process host="Fafard" function="slave" on_failure="RESTART"/>
<process host="Ginette" function="slave" on_failure="RESTART"/>
<process host="Bourassa" function="slave" on_failure="RESTART"/>
</platform>

Example of platform file

<?xml version='1.0'?>
<!DOCTYPE platform SYSTEM "http://simgrid.gforge.inria.fr/simgrid.dtd">
<platform version="3">
<AS id="AS0" routing="Full">
<!-- ljlkj -->
<host id="Tremblay" power="98095000"/>
<host id="Jupiter" power="76296000"/>
<host id="Fafard" power="76296000"/>
<host id="Ginette" power="48492000"/>
<host id="Bourassa" power="48492000"/>
<link id="6" bandwidth="41279125" latency="5.9904e-05"/>
<link id="11" bandwidth="252750" latency="0.00570455"/>
<link id="3" bandwidth="34285625" latency="0.000514433"/>
<link id="7" bandwidth="11618875" latency="0.00018998"/>
<link id="9" bandwidth="7209750" latency="0.001461517"/>
<link id="12" bandwidth="1792625" latency="0.007877863"/>
<link id="2" bandwidth="118682500" latency="0.000136931"/>
<link id="8" bandwidth="8158000" latency="0.000270544"/>
<link id="1" bandwidth="34285625" latency="0.000514433"/>
<link id="4" bandwidth="10099625" latency="0.00047978"/>
<link id="0" bandwidth="41279125" latency="5.9904e-05"/>
<link id="10" bandwidth="4679750" latency="0.000848712"/>
<link id="5" bandwidth="27946250" latency="0.000278066"/>
<link id="loopback" bandwidth="498000000" latency="0.000015" sharing_policy="FATPIPE"/>
<route src="Tremblay" dst="Tremblay"><link_ctn id="loopback"/></route>
<route src="Jupiter" dst="Jupiter"><link_ctn id="loopback"/></route>
<route src="Fafard" dst="Fafard"><link_ctn id="loopback"/></route>
<route src="Ginette" dst="Ginette"><link_ctn id="loopback"/></route>
<route src="Bourassa" dst="Bourassa"><link_ctn id="loopback"/></route>
<route src="Tremblay" dst="Jupiter">
<link_ctn id="9"/>
</route>
<route src="Tremblay" dst="Fafard">
<link_ctn id="4"/><link_ctn id="3"/><link_ctn id="2"/><link_ctn id="0"/><link_ctn id="1"/><link_ctn id="8"/>
</route>
<route src="Tremblay" dst="Ginette">
<link_ctn id="4"/><link_ctn id="3"/><link_ctn id="5"/>
</route>
<route src="Tremblay" dst="Bourassa">
<link_ctn id="4"/><link_ctn id="3"/><link_ctn id="2"/><link_ctn id="0"/><link_ctn id="1"/><link_ctn id="6"/><link_ctn id="7"/>
</route>
<route src="Jupiter" dst="Fafard">
<link_ctn id="9"/><link_ctn id="4"/><link_ctn id="3"/><link_ctn id="2"/><link_ctn id="0"/><link_ctn id="1"/><link_ctn id="8"/>
</route>
<route src="Jupiter" dst="Ginette">
<link_ctn id="9"/><link_ctn id="4"/><link_ctn id="3"/><link_ctn id="5"/>
</route>
<route src="Jupiter" dst="Bourassa">
<link_ctn id="9"/><link_ctn id="4"/><link_ctn id="3"/><link_ctn id="2"/><link_ctn id="0"/><link_ctn id="1"/><link_ctn id="6"/><link_ctn id="7"/>
</route>
<route src="Fafard" dst="Ginette">
<link_ctn id="8"/><link_ctn id="1"/><link_ctn id="0"/><link_ctn id="2"/><link_ctn id="5"/>
</route>
<route src="Fafard" dst="Bourassa">
<link_ctn id="8"/><link_ctn id="6"/><link_ctn id="7"/>
</route>
<route src="Ginette" dst="Bourassa">
<link_ctn id="5"/><link_ctn id="2"/><link_ctn id="0"/><link_ctn id="1"/><link_ctn id="6"/><link_ctn id="7"/>
</route>
</AS>
</platform>

Using GRAS

Here are some examples on how to use GRAS. You should also check our online tutorial section that contains a dedicated tutorial.

There is for now rather few examples of GRAS, but it's better than nothing, isn't it?

Ping-Pong

This example implements the very classical ping-pong in GRAS. It involves a client (initiating the ping-pong) and a server (answering to client's requests).

It works the following way:

  • Both the client and the server register all needed messages
  • The server registers a callback to the ping message, which sends pong to the expeditor
  • The client sends the ping message to the server, and waits for the pong message as an answer.

This example resides in the examples/gras/ping/ping.c file. Yes, both the code of the client and of the server is placed in the same file.

Table of contents of the ping example


1) Common code to the client and the server

1.a) Initial settings

Let's first load the module header and declare a logging category (see Logging support for more info on logging).

#include "ping.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(Ping, "Messages specific to this example");

The module header ping.h reads:

#include "gras.h"
/* register messages which may be sent (common to client and server) */
void ping_register_messages(void);
/* Function prototypes */
int server(int argc, char *argv[]);
int client(int argc, char *argv[]);

1.b) Register the messages

This function, called by both the client and the server is in charge of declaring the existing messages to GRAS. Since the payload does not involve any newly created types but only int, this is quite easy. (to exchange more complicated types, see Data description or A simple RPC for matrix multiplication for an example).

void ping_register_messages(void)
{
XBT_INFO("Messages registered");
}

[Back to Table of contents of the ping example]

2) Server's code

2.a) The server's globals

In order to ensure the communication between the "main" and the callback of the server, we need to declare some globals. We have to put them in a struct definition so that they can be handled properly in GRAS.

typedef struct {
int endcondition;
} server_data_t;

2.b) The callback to the ping message

Here is the callback run when the server receives any ping message (this will be registered later by the server).

static int server_cb_ping_handler(gras_msg_cb_ctx_t ctx, void *payload)
{
/* 1. Get the payload into the msg variable, and retrieve my caller */
int msg = *(int *) payload;
/* 2. Retrieve the server's state (globals) */
server_data_t *globals = (server_data_t *) gras_userdata_get();
globals->endcondition = 0;
/* 3. Log which client connected */
XBT_INFO(">>>>>>>> Got message PING(%d) from %s:%d <<<<<<<<",
msg,
xbt_socket_peer_port(expeditor));
/* 4. Change the value of the msg variable */
msg = 4321;
/* 5. Send it back as payload of a pong message to the expeditor */
TRY {
gras_msg_send(expeditor, "pong", &msg);
/* 6. Deal with errors: add some details to the exception */
}
gras_socket_close(globals->sock);
RETHROWF("Unable answer with PONG: %s");
}
XBT_INFO(">>>>>>>> Answered with PONG(4321) <<<<<<<<");
/* 7. Set the endcondition boolean to true (and make sure the server stops after receiving it). */
globals->endcondition = 1;
/* 8. Tell GRAS that we consummed this message */
return 0;
} /* end_of_server_cb_ping_handler */

2.c) The "main" of the server

This is the "main" of the server. You must not write any main() function yourself. Instead, you just have to write a regular function like this one which will act as a main.

int server(int argc, char *argv[])
{
server_data_t *globals;
int port = 4000;
/* 1. Init the GRAS infrastructure and declare my globals */
gras_init(&argc, argv);
globals = gras_userdata_new(server_data_t);
/* 2. Get the port I should listen on from the command line, if specified */
if (argc == 2) {
port = atoi(argv[1]);
}
XBT_INFO("Launch server (port=%d)", port);
/* 3. Create my master socket */
globals->sock = gras_socket_server(port);
/* 4. Register the known messages. This function is called twice here, but it's because
this file also acts as regression test, no need to do so yourself of course. */
ping_register_messages();
ping_register_messages(); /* just to make sure it works ;) */
/* 5. Register my callback */
gras_cb_register("ping", &server_cb_ping_handler);
XBT_INFO(">>>>>>>> Listening on port %d <<<<<<<<",
xbt_socket_my_port(globals->sock));
globals->endcondition = 0;
/* 6. Wait up to 10 minutes for an incoming message to handle */
/* 7. Housekeeping */
if (!globals->endcondition)
("An error occured, the endcondition was not set by the callback");
/* 8. Free the allocated resources, and shut GRAS down */
gras_socket_close(globals->sock);
free(globals);
XBT_INFO("Done.");
return 0;
} /* end_of_server */

[Back to Table of contents of the ping example]

3) Client's code

3.a) Client's "main" function

This function is quite straightforward, and the inlined comments should be enough to understand it.

static xbt_socket_t try_gras_socket_client(const char *host, int port)
{
volatile xbt_socket_t sock = NULL;
TRY {
sock = gras_socket_client(host, port);
}
CATCH(e) {
/* dunno what happened, let the exception go through */
RETHROWF("Unable to connect to the server: %s");
}
return sock;
}
int client(int argc, char *argv[])
{
xbt_socket_t toserver = NULL; /* peer */
int ping, pong;
const char *host = "127.0.0.1";
int port = 4000;
/* 1. Init the GRAS's infrastructure */
gras_init(&argc, argv);
/* 2. Get the server's address. The command line override defaults when specified */
if (argc == 3) {
host = argv[1];
port = atoi(argv[2]);
}
XBT_INFO("Launch client (server on %s:%d)", host, port);
/* 3. Create a socket to speak to the server */
while (!(toserver = try_gras_socket_client(host, port)))
XBT_INFO("Connected to %s:%d.", host, port);
/* 4. Register the messages.
See, it doesn't have to be done completely at the beginning, only before use */
ping_register_messages();
/* 5. Keep the user informed of what's going on */
XBT_INFO(">>>>>>>> Connected to server which is on %s:%d <<<<<<<<",
/* 6. Prepare and send the ping message to the server */
ping = 1234;
TRY {
gras_msg_send(toserver, "ping", &ping);
}
gras_socket_close(toserver);
RETHROWF("Failed to send PING to server: %s");
}
XBT_INFO(">>>>>>>> Message PING(%d) sent to %s:%d <<<<<<<<",
ping, xbt_socket_peer_name(toserver), xbt_socket_peer_port(toserver));
/* 7. Wait for the answer from the server, and deal with issues */
TRY {
gras_msg_wait(6000, "pong", &from, &pong);
}
gras_socket_close(toserver);
RETHROWF("Why can't I get my PONG message like everyone else: %s");
}
/* 8. Keep the user informed of what's going on, again */
XBT_INFO(">>>>>>>> Got PONG(%d) from %s:%d <<<<<<<<",
/* 9. Free the allocated resources, and shut GRAS down */
gras_socket_close(toserver);
XBT_INFO("Done.");
return 0;
} /* end_of_client */

[Back to Table of contents of the ping example]

Token Ring example

This example implements the token ring algorithm. It involves several nodes arranged in a ring (each of them have a left and a right neighbour) and exchanging a "token". This algorithm is one of the solution to ensure the mutual exclusion between distributed processes. There is only one token at any time, so the process in its possession is ensured to be the only one having it. So, if there is an action you want all processes to do alternativly, but you cannot afford to have two processes doing it at the same time, let the process having the token doing it.

Actually, there is a lot of different token ring algorithms in the litterature, so this example implements one of them: the simplest one. The ring is static (no new node can join it, and you'll get trouble if one node dies or leaves), and nothing is done for the case in which the token is lost.

1) Deployment file

Here is the deployment file:

<?xml version='1.0'?>
<!DOCTYPE platform SYSTEM "http://simgrid.gforge.inria.fr/simgrid.dtd">
<platform version="3">
<process host="Tremblay" function="node">
<argument value="4000"/> <!-- port on which I am listening -->
<argument value="Fafard"/><argument value="4000"/> <!-- peer (successor) host id and port-->
<argument value="--create-token"/> <!-- I'm first client, ie I have to create the token -->
</process>
<process host="Fafard" function="node">
<argument value="4000"/> <!-- port on which I am listening -->
<argument value="Jupiter"/><argument value="4000"/><!-- peer (successor) host id and port-->
</process>
<process host="Jupiter" function="node">
<argument value="4000"/> <!-- port on which I am listening -->
<argument value="Ginette"/><argument value="4000"/> <!-- peer (successor) host id and port-->
</process>
<process host="Ginette" function="node">
<argument value="4000"/> <!-- port on which I am listening -->
<argument value="Bourassa"/><argument value="4000"/><!-- peer (successor) host id and port-->
</process>
<process host="Bourassa" function="node">
<argument value="4000"/> <!-- port on which I am listening -->
<argument value="Tremblay"/><argument value="4000"/><!-- peer (successor) host id and port-->
</process>
</platform>

The neighbour of each node is given at startup as command line argument. Moreover, one of the nodes is instructed by a specific argument (the one on Tremblay here) to create the token at the begining of the algorithm.

2) Global definition

The token is incarned by a specific message, which circulates from node to node (the payload is an integer incremented at each hop). So, the most important part of the code is the message callback, which forwards the message to the next node. That is why we have to store all variable in a global, as explained in the Globals section.

typedef struct {
xbt_socket_t sock; /* server socket on which I hear */
int remaining_loop; /* number of loops to do until done */
int create; /* whether I have to create the token */
xbt_socket_t tosuccessor; /* how to connect to the successor on ring */
double start_time; /* to measure the elapsed time. Only used by the
node that creates the token */
} node_data_t;

3) The callback

Even if this is the core of this algorithm, this function is quite straightforward.

static int node_cb_stoken_handler(gras_msg_cb_ctx_t ctx, void *payload)
{
/* 1. Get the payload into the msg variable, and retrieve my caller */
int msg = *(int *) payload;
/* 2. Retrieve the node's state (globals) */
node_data_t *globals = (node_data_t *) gras_userdata_get();
/* 3. Log which predecessor connected */
int supersteps = 1; /* only log every superstep loop */
if (NBLOOPS >= 1000) {
supersteps = 100;
} else if (NBLOOPS >= 100) {
supersteps = 10;
} else if (NBLOOPS <= 10) {
supersteps = 1;
}
if (globals->create && (!(globals->remaining_loop % supersteps))) {
XBT_INFO("Begin a new loop. Still to do: %d", globals->remaining_loop);
} else if (!(globals->remaining_loop % supersteps)) {
XBT_VERB("Got token(%d) from %s remaining_loop=%d",
msg, xbt_socket_peer_name(expeditor), globals->remaining_loop);
}
/* 4. If the right shouldn't be stopped yet */
if (globals->remaining_loop > 0) {
msg += 1;
XBT_DEBUG("Send token(%d) to %s:%d", msg,
xbt_socket_peer_name(globals->tosuccessor),
xbt_socket_peer_port(globals->tosuccessor));
/* 5. Send the token as payload of a stoken message to the successor */
TRY {
gras_msg_send(globals->tosuccessor, "stoken", &msg);
/* 6. Deal with errors */
}
gras_socket_close(globals->sock);
RETHROWF("Unable to forward token: %s");
}
}
/* DO NOT CLOSE THE expeditor SOCKET since the client socket is
reused by our predecessor.
Closing this side would thus create troubles */
/* 7. Decrease the remaining_loop integer. */
globals->remaining_loop -= 1;
/* 8. Repport the hop number to the user at the end */
if (globals->remaining_loop == -1 && globals->create) {
double elapsed = gras_os_time() - globals->start_time;
XBT_INFO("Shut down the token-ring. There was %d hops.", msg);
XBT_VERB("Elapsed time: %g", elapsed);
}
/* 9. Tell GRAS that we consummed this message */
return 0;
} /* end_of_node_cb_stoken_handler */

4) The main function

This function is splited in two parts: The first one performs all the needed initialisations (points 1-7) while the end (point 8. below) calls gras_msg_handle() as long as the planned amount of ring loops are not performed.

int node(int argc, char *argv[])
{
node_data_t *globals;
const char *host;
int myport;
int peerport;
/* 1. Init the GRAS infrastructure and declare my globals */
gras_init(&argc, argv);
globals = gras_userdata_new(node_data_t);
/* 2. Get the successor's address. The command line overrides
defaults when specified */
host = "127.0.0.1";
myport = 4000;
peerport = 4000;
if (argc >= 4) {
myport = atoi(argv[1]);
host = argv[2];
peerport = atoi(argv[3]);
}
/* 3. Save successor's address in global var */
globals->remaining_loop = NBLOOPS;
globals->create = 0;
globals->tosuccessor = NULL;
if (!gras_os_getpid() % 100 || gras_if_RL())
XBT_INFO("Launch node listening on %d (successor on %s:%d)",
myport, host, peerport);
/* 4. Register the known messages. */
/* 5. Create my master socket for listening */
globals->sock = gras_socket_server(myport);
gras_os_sleep(1.0); /* Make sure all server sockets are created */
/* 6. Create socket to the successor on the ring */
XBT_DEBUG("Connect to my successor on %s:%d", host, peerport);
globals->tosuccessor = gras_socket_client(host, peerport);
/* 7. Register my callback */
gras_cb_register("stoken", &node_cb_stoken_handler);
/* 8. One node has to create the token at startup.
It's specified by a command line argument */
if (argc >= 5
&& !strncmp("--create-token", argv[4], strlen("--create-token")))
globals->create = 1;
if (globals->create) {
int token = 0;
globals->start_time = gras_os_time();
globals->remaining_loop = NBLOOPS - 1;
XBT_INFO("Create the token (with value %d) and send it to %s:%d",
token, host, peerport);
TRY {
gras_msg_send(globals->tosuccessor, "stoken", &token);
}
RETHROWF("Unable to send the freshly created token: %s");
}
}
/* 8. Wait up to 10 seconds for an incoming message to handle */
while (globals->remaining_loop > (globals->create ? -1 : 0)) {
XBT_DEBUG("looping (remaining_loop=%d)", globals->remaining_loop);
}
gras_os_sleep(1.0); /* FIXME: if the sender quited, receive fail */
/* 9. Free the allocated resources, and shut GRAS down */
gras_socket_close(globals->sock);
gras_socket_close(globals->tosuccessor);
free(globals);
return 0;
} /* end_of_node */

A simple RPC for matrix multiplication

This example implements a remote matrix multiplication. It involves a client (creating the matrices and sending the multiplications requests) and a server (computing the multiplication on client's behalf).

This example also constitutes a more advanced example of data description mechanisms, since the message payload type is a bit more complicated than in other examples such as the ping one (Ping-Pong).

It works the following way (not very different from the ping example):

  • Both the client and the server register all needed messages and datatypes
  • The server registers a callback to the "request" message, which computes what needs to be and returns the result to the expeditor.
  • The client creates two matrices, ask for their multiplication and check the server's answer.

This example resides in the examples/gras/mmrpc/mmrpc.c file.

Table of contents of the mmrpc example


1) Common code to the client and the server (mmrpc_common.c and mmrpc.h)

1.a) Module header (mmrpc.h)

This loads the gras header and declare the function's prototypes as well as the matrix size.

#include "gras.h"
#define MATSIZE 128
/* register messages which may be sent and their payload
(common to client and server) */
void mmrpc_register_messages(void);
/* Function prototypes */
int server(int argc, char *argv[]);
int client(int argc, char *argv[]);

1.b) Register the data types (mmrpc.h)

The messages involved in a matrix of double. This type is automatically known by the GRAS mecanism, using the gras_datadesc_matrix() function of the xbt/matrix module.

1.c) Logging category definition (mmrpc_common.c)

Let's first load the module header and declare a logging category (see Logging support for more info on logging). This logging category does live in this file (ie the required symbols are defined here and declared as "extern" in any other file using them). That is why we use XBT_LOG_NEW_DEFAULT_CATEGORY here and XBT_LOG_EXTERNAL_DEFAULT_CATEGORY in mmrpc_client.c and mmrpc_server.c.

#include "mmrpc.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(MatMult, "Messages specific to this example");

1.d) Register the messages (mmrpc_common.c)

This function, called by both the client and the server is in charge of declaring the existing messages to GRAS.

The datatype description builded that way can then be used to build an array datatype or to declare messages.

void mmrpc_register_messages(void)
{
xbt_datadesc_type_t matrix_type, request_type;
matrix_type =
xbt_datadesc_matrix(xbt_datadesc_by_name("double"), NULL);
request_type =
xbt_datadesc_array_fixed("s_matrix_t(double)[2]", matrix_type, 2);
gras_msgtype_declare("answer", matrix_type);
gras_msgtype_declare("request", request_type);
}

[Back to Table of contents of the mmrpc example]

2) Server's code (mmrpc_server.c)

2.a) Server intial settings

All module symbols live in the mmrpc_common.c file. We thus have to define XBT_DEFINE_TYPE_EXTERN to the preprocessor so that the XBT_DEFINE_TYPE symbols don't get included here. Likewise, we use XBT_LOG_EXTERNAL_DEFAULT_CATEGORY to get the log category in here.

#define XBT_DEFINE_TYPE_EXTERN
#include "xbt/matrix.h"
#include "mmrpc.h"

2.b) The callback to the mmrpc message

Here is the callback run when the server receives any mmrpc message (this will be registered later by the server). Note the way we get the message payload. In the ping example, there was one additional level of pointer indirection (see 2.b) The callback to the ping message). This is because the payload is an array here (ie a pointer) whereas it is a scalar in the ping example.

static int server_cb_request_handler(gras_msg_cb_ctx_t ctx,
void *payload_data)
{
/* 1. Get the payload into the data variable */
xbt_matrix_t *request = (xbt_matrix_t *) payload_data;
xbt_matrix_t result;
/* 2. Do the computation */
result = xbt_matrix_double_new_mult(request[0], request[1]);
/* 3. Send it back as payload of a pong message to the expeditor */
gras_msg_send(expeditor, "answer", &result);
/* 4. Cleanups */
xbt_matrix_free(request[0]);
xbt_matrix_free(request[1]);
xbt_matrix_free(result);
return 0;
} /* end_of_server_cb_request_handler */

2.c) The "main" of the server

This is the "main" of the server. You must not write any main() function yourself. Instead, you just have to write a regular function like this one which will act as a main.

static xbt_socket_t try_gras_socket_server(int port)
{
volatile xbt_socket_t sock = NULL;
TRY {
sock = gras_socket_server(port);
}
RETHROWF("Unable to establish a server socket: %s");
}
return sock;
}
int server(int argc, char *argv[])
{
xbt_socket_t sock = NULL;
int port = 4002;
/* 1. Init the GRAS infrastructure */
gras_init(&argc, argv);
/* 2. Get the port I should listen on from the command line, if specified */
if (argc == 2) {
port = atoi(argv[1]);
}
/* 3. Register the known messages and payloads. */
mmrpc_register_messages();
/* 4. Register my callback */
gras_cb_register("request", &server_cb_request_handler);
/* 5. Create my master socket */
XBT_INFO("Launch server (port=%d)", port);
sock = try_gras_socket_server(port);
/* 6. Wait up to 10 minutes for an incoming message to handle */
/* 7. Free the allocated resources, and shut GRAS down */
return 0;
} /* end_of_server */

[Back to Table of contents of the mmrpc example]

3) Client's code (mmrpc_client.c)

2.a) Server intial settings

As for the server, some extra love is needed to make sure that automatic datatype parsing and log categories do work even if we are using several files.

#define XBT_DEFINE_TYPE_EXTERN
#include "xbt/matrix.h"
#include "mmrpc.h"

3.b) Client's "main" function

This function is quite straightforward, and the inlined comments should be enough to understand it.

int client(int argc, char *argv[])
{
xbt_socket_t toserver = NULL; /* peer */
xbt_matrix_t request[2], answer;
int i;
_XBT_GNUC_UNUSED int j;
const char *host = "127.0.0.1";
int port = 4000;
/* 1. Init the GRAS's infrastructure */
gras_init(&argc, argv);
/* 2. Get the server's address. The command line override defaults when specified */
if (argc == 3) {
host = argv[1];
port = atoi(argv[2]);
}
XBT_INFO("Launch client (server on %s:%d)", host, port);
/* 3. Register the messages (before use) */
mmrpc_register_messages();
/* 4. Create a socket to speak to the server */
while (!(toserver = try_gras_socket_client(host, port)))
XBT_INFO("Connected to %s:%d.", host, port);
/* 5. Keep the user informed of what's going on */
XBT_INFO(">>>>>>>> Connected to server which is on %s:%d <<<<<<<<",
/* 6. Prepare and send the request to the server */
request[0] = xbt_matrix_double_new_id(MATSIZE, MATSIZE);
request[1] = xbt_matrix_double_new_rand(MATSIZE, MATSIZE);
/*
xbt_matrix_dump(request[0],"C:sent0",0,xbt_matrix_dump_display_double);
xbt_matrix_dump(request[1],"C:sent1",0,xbt_matrix_dump_display_double);
*/
gras_msg_send(toserver, "request", &request);
xbt_matrix_free(request[0]);
XBT_INFO(">>>>>>>> Request sent to %s:%d <<<<<<<<",
/* 7. Wait for the answer from the server, and deal with issues */
gras_msg_wait(6000, "answer", &from, &answer);
/*
xbt_matrix_dump(answer,"C:answer",0,xbt_matrix_dump_display_double);
*/
for (i = 0; i < MATSIZE; i++)
for (j = 0; i < MATSIZE; i++)
xbt_assert(xbt_matrix_get_as(answer, i, j, double) ==
xbt_matrix_get_as(request[1], i, j, double),
"Answer does not match expectations. Found %f at cell %d,%d instead of %f",
xbt_matrix_get_as(answer, i, j, double), i, j,
xbt_matrix_get_as(request[1], i, j, double));
/* 8. Keep the user informed of what's going on, again */
XBT_INFO(">>>>>>>> Got answer from %s:%d (values are right) <<<<<<<<",
/* 9. Free the allocated resources, and shut GRAS down */
xbt_matrix_free(request[1]);
xbt_matrix_free(answer);
gras_socket_close(toserver);
return 0;
} /* end_of_client */

[Back to Table of contents of the mmrpc example]

Some timer games

This example fools around with the GRAS timers (Timers). It is mainly a regression test, since it uses almost all timer features.

The main program registers a repetititive task and a delayed one, and then loops until the still_to_do variables of its globals reach 0. The delayed task set it to 5, and the repetititive one decrease it each time. Here is an example of output:

Initialize GRAS
 Initialize XBT
 [1108335471] Programming the repetitive_action with a frequency of 1.000000 sec
 [1108335471] Programming the delayed_action for after 2.000000 sec
 [1108335471] Have a rest
 [1108335472] Canceling the delayed_action.
 [1108335472] Re-programming the delayed_action for after 2.000000 sec
 [1108335472] Repetitive_action has nothing to do yet
 [1108335473] Repetitive_action has nothing to do yet
 [1108335473] delayed_action setting globals->still_to_do to 5
 [1108335474] repetitive_action decrementing globals->still_to_do. New value: 4
 [1108335475] repetitive_action decrementing globals->still_to_do. New value: 3
 [1108335476] repetitive_action decrementing globals->still_to_do. New value: 2
 [1108335477] repetitive_action decrementing globals->still_to_do. New value: 1
 [1108335478] repetitive_action decrementing globals->still_to_do. New value: 0
 Exiting GRAS

Source code:

1. Declarations and headers

#include "gras.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(test, "Logging specific to this test");
#define REPEAT_INTERVAL 1.0
#define DELAY_INTERVAL 2.0
#define LOOP_COUNT 5
typedef struct {
int still_to_do;
} my_globals;

2. Source code of the delayed action

static void repetitive_action(void)
{
my_globals *globals = (my_globals *) gras_userdata_get();
/* Stop if nothing to do yet */
if (globals->still_to_do <= 0) {
XBT_INFO("Repetitive_action has nothing to do yet");
return;
}
if (globals->still_to_do == 1) {
/* Unregister myself if I'm done */
gras_timer_cancel_repeat(REPEAT_INTERVAL, repetitive_action);
}
("repetitive_action decrementing globals->still_to_do. New value: %d",
globals->still_to_do - 1);
globals->still_to_do--; /* should be the last line of the action since value=0 stops the program */
} /* end_of_repetitive_action */

3. Source code of the repetitive action

static void delayed_action(void)
{
my_globals *globals = (my_globals *) gras_userdata_get();
XBT_INFO("delayed_action setting globals->still_to_do to %d", LOOP_COUNT);
globals->still_to_do = LOOP_COUNT;
} /* end_of_delayed_action */

4. Source code of main function

int client(int argc, char *argv[])
{
my_globals *globals;
gras_init(&argc, argv);
globals = gras_userdata_new(my_globals);
globals->still_to_do = -1;
XBT_INFO("Programming the repetitive_action with a frequency of %f sec",
REPEAT_INTERVAL);
gras_timer_repeat(REPEAT_INTERVAL, repetitive_action);
XBT_INFO("Programming the delayed_action for after %f sec", DELAY_INTERVAL);
gras_timer_delay(REPEAT_INTERVAL, delayed_action);
XBT_INFO("Have a rest");
gras_os_sleep(DELAY_INTERVAL / 2.0);
XBT_INFO("Canceling the delayed_action.");
gras_timer_cancel_delay(REPEAT_INTERVAL, delayed_action);
XBT_INFO("Re-programming the delayed_action for after %f sec",
DELAY_INTERVAL);
gras_timer_delay(REPEAT_INTERVAL, delayed_action);
while (globals->still_to_do == -1 || /* Before delayed action runs */
globals->still_to_do >
0 /* after delayed_action, and not enough repetitive_action */ ) {
XBT_DEBUG("Prepare to handle messages for 5 sec (still_to_do=%d)",
globals->still_to_do);
}
xbt_free(globals);
return 0;
} /* end_of_client */

Using SMPI

You should check our online tutorial section that contains a dedicated tutorial.

Using Model Checking

You should check our online tutorial section that contains a dedicated tutorial.

Using Trace

Check out the Tracing Simulations for Visualization section.

You should check our online tutorial section that contains a dedicated tutorial.

Using SimDag

You should check our online tutorial section that contains a dedicated tutorial.

Using SIMIX

You should check our online tutorial section that contains a dedicated tutorial.