Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
5aa00f8350b8a50678f566f4d8d5e8da9223fc18
[simgrid.git] / src / bindings / lua / lua_task.c
1 /* Copyright (c) 2010, 2012-2015. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 #include "lua_private.h"
8 #include "lua_utils.h"
9 #include "lua_state_cloner.h"
10 #include <lauxlib.h>
11
12 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(lua_task, bindings, "Lua bindings (task module)");
13
14 #define TASK_MODULE_NAME "simgrid.task"
15
16 /* ********************************************************************************* */
17 /*                                simgrid.task API                                   */
18 /* ********************************************************************************* */
19
20 /**
21  * \brief Ensures that a value in the stack is a valid task and returns it.
22  *        A task is "valid" if the value in the stack at the given index is
23  *          (a) a table
24  *          (b) has a field called "__simgrid_task" which
25  *          (c) contains the ctask itself (which can be typecast to msg_task_t*)
26  * \param L a Lua state
27  * \param index an index in the Lua stack
28  * \return the C task corresponding to this Lua task
29  */
30 msg_task_t sglua_check_task(lua_State* L, int index)
31 {
32   sglua_stack_dump("check task: ", L);
33   luaL_checktype(L, index, LUA_TTABLE);
34                                   /* ... task ... */
35   lua_getfield(L, index, "__simgrid_task");
36                                   /* ... task ... ctask */
37   msg_task_t task = *((msg_task_t*) luaL_checkudata(L, -1, TASK_MODULE_NAME));
38   lua_pop(L, 1);
39                                   /* ... task ... */
40
41   if (task == NULL) {
42     luaL_error(L, "This task was sent to someone else, you cannot access it anymore");
43   }
44
45   return task;
46 }
47
48 /**
49  * \brief Creates a new task and places it onto the stack.
50  * \param L a Lua state
51  * \return number of values returned to Lua
52  *
53  * - Argument 1 (string): name of the task
54  * - Argument 2 (number): computation size
55  * - Argument 3 (number): communication size
56  * - Return value (task): the task created
57  *
58  * A Lua task is a regular table with a full userdata inside, and both share
59  * the same metatable. For the regular table, the metatable allows OO-style
60  * writing such as your_task:send(someone) instead of your_task.send(your_task, someone)
61  * For the userdata, the metatable is used to check its type.
62  * TODO: make the task name an optional last parameter
63  */
64 static int l_task_new(lua_State* L)
65 {
66   XBT_DEBUG("Task new");
67   const char* name = luaL_checkstring(L, 1);
68   int comp_size    = (int) luaL_checkinteger(L, 2);
69   int msg_size     = (int) luaL_checkinteger(L, 3);
70                                   /* name comp comm */
71   lua_settop(L, 0);
72                                   /* -- */
73   msg_task_t msg_task = MSG_task_create(name, comp_size, msg_size, NULL);
74
75   lua_newtable(L);
76                                   /* task */
77   luaL_getmetatable(L, TASK_MODULE_NAME);
78                                   /* task mt */
79   lua_setmetatable(L, -2);
80                                   /* task */
81   msg_task_t* lua_task = (msg_task_t*) lua_newuserdata(L, sizeof(msg_task_t));
82                                   /* task ctask */
83   *lua_task = msg_task;
84   luaL_getmetatable(L, TASK_MODULE_NAME);
85                                   /* task ctask mt */
86   lua_setmetatable(L, -2);
87                                   /* task ctask */
88   lua_setfield(L, -2, "__simgrid_task");
89                                   /* task */
90   return 1;
91 }
92
93 /**
94  * \brief Returns the name of a task.
95  * \param L a Lua state
96  * \return number of values returned to Lua
97  *
98  * - Argument 1 (task): a task
99  * - Return value (string): name of the task
100  */
101 static int l_task_get_name(lua_State* L)
102 {
103   msg_task_t task = sglua_check_task(L, 1);
104   lua_pushstring(L, MSG_task_get_name(task));
105   return 1;
106 }
107
108 /**
109  * \brief Returns the computation duration of a task.
110  * \param L a Lua state
111  * \return number of values returned to Lua
112  *
113  * - Argument 1 (task): a task
114  * - Return value (number): computation duration of this task
115  */
116 static int l_task_get_computation_duration(lua_State* L)
117 {
118   msg_task_t task = sglua_check_task(L, 1);
119   lua_pushnumber(L, MSG_task_get_flops_amount(task));
120   return 1;
121 }
122
123 /**
124  * \brief Executes a task.
125  * \param L a Lua state
126  * \return number of values returned to Lua
127  *
128  * - Argument 1 (task): the task to execute
129  * - Return value (nil or string): nil if the task was successfully executed,
130  * or an error string in case of failure, which may be "task canceled" or
131  * "host failure"
132  */
133 static int l_task_execute(lua_State* L)
134 {
135   XBT_DEBUG("Called task_execute");
136   msg_task_t task = sglua_check_task(L, 1);
137   msg_error_t res = MSG_task_execute(task);
138
139   if (res == MSG_OK) {
140     return 0;
141   }
142   else {
143     lua_pushstring(L, sglua_get_msg_error(res));
144     return 1;
145   }
146 }
147
148 /**
149  * \brief Pops the Lua task from the stack and registers it so that the
150  * process can retrieve it later knowing the C task.
151  * \param L a lua state
152  */
153 void sglua_task_register(lua_State* L) {
154
155   msg_task_t task = sglua_check_task(L, -1);
156                                   /* ... task */
157   /* put in the C task a ref to the lua task so that the receiver finds it */
158   unsigned long ref = luaL_ref(L, LUA_REGISTRYINDEX);
159                                   /* ... */
160   MSG_task_set_data(task, (void*) ref);
161 }
162
163 /**
164  * \brief Pushes onto the stack the Lua task corresponding to a C task.
165  *
166  * The Lua task must have been previously registered with task_register so
167  * that it can be retrieved knowing the C task.
168  *
169  * \param L a lua state
170  * \param task a C task
171  */
172 void sglua_task_unregister(lua_State* L, msg_task_t task) {
173
174                                   /* ... */
175   /* the task is in my registry, put it onto my stack */
176   unsigned long ref = (unsigned long) MSG_task_get_data(task);
177   lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
178                                   /* ... task */
179   luaL_unref(L, LUA_REGISTRYINDEX, ref);
180   MSG_task_set_data(task, NULL);
181 }
182
183 /**
184  * \brief This function is called when a C task has just been copied.
185  *
186  * This callback is used to move the corresponding Lua task from the sender
187  * process to the receiver process (so that the receiver can access and deal
188  * with it).
189  * It is executed in SIMIX kernel mode when the communication finishes,
190  * before both processes are awaken. Thus, this function is thread-safe when
191  * user processes are executed in parallel, though it modifies the Lua
192  * stack of both processes to move the task.
193  * After this function, both Lua stacks are restored in their previous state.
194  * The task is moved from the registry of the sender to the registry of the
195  * receiver.
196  *
197  * \param task the task copied
198  * \param src_process the sender
199  * \param dst_process the receiver
200  */
201 static void task_copy_callback(msg_task_t task, msg_process_t src_process,
202     msg_process_t dst_process) {
203
204   lua_State* src = (lua_State*) MSG_process_get_data(src_process);
205   lua_State* dst = (lua_State*) MSG_process_get_data(dst_process);
206
207                                   /* src: ...
208                                      dst: ... */
209   sglua_task_unregister(src, task);
210                                   /* src: ... task */
211   sglua_copy_value(src, dst);
212                                   /* src: ... task
213                                      dst: ... task */
214   sglua_task_register(dst);
215                                   /* dst: ... */
216
217   /* the receiver is now the owner of the task and may destroy it:
218    * make the sender forget the C task so that it doesn't garbage */
219   lua_getfield(src, -1, "__simgrid_task");
220                                   /* src: ... task ctask */
221   msg_task_t* udata = (msg_task_t*) luaL_checkudata(src, -1, TASK_MODULE_NAME);
222   *udata = NULL;
223   lua_pop(src, 2);
224                                   /* src: ... */
225 }
226
227 /**
228  * \brief Sends a task to a mailbox and waits for its completion.
229  * \param L a Lua state
230  * \return number of values returned to Lua
231  *
232  * - Argument 1 (task): the task to send
233  * - Argument 2 (string or compatible): mailbox name, as a real string or any
234  * type convertible to string (numbers always are)
235  * - Argument 3 (number, optional): timeout (default is no timeout)
236  * - Return values (boolean + string): true if the communication was successful,
237  * or false plus an error string in case of failure, which may be "timeout",
238  * "host failure" or "transfer failure"
239  */
240 static int l_task_send(lua_State* L)
241 {
242   const char* mailbox = luaL_checkstring(L, 2);
243   msg_task_t task = sglua_check_task(L, 1);
244   double timeout;
245   if (lua_gettop(L) >= 3) {
246     timeout = luaL_checknumber(L, 3);
247   }
248   else {
249     timeout = -1;
250     /* no timeout by default */
251   }
252   lua_settop(L, 1);
253                                   /* task */
254   sglua_task_register(L);
255                                   /* -- */
256   msg_error_t res = MSG_task_send_with_timeout(task, mailbox, timeout);
257
258   if (res == MSG_OK) {
259     lua_pushboolean(L, 1);
260                                   /* true */
261     return 1;
262   }
263   else {
264     /* the communication has failed, I'm still the owner of the task */
265     sglua_task_unregister(L, task);
266                                   /* task */
267     lua_pushboolean(L, 0);
268                                   /* task false */
269     lua_pushstring(L, sglua_get_msg_error(res));
270                                   /* task false error */
271     return 2;
272   }
273 }
274
275 /**
276  * \brief Sends a task to a mailbox.
277  * \param L a Lua state
278  * \return number of values returned to Lua
279  *
280  * This is a non-blocking function: use simgrid.comm.wait() or
281  * simgrid.comm.test() to end the communication.
282  *
283  * - Argument 1 (task): the task to send
284  * - Argument 2 (string or compatible): mailbox name, as a real string or any
285  * type convertible to string (numbers always are)
286  * - Return value (comm): a communication object to be used later with wait or test
287  */
288 static int l_task_isend(lua_State* L)
289 {
290   const char* mailbox = luaL_checkstring(L, 2);
291   msg_task_t task = sglua_check_task(L, 1);
292                                   /* task mailbox ... */
293   lua_settop(L, 1);
294                                   /* task */
295   sglua_task_register(L);
296                                   /* -- */
297   msg_comm_t comm = MSG_task_isend(task, mailbox);
298
299   sglua_push_comm(L, comm);
300                                   /* comm */
301   return 1;
302 }
303
304 /**
305  * \brief Sends a task on a mailbox on a best effort way (detached send).
306  * \param L a Lua state
307  * \return number of values returned to Lua
308  *
309  * Like simgrid.task.isend, this is a non-blocking function.
310  * You can use this function if you don't care about when the communication
311  * ends and whether it succeeds.
312  * FIXME: isn't this equivalent to calling simgrid.task.isend() and ignoring
313  * the result?
314  *
315  * - Argument 1 (task): the task to send
316  * - Argument 2 (string or compatible): mailbox name, as a real string or any
317  * type convertible to string (numbers always are)
318  */
319 static int l_task_dsend(lua_State* L)
320 {
321   const char* mailbox = luaL_checkstring(L, 2);
322   msg_task_t task = sglua_check_task(L, 1);
323                                   /* task mailbox ... */
324   lua_settop(L, 1);
325                                   /* task */
326   sglua_task_register(L);
327                                   /* -- */
328   MSG_task_dsend(task, mailbox, NULL);
329   return 0;
330 }
331
332 /**
333  * \brief Receives a task.
334  * \param L a Lua state
335  * \return number of values returned to Lua
336  *
337  * - Argument 1 (string or compatible): mailbox name, as a real string or any
338  * type convertible to string (numbers always are)
339  * - Argument 2 (number, optional): timeout (default is no timeout)
340  * - Return values (task or nil + string): the task received, or nil plus an
341  * error message if the communication has failed
342  */
343 static int l_task_recv(lua_State* L)
344 {
345   msg_task_t task = NULL;
346   const char* mailbox = luaL_checkstring(L, 1);
347   double timeout;
348   if (lua_gettop(L) >= 2) {
349                                   /* mailbox timeout ... */
350     timeout = luaL_checknumber(L, 2);
351   }
352   else {
353                                   /* mailbox */
354     timeout = -1;
355     /* no timeout by default */
356   }
357                                   /* mailbox ... */
358   msg_error_t res = MSG_task_receive_with_timeout(&task, mailbox, timeout);
359
360   if (res == MSG_OK) {
361     sglua_task_unregister(L, task);
362                                   /* mailbox ... task */
363     return 1;
364   }
365   else {
366     lua_pushnil(L);
367                                   /* mailbox ... nil */
368     lua_pushstring(L, sglua_get_msg_error(res));
369                                   /* mailbox ... nil error */
370     return 2;
371   }
372 }
373
374 /**
375  * \brief Asynchronously receives a task on a mailbox.
376  * \param L a Lua state
377  * \return number of values returned to Lua
378  *
379  * This is a non-blocking function: use simgrid.comm.wait() or
380  * simgrid.comm.test() to end the communication and get the task in case of
381  * success.
382  *
383  * - Argument 1 (string or compatible): mailbox name, as a real string or any
384  * type convertible to string (numbers always are)
385  * - Return value (comm): a communication object to be used later with wait or test
386  */
387
388 static int l_task_irecv(lua_State* L)
389 {
390   const char* mailbox = luaL_checkstring(L, 1);
391                                   /* mailbox ... */
392   msg_task_t* task = xbt_new0(msg_task_t, 1); // FIXME fix this leak
393   msg_comm_t comm = MSG_task_irecv(task, mailbox);
394   sglua_push_comm(L, comm);
395                                   /* mailbox ... comm */
396   return 1;
397 }
398
399 static const luaL_Reg task_functions[] = {
400   {"new", l_task_new},
401   {"get_name", l_task_get_name},
402   {"get_computation_duration", l_task_get_computation_duration},
403   {"execute", l_task_execute},
404   {"send", l_task_send},
405   {"isend", l_task_isend},
406   {"dsend", l_task_dsend},
407   {"recv", l_task_recv},
408   {"irecv", l_task_irecv},
409   {NULL, NULL}
410 };
411
412 /**
413  * \brief Finalizes the userdata of a task.
414  * \param L a Lua state
415  * \return number of values returned to Lua
416  *
417  * - Argument 1 (userdata): a C task, possibly NULL if it was sent to another
418  * Lua state
419  */
420 static int l_task_gc(lua_State* L)
421 {
422   msg_task_t task = *((msg_task_t*) luaL_checkudata(L, 1, TASK_MODULE_NAME));
423   /* the task is NULL if I sent it to someone else */
424   if (task != NULL) {
425     MSG_task_destroy(task);
426   }
427   lua_settop(L, 0);
428   return 0;
429 }
430
431 /**
432  * \brief Returns a string representation of a C task.
433  * \param L a Lua state
434  * \return number of values returned to Lua
435  *
436  * - Argument 1 (userdata): a task
437  * - Return value (string): a string describing this task
438  */
439 static int l_task_tostring(lua_State* L)
440 {
441   msg_task_t task = *((msg_task_t*) luaL_checkudata(L, 1, TASK_MODULE_NAME));
442   lua_pushfstring(L, "Task: %p", task);
443   return 1;
444 }
445
446 /**
447  * \brief Metamethods of both a task table and the userdata inside it.
448  */
449 static const luaL_Reg task_meta[] = {
450   {"__gc", l_task_gc}, /* will be called only for userdata */
451   {"__tostring", l_task_tostring},
452   {NULL, NULL}
453 };
454
455 /**
456  * \brief Registers the task functions into the table simgrid.task.
457  *
458  * Also initialize the metatable of the task userdata type.
459  *
460  * \param L a lua state
461  */
462 void sglua_register_task_functions(lua_State* L)
463 {
464   /* create a table simgrid.task and fill it with task functions */
465   lua_getglobal(L, "simgrid");    /* simgrid */
466   luaL_newlib(L, task_functions); /* simgrid simgrid.task */
467
468   /* create the metatable for tasks, add it to the Lua registry */
469   luaL_newmetatable(L, TASK_MODULE_NAME); /* simgrid simgrid.task mt */
470
471   /* fill the metatable */
472   luaL_setfuncs(L, task_meta, 0); /* simgrid simgrid.task mt */
473   lua_pushvalue(L, -2);    /* simgrid simgrid.task mt simgrid.task */
474
475   /* metatable.__index = simgrid.task
476    * we put the task functions inside the task itself:
477    * this allows to write my_task:method(args) for
478    * simgrid.task.method(my_task, args) */
479   lua_setfield(L, -2, "__index"); /* simgrid simgrid.task mt */
480
481   lua_setmetatable(L, -2);        /* simgrid simgrid.task */
482   lua_setfield(L, -2, "task");    /* simgrid */
483   lua_pop(L, 1);                  /* -- */
484
485   /* set up MSG to copy Lua tasks between states */
486   MSG_task_set_copy_callback(task_copy_callback);
487 }
488