SimGrid 3.7.1
Scalable simulation of distributed systems
Lesson 7: Using internal timers

Table of Contents


Introduction

The messaging primitives we saw until now allow the processes to react to external events. This is good, but sometimes, you want the same action to be done periodically. Think of a system based on a group of processes. If you want to give some adaptability to this system, you shouldn't hardcode the memberships but have the members send a message to a coordinator to register to the system.

This add some dynamism to your system since new members can join at any time. To have a process leaving the system, you can imagine an "unregister" message symetric to the "register" one. But how will you deal with failures? What if a process leaves without being given the ability to inform the coordinator?

One solution is to have the members re-register periodically, so that the coordinator can detect the processes which didn't do so since a while, and dismiss them.

To implement this in GRAS, we need some more functions: gras_timer_repeat() allows to specify a periodic action and gras_timer_delay() allows to get an action done once a given delay expires. gras_timer_cancel_delay() and gras_timer_cancel_repeat() allow to remove already declared timers. Actions must be function without argument nor result (void my_action(void){ ... }).

It is important to note that timers are not prehemptive. They will not start as soon as they are ready. Instead, they get served when you go into gras_msg_handle() (and they are served before incoming messages). This is because allowing timers to run in parallel to the callbacks would add parallelism to the user code, which would have to protect data with mutexes. This is a level of complexity I really don't want for user code. If you really need several running entities, simply run several processes (see The model provided by GRAS for more details).

Putting timers into action

We will change the client of our example so that it send an hello message every half second to the server. Then we will add a delayed action scheduled 5 seconds later in charge of stopping every processes. For this to work, we first need to add a global to the server too, containing the socket binded onto the server (to send messages) and a boolean indicating whether we are done or not, just like we did on the server side in Lesson 5: Using globals in processes. Here is the resulting global structure:

/* client_data */
typedef struct {
  gras_socket_t toserver;
  int done;
} client_data_t;

Then, we define the repetitive action in charge of sending messages to the server:

void client_do_hello(void)
{
  client_data_t *globals = (client_data_t *) gras_userdata_get();

  gras_msg_send(globals->toserver, "hello", NULL);
  XBT_INFO("Hello sent to server");
}                               /* end_of_client_do_hello */

This timer is installed the following way. You simply tell the action to schedule and its periodicity.

  gras_timer_repeat(0.5, client_do_hello);

Desinstalling this is not harder. You tell the action to unschedule, and the periodicity at which it was desinstalled (so that the same action can be scheduled at different intervals, and each of them be desinstallable separately).

  gras_timer_cancel_repeat(0.5, client_do_hello);

Then comes the delayed action in charge of stopping everything, which should be self-explanatory at this point. It could be cancelled before its expiration using gras_timer_cancel_delay(), which accepts exactly the same kind of arguments than gras_timer_cancel_repeat().

void client_do_stop(void)
{
  client_data_t *globals = (client_data_t *) gras_userdata_get();

  gras_msg_send(globals->toserver, "kill", NULL);
  XBT_INFO("Kill sent to server");

  gras_timer_cancel_repeat(0.5, client_do_hello);

  globals->done = 1;
  XBT_INFO("Break the client's while loop");
}                               /* end_of_client_do_stop */

Finally, we should change the client main function to adapt to these modifications, as you can see in the recapping below.

Recapping everything together

The program now reads:

/* Copyright (c) 2006, 2007, 2009, 2010. The SimGrid Team.
 * All rights reserved.                                                     */

/* This program is free software; you can redistribute it and/or modify it
  * under the terms of the license (GNU LGPL) which comes with this package. */

#include <gras.h>

XBT_LOG_NEW_DEFAULT_CATEGORY(test, "My little example");

/* *********************** Server *********************** */
typedef struct {
  int killed;
} server_data_t;

int server_kill_cb(gras_msg_cb_ctx_t ctx, void *payload)
{
  gras_socket_t client = gras_msg_cb_ctx_from(ctx);
  server_data_t *globals = (server_data_t *) gras_userdata_get();

  XBT_CRITICAL("Argh, killed by %s:%d! Bye folks...",
            gras_socket_peer_name(client), gras_socket_peer_port(client));

  globals->killed = 1;

  return 0;
}                               /* end_of_kill_callback */

