From 5783ed629595652c798bd0052b6e0ea7b1fd70fe Mon Sep 17 00:00:00 2001 From: mquinson Date: Mon, 6 Nov 2006 16:32:31 +0000 Subject: [PATCH] Write the lesson 10 on RPC in GRAS 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 | 121 +++++++++++++++++++++++++++++++++++ doc/gtut-files/10-rpc.output | 58 +++++++++++++++++ doc/gtut-tour-10-rpc.doc | 78 +++++++++++++++++++++- 3 files changed, 255 insertions(+), 2 deletions(-) create mode 100644 doc/gtut-files/10-rpc.c create mode 100644 doc/gtut-files/10-rpc.output diff --git a/doc/gtut-files/10-rpc.c b/doc/gtut-files/10-rpc.c new file mode 100644 index 0000000000..3c954945d0 --- /dev/null +++ b/doc/gtut-files/10-rpc.c @@ -0,0 +1,121 @@ +#include +#include + +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 index 0000000000..e309f720ef --- /dev/null +++ b/doc/gtut-files/10-rpc.output @@ -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 +$ diff --git a/doc/gtut-tour-10-rpc.doc b/doc/gtut-tour-10-rpc.doc index 4684cf03c1..e57384554e 100644 --- a/doc/gtut-tour-10-rpc.doc +++ b/doc/gtut-tour-10-rpc.doc @@ -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 - - \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
\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 +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 -- 2.20.1