Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Write the lesson 10 on RPC in GRAS
authormquinson <mquinson@48e7efb5-ca39-0410-a469-dd3cf9ba447f>
Mon, 6 Nov 2006 16:32:31 +0000 (16:32 +0000)
committermquinson <mquinson@48e7efb5-ca39-0410-a469-dd3cf9ba447f>
Mon, 6 Nov 2006 16:32:31 +0000 (16:32 +0000)
git-svn-id: svn+ssh://scm.gforge.inria.fr/svn/simgrid/simgrid/trunk@2910 48e7efb5-ca39-0410-a469-dd3cf9ba447f

doc/gtut-files/10-rpc.c [new file with mode: 0644]
doc/gtut-files/10-rpc.output [new file with mode: 0644]
doc/gtut-tour-10-rpc.doc

diff --git a/doc/gtut-files/10-rpc.c b/doc/gtut-files/10-rpc.c
new file mode 100644 (file)
index 0000000..3c95494
--- /dev/null
@@ -0,0 +1,121 @@
+#include <stdlib.h>
+#include <gras.h>
+
+XBT_LOG_NEW_DEFAULT_CATEGORY(test,"My little example");
+
+typedef struct {
+   int done;
+} server_data_t;
+int server_done_cb(gras_msg_cb_ctx_t ctx, void *payload) {
+  server_data_t *globals=(server_data_t*)gras_userdata_get();
+  globals->done = 1;
+  INFO0("Server done");
+   
+  return 1;
+} /* end_of_done_callback */
+
+void message_declaration(void) {
+  gras_msgtype_declare_rpc("convert a2i", gras_datadesc_by_name("string"), gras_datadesc_by_name("long"));
+  gras_msgtype_declare_rpc("convert i2a", gras_datadesc_by_name("long"), gras_datadesc_by_name("string"));
+  /* the other message allowing the client to stop the server after use */
+  gras_msgtype_declare("done", NULL); 
+}
+
+int server_convert_i2a_cb(gras_msg_cb_ctx_t ctx, void *payload) {
+  long data = *(long*)payload;
+  char *result;
+  char *p;
+
+  INFO1("Convert %ld to string",data);  
+  result = bprintf("%ld", data);   
+  INFO2("%ld converted to string: %s",data,result);
+   
+  gras_msg_rpcreturn(60,ctx,&result);
+  free(result);
+  return 1;
+} /* end_of_convert_callback */
+
+int server_convert_a2i_cb(gras_msg_cb_ctx_t ctx, void *payload) {
+  char *string = *(char**)payload;
+  long result;
+  char *p;
+
+  INFO1("Convert %s to long",string);
+  result = strtol(string, &p, 10);
+   
+  if (*p != '\0')
+     THROW2(arg_error,0,"Error while converting %s: this does not seem to be a valid number (problem at '%s')",string,p);
+             
+  gras_msg_rpcreturn(60,ctx,&result);
+  return 1;
+} /* end_of_convert_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->done=0;
+
+  message_declaration();
+  mysock = gras_socket_server(atoi(argv[1]));
+   
+  gras_cb_register(gras_msgtype_by_name("convert a2i"),&server_convert_a2i_cb);
+  gras_cb_register(gras_msgtype_by_name("convert i2a"),&server_convert_i2a_cb);
+  gras_cb_register(gras_msgtype_by_name("done"),&server_done_cb);
+
+  while (!globals->done) {
+     gras_msg_handle(-1); /* blocking */
+  }
+    
+  gras_exit();
+  return 0;
+}
+
+int client(int argc, char *argv[]) {
+  gras_socket_t mysock;   /* socket on which I listen */
+  gras_socket_t toserver; /* socket used to write to the server */
+
+  gras_init(&argc,argv);
+
+  message_declaration();
+  mysock = gras_socket_server_range(1024, 10000, 0, 0);
+  
+  VERB1("Client ready; listening on %d", gras_socket_my_port(mysock));
+  
+  gras_os_sleep(1.5); /* sleep 1 second and half */
+  toserver = gras_socket_client(argv[1], atoi(argv[2]));
+  
+  long long_to_convert=4321;
+  char *string_result;
+  INFO1("Ask to convert %ld", long_to_convert);
+  gras_msg_rpccall(toserver, 60, gras_msgtype_by_name("convert i2a"), &long_to_convert, &string_result);
+  INFO2("The server says that %ld is equal to \"%s\".", long_to_convert, string_result);
+  free(string_result);
+   
+  char *string_to_convert="1234";
+  long long_result;
+  INFO1("Ask to convert %s", string_to_convert);
+  gras_msg_rpccall(toserver, 60, gras_msgtype_by_name("convert a2i"), &string_to_convert, &long_result);
+  INFO2("The server says that \"%s\" is equal to %d.", string_to_convert, long_result);
+   
+  xbt_ex_t e;
+  string_to_convert = "azerty";
+  TRY {
+     gras_msg_rpccall(toserver, 60, gras_msgtype_by_name("convert a2i"), &string_to_convert, &long_result);
+  } CATCH(e) {
+     INFO1("The server refuses to convert %s. Here is the received exception:",string_to_convert);
+     xbt_ex_display(&e);
+     INFO0("Again, previous exception was excepted");
+  }            
+   
+  gras_msg_send(toserver,gras_msgtype_by_name("done"), NULL);
+  INFO0("Stopped the server");
+   
+  gras_exit();
+  return 0;
+}
diff --git a/doc/gtut-files/10-rpc.output b/doc/gtut-files/10-rpc.output
new file mode 100644 (file)
index 0000000..e309f72
--- /dev/null
@@ -0,0 +1,58 @@
+$ ./test_server & ./test_client 127.0.0.1 
+[blaise:client:(2223) 0.000005] test.c:95: [test/INFO] Ask to convert 4321
+[blaise:client:(2223) 0.001210] test.c:97: [test/INFO] The server says that 4321 is equal to "4321".
+[blaise:client:(2223) 0.001227] test.c:102: [test/INFO] Ask to convert 1234
+[blaise:client:(2223) 0.001327] test.c:104: [test/INFO] The server says that "1234" is equal to 1234.
+[blaise:client:(2223) 0.040861] test.c:111: [test/INFO] The server refuses to convert azerty. Here is the received exception:
+[blaise:client:(2223) 0.040895] xbt/ex.c:229: [xbt_ex/CRITICAL] Error while converting azerty: this does not seem to be a valid number (problem at 'azerty')
+** SimGrid: UNCAUGHT EXCEPTION received on blaise(2223): category: invalid_arg; value: 0
+** Error while converting azerty: this does not seem to be a valid number (problem at 'azerty')
+** Thrown by server() in this process
+
+**   In server_convert_a2i_cb() at /home/mquinson/CVSIMPORT/gras/gras/doc/gtut-files/test.c:48 (static symbol)
+**   In gras_msg_handle() at gras/Msg/msg.c:634 (dynamic symbol)
+**   In server() at /home/mquinson/CVSIMPORT/gras/gras/doc/gtut-files/test.c:72 (static symbol)
+**   In main() at /home/mquinson/CVSIMPORT/gras/gras/doc/gtut-files/_test_server.c:21 (static symbol)
+**   In __libc_start_main() at ??:0 (dynamic symbol)
+**   In _start() at ../sysdeps/i386/elf/start.S:122 (static symbol)
+[blaise:client:(2223) 0.040933] test.c:113: [test/INFO] Again, previous exception was excepted
+[blaise:client:(2223) 0.040957] test.c:117: [test/INFO] Stopped the server
+[blaise:client:(2223) 0.040972] gras/gras.c:85: [gras/INFO] Exiting GRAS
+[blaise:server:(2220) 0.000005] test.c:30: [test/INFO] Convert 4321 to string
+[blaise:server:(2220) 0.000045] test.c:32: [test/INFO] 4321 converted to string: 4321
+[blaise:server:(2220) 0.000197] test.c:44: [test/INFO] Convert 1234 to long
+[blaise:server:(2220) 0.000294] test.c:44: [test/INFO] Convert azerty to long
+[blaise:server:(2220) 0.040123] test.c:12: [test/INFO] Server done
+[blaise:server:(2220) 0.040144] gras/gras.c:85: [gras/INFO] Exiting GRAS
+$
+$ killall test_server
+$
+$ ./test_simulator platform.xml test.xml
+[Boivin:client:(2) 0.000000] test.c:95: [test/INFO] Ask to convert 4321
+[Jacquelin:server:(1) 0.000538] test.c:30: [test/INFO] Convert 4321 to string
+[Jacquelin:server:(1) 0.000538] test.c:32: [test/INFO] 4321 converted to string: 4321
+[Boivin:client:(2) 0.001077] test.c:97: [test/INFO] The server says that 4321 is equal to "4321".
+[Boivin:client:(2) 0.001077] test.c:102: [test/INFO] Ask to convert 1234
+[Jacquelin:server:(1) 0.001615] test.c:44: [test/INFO] Convert 1234 to long
+[Boivin:client:(2) 0.002153] test.c:104: [test/INFO] The server says that "1234" is equal to 1234.
+[Jacquelin:server:(1) 0.002692] test.c:44: [test/INFO] Convert azerty to long
+[Boivin:client:(2) 0.003435] test.c:111: [test/INFO] The server refuses to convert azerty. Here is the received exception:
+[Boivin:client:(2) 0.003435] xbt/ex.c:229: [xbt_ex/CRITICAL] Error while converting azerty: this does not seem to be a valid number (problem at 'azerty')
+** SimGrid: UNCAUGHT EXCEPTION received on Boivin(2): category: invalid_arg; value: 0
+** Error while converting azerty: this does not seem to be a valid number (problem at 'azerty')
+** Thrown by server() in this process
+
+**   In server_convert_a2i_cb() at /home/mquinson/CVSIMPORT/gras/gras/doc/gtut-files/test.c:48 (static symbol)
+**   In gras_msg_handle() at gras/Msg/msg.c:634 (dynamic symbol)
+**   In server() at /home/mquinson/CVSIMPORT/gras/gras/doc/gtut-files/test.c:72 (static symbol)
+**   In launch_server() at /home/mquinson/CVSIMPORT/gras/gras/doc/gtut-files/_test_simulator.c:51 (static symbol)
+**   In __context_exit() at xbt/context.c:116 (dynamic symbol)
+**   In start_thread() at ??:0 (dynamic symbol)
+**   In clone() at ??:0 (dynamic symbol)
+[Boivin:client:(2) 0.003435] test.c:113: [test/INFO] Again, previous exception was excepted
+[Boivin:client:(2) 0.003972] test.c:117: [test/INFO] Stopped the server
+[Boivin:client:(2) 0.003972] gras/gras.c:85: [gras/INFO] Exiting GRAS
+[Jacquelin:server:(1) 0.003972] test.c:12: [test/INFO] Server done
+[Jacquelin:server:(1) 0.003972] gras/gras.c:85: [gras/INFO] Exiting GRAS
+[0.003972] msg/global.c:479: [msg_kernel/INFO] Congratulations ! Simulation terminated : all processes are over
+$
index 4684cf0..e573845 100644 (file)
@@ -1,18 +1,92 @@
 /**
 /**
-@page GRAS_tut_tour_rpc Lesson 10: Remote Procedure Calling (RPCing) (TODO)
+@page GRAS_tut_tour_rpc Lesson 10: Remote Procedure Calling (RPC)
 
 \section GRAS_tut_tour_rpc_toc Table of Contents
  - \ref GRAS_tut_tour_rpc_intro
 
 \section GRAS_tut_tour_rpc_toc Table of Contents
  - \ref GRAS_tut_tour_rpc_intro
- - \ref GRAS_tut_tour_rpc_use
+  - \ref GRAS_tut_tour_rpc_use
+      - \ref GRAS_tut_tour_rpc_use_declare
+      - \ref GRAS_tut_tour_rpc_use_i2a_cb
+      - \ref GRAS_tut_tour_rpc_use_a2i_cb
+      - \ref GRAS_tut_tour_rpc_use_rest
  - \ref GRAS_tut_tour_rpc_recap
    
 <hr>
 
 \section GRAS_tut_tour_rpc_intro Introduction
 
  - \ref GRAS_tut_tour_rpc_recap
    
 <hr>
 
 \section GRAS_tut_tour_rpc_intro Introduction
 
+So far, we saw how to send messages from one host to another, but quite
+often, we need a two-way message exchange: a "client" sends a request to a
+"server", and the server returns a result after doing some sort of
+computation. This design is often refered to as "Remote Procedure Call" or
+RPC for short.
+
+It is naturally possible to build RPC exchanges using only one-way messages,
+as the ones we used in GRAS so far, but it's a bit awkward. That is why GRAS
+provide a support for RPC, as we will now detail.
 
 \section GRAS_tut_tour_rpc_use Putting rpc into action
 
 
 \section GRAS_tut_tour_rpc_use Putting rpc into action
 
+We will build a simple RPC where clients use a remote server to convert
+strings into numbers and vice-versa (ie, changing between "1234" and 1234).
+To achieve its duty, the server will simply use the strtol function in one
+direction. In the other direction, we will use bprintf(). This is a sprintf()
+version allocating the needed room before doing the conversion. Its
+portability is discutable, but SimGrid declares this function when it cannot
+be found on the host architecture, so you can use it peacefully.
+
+\subsection GRAS_tut_tour_rpc_use_declare Declaring the RPC
+
+To declare a RPC message, we should simply use gras_msgtype_declare_rpc().
+Compared to gras_msgtype_declare() that we use to declare one-way messages,
+this function accepts one extra argument: the datatype of the answer
+message. In our example, we accept one string in input, and a long in
+output for the a2i conversion (a=char 2=to i=integer), and the contrary in
+the other direction.
+
+\dontinclude 10-rpc.c
+\skip gras_msgtype_declare_rpc
+\until long
+\until string
+
+\subsection GRAS_tut_tour_rpc_use_i2a_cb Declaring a simple RPC callback: the integer to string conversion
+
+RPC callbacks are very close to "regular" ones. The only difference is that
+they must call gras_msg_rpcreturn() at some point to return their result to
+the caller. This function accepts 3 arguments: First the timeout to use when
+sending back the result (we must use callbacks when doing network
+communication to avoid deadlocks and such issues). The second argument is
+the callback context that the callback got as first argument. It denotes how
+to reach the caller and such. The last argument is a pointer to a variable
+containing the result to pass to the caller. 
+
+Having the callee explicitly returning data to the caller allows to free
+data that were allocated to do the job asked by the client, as in this
+example. 
+
+\skip server_convert_i2a_cb
+\until end_of_convert_callback
+
+\subsection GRAS_tut_tour_rpc_use_a2i_cb RPC and exceptions: the string to integer conversion
+
+When converting strings into integer, we must deal with the possibility that
+the provided string is not a number. This is done very easily by raising an
+exception in the RPC callback. This exception will get captured by the
+middleware running the callback on the server side, sent accross the network
+to the client side, and revived here. In short, exceptions raised on callee
+side get passed automagically to the caller.
+
+\skip server_convert_a2i_cb
+\until end_of_convert_callback
+
+\subsection GRAS_tut_tour_rpc_use_rest The rest of the story
+
+The rest of the story is not really exciting. The server and the client are
+very classical compared to what we saw so far. We simply have a specific
+message "done" to stop the server when the client is done using it.
+
+This may also be the first time you see the xbt_ex_display() function, which
+allows to display an exception as if it were not catched without killing the
+process.
 
 \section GRAS_tut_tour_rpc_recap Recapping everything together
 
 
 \section GRAS_tut_tour_rpc_recap Recapping everything together