int server_hello_cb(gras_msg_cb_ctx_t ctx, void *payload)
{
  gras_socket_t client = gras_msg_cb_ctx_from(ctx);

  XBT_INFO("Cool, we received the message from %s:%d.",
        gras_socket_peer_name(client), gras_socket_peer_port(client));

  return 0;
}                               /* end_of_hello_callback */

int server(int argc, char *argv[])
{
  gras_socket_t mysock;         /* socket on which I listen */
  server_data_t *globals;

  gras_init(&argc, argv);

  globals = gras_userdata_new(server_data_t *);
  globals->killed = 0;

  gras_msgtype_declare("hello", NULL);
  gras_msgtype_declare("kill", NULL);
  mysock = gras_socket_server(atoi(argv[1]));

  gras_cb_register("hello", &server_hello_cb);
  gras_cb_register("kill", &server_kill_cb);

  while (!globals->killed) {
    gras_msg_handle(60);
  }

  gras_exit();
  return 0;
}

/* *********************** Client *********************** */
/* client_data */
typedef struct {
  gras_socket_t toserver;
  int done;
} client_data_t;


void client_do_hello(void)
{
  client_data_t *globals = (client_data_t *) gras_userdata_get();

  gras_msg_send(globals->toserver, "hello", NULL);
  XBT_INFO("Hello sent to server");
}                               /* end_of_client_do_hello */

void client_do_stop(void)
{
  client_data_t *globals = (client_data_t *) gras_userdata_get();

  gras_msg_send(globals->toserver, "kill", NULL);
  XBT_INFO("Kill sent to server");

  gras_timer_cancel_repeat(0.5, client_do_hello);

  globals->done = 1;
  XBT_INFO("Break the client's while loop");
}                               /* end_of_client_do_stop */

int client(int argc, char *argv[])
{
  gras_socket_t mysock;         /* socket on which I listen */
  client_data_t *globals;

  gras_init(&argc, argv);

  gras_msgtype_declare("hello", NULL);
  gras_msgtype_declare("kill", NULL);
  mysock = gras_socket_server_range(1024, 10000, 0, 0);

  XBT_VERB("Client ready; listening on %d", gras_socket_my_port(mysock));

  globals = gras_userdata_new(client_data_t *);
  globals->done = 0;

  gras_os_sleep(1.5);           /* sleep 1 second and half */
  globals->toserver = gras_socket_client(argv[1], atoi(argv[2]));

  XBT_INFO("Programming the repetitive action with a frequency of 0.5 sec");
  gras_timer_repeat(0.5, client_do_hello);

  XBT_INFO("Programming the delayed action in 5 secs");
  gras_timer_delay(5, client_do_stop);

  while (!globals->done) {
    gras_msg_handle(60);
  }

  gras_exit();
  return 0;
}

Which produces the expected output:

$ ./test_server 12345 & ./test_client 127.0.0.1 12345
[arthur:client:(27825) 0.000016] [test/INFO] Programming the repetitive action with a frequency of 0.5 sec
[arthur:client:(27825) 0.000125] [test/INFO] Programming the delayed action in 5 secs
[arthur:client:(27825) 0.500685] [test/INFO] Hello sent to server
[arthur:client:(27825) 1.000855] [test/INFO] Hello sent to server
[arthur:client:(27825) 1.501094] [test/INFO] Hello sent to server
[arthur:client:(27825) 2.001261] [test/INFO] Hello sent to server
[arthur:client:(27825) 2.501446] [test/INFO] Hello sent to server
[arthur:client:(27825) 3.001591] [test/INFO] Hello sent to server
[arthur:client:(27825) 3.501798] [test/INFO] Hello sent to server
[arthur:client:(27825) 4.002111] [test/INFO] Hello sent to server
[arthur:client:(27825) 4.502264] [test/INFO] Hello sent to server
[arthur:client:(27825) 5.000513] [test/INFO] Kill sent to server
[arthur:client:(27825) 5.000562] [test/INFO] Break the client's while loop
[arthur:client:(27825) 5.000594] [gras/INFO] Exiting GRAS
[arthur:server:(27821) 0.000014] [test/INFO] Cool, we received the message from 127.0.0.1:1024.
[arthur:server:(27821) 0.500134] [test/INFO] Cool, we received the message from 127.0.0.1:1024.
[arthur:server:(27821) 1.000349] [test/INFO] Cool, we received the message from 127.0.0.1:1024.
[arthur:server:(27821) 1.500519] [test/INFO] Cool, we received the message from 127.0.0.1:1024.
[arthur:server:(27821) 2.000788] [test/INFO] Cool, we received the message from 127.0.0.1:1024.
[arthur:server:(27821) 2.500996] [test/INFO] Cool, we received the message from 127.0.0.1:1024.
[arthur:server:(27821) 3.001083] [test/INFO] Cool, we received the message from 127.0.0.1:1024.
[arthur:server:(27821) 3.501370] [test/INFO] Cool, we received the message from 127.0.0.1:1024.
[arthur:server:(27821) 4.001514] [test/INFO] Cool, we received the message from 127.0.0.1:1024.
[arthur:server:(27821) 4.499772] test.c:15: [test/CRITICAL] Argh, killed by 127.0.0.1:1024! Bye folks...
[arthur:server:(27821) 4.499823] [gras/INFO] Exiting GRAS
$
$ ./test_simulator platform.xml test.xml
[Boivin:client:(2) 0.000000] [test/INFO] Programming the repetitive action with a frequency of 0.5 sec
[Boivin:client:(2) 0.000000] [test/INFO] Programming the delayed action in 5 secs
[Boivin:client:(2) 0.500537] [test/INFO] Hello sent to server
[Jacquelin:server:(1) 0.500537] [test/INFO] Cool, we received the message from Boivin:1024.
[Boivin:client:(2) 1.000537] [test/INFO] Hello sent to server
[Jacquelin:server:(1) 1.000537] [test/INFO] Cool, we received the message from Boivin:1024.
[Boivin:client:(2) 1.500537] [test/INFO] Hello sent to server
[Jacquelin:server:(1) 1.500537] [test/INFO] Cool, we received the message from Boivin:1024.
[Boivin:client:(2) 2.000537] [test/INFO] Hello sent to server
[Jacquelin:server:(1) 2.000537] [test/INFO] Cool, we received the message from Boivin:1024.
[Boivin:client:(2) 2.500537] [test/INFO] Hello sent to server
[Jacquelin:server:(1) 2.500537] [test/INFO] Cool, we received the message from Boivin:1024.
[Boivin:client:(2) 3.000537] [test/INFO] Hello sent to server
[Jacquelin:server:(1) 3.000537] [test/INFO] Cool, we received the message from Boivin:1024.
[Boivin:client:(2) 3.500537] [test/INFO] Hello sent to server
[Jacquelin:server:(1) 3.500537] [test/INFO] Cool, we received the message from Boivin:1024.
[Boivin:client:(2) 4.000537] [test/INFO] Hello sent to server
[Jacquelin:server:(1) 4.000537] [test/INFO] Cool, we received the message from Boivin:1024.
[Boivin:client:(2) 4.500537] [test/INFO] Hello sent to server
[Jacquelin:server:(1) 4.500537] [test/INFO] Cool, we received the message from Boivin:1024.
[Boivin:client:(2) 5.000537] [test/INFO] Hello sent to server
[Jacquelin:server:(1) 5.000537] [test/INFO] Cool, we received the message from Boivin:1024.
[Boivin:client:(2) 5.001074] [test/INFO] Kill sent to server
[Boivin:client:(2) 5.001074] [test/INFO] Break the client's while loop
[Boivin:client:(2) 5.001074] [gras/INFO] Exiting GRAS
[Jacquelin:server:(1) 5.001074] test.c:15: [test/CRITICAL] Argh, killed by Boivin:1024! Bye folks...
[Jacquelin:server:(1) 5.001074] [gras/INFO] Exiting GRAS
$

Go to Lesson 8: Handling errors through exceptions


Back to the main Simgrid Documentation page The version of SimGrid documented here is v3.7.1.
Documentation of other versions can be found in their respective archive files (directory doc/html).
Generated by doxygen