Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'lua'
authorChristophe Thiéry <christopho128@gmail.com>
Thu, 13 Oct 2011 11:47:21 +0000 (13:47 +0200)
committerChristophe Thiéry <christopho128@gmail.com>
Thu, 13 Oct 2011 11:47:21 +0000 (13:47 +0200)
Conflicts:
buildtools/Cmake/AddTests.cmake

14 files changed:
buildtools/Cmake/AddTests.cmake
buildtools/Cmake/DefinePackages.cmake
examples/lua/SimSplay/TODO
examples/lua/SimSplay/splay_school.lua
examples/lua/masterslave/slave.lua
examples/lua/state_cloner/deployment_duplicated_globals.xml [new file with mode: 0644]
examples/lua/state_cloner/duplicated_globals.lua [new file with mode: 0644]
examples/lua/state_cloner/duplicated_globals.tesh [new file with mode: 0644]
src/bindings/lua/lua_state_cloner.c [new file with mode: 0644]
src/bindings/lua/lua_state_cloner.h [new file with mode: 0644]
src/bindings/lua/lua_utils.c [new file with mode: 0644]
src/bindings/lua/lua_utils.h [new file with mode: 0644]
src/bindings/lua/simgrid_lua.c
src/bindings/lua/simgrid_lua.h

index 06b05c2..65184d5 100644 (file)
@@ -378,10 +378,12 @@ ENDIF(HAVE_TRACING)
 
 # Lua examples
 if(HAVE_LUA)
+ADD_TEST(lua-duplicated-globals                                ${CMAKE_BINARY_DIR}/bin/tesh ${TESH_OPTION} --cd ${CMAKE_HOME_DIRECTORY}/examples/lua/state_cloner duplicated_globals.tesh)
 ADD_TEST(lua-masterslave                               ${CMAKE_BINARY_DIR}/bin/tesh ${TESH_OPTION} --cd ${CMAKE_HOME_DIRECTORY}/examples/lua/masterslave master_slave.tesh)
 ADD_TEST(lua-mult-matrix                               ${CMAKE_BINARY_DIR}/bin/tesh ${TESH_OPTION} --cd ${CMAKE_HOME_DIRECTORY}/examples/lua/multi_matrix mult_matrix.tesh)
 ADD_TEST(lua-masterslave-bypass                ${CMAKE_BINARY_DIR}/bin/tesh ${TESH_OPTION} --cd ${CMAKE_HOME_DIRECTORY}/examples/lua/console master_slave_bypass.tesh)
 ADD_TEST(lua-msg-masterslave-console   ${CMAKE_BINARY_DIR}/bin/tesh ${TESH_OPTION} --cd ${CMAKE_BINARY_DIR}/examples/msg/masterslave --setenv srcdir=${CMAKE_HOME_DIRECTORY}/examples/msg/masterslave  ${CMAKE_HOME_DIRECTORY}/examples/msg/masterslave/masterslave_console.tesh)
+set_tests_properties(lua-duplicated-globals                            PROPERTIES ENVIRONMENT "LUA_CPATH=${CMAKE_BINARY_DIR}/examples/lua/?.so")
 set_tests_properties(lua-masterslave                           PROPERTIES ENVIRONMENT "LUA_CPATH=${CMAKE_BINARY_DIR}/examples/lua/?.so")
 set_tests_properties(lua-mult-matrix                           PROPERTIES ENVIRONMENT "LUA_CPATH=${CMAKE_BINARY_DIR}/examples/lua/?.so")
 set_tests_properties(lua-masterslave-bypass            PROPERTIES ENVIRONMENT "LUA_CPATH=${CMAKE_BINARY_DIR}/examples/lua/?.so")
index c43b79a..95636b9 100644 (file)
@@ -322,6 +322,8 @@ set(LUA_SRC
        src/bindings/lua/simgrid_lua.c
        src/bindings/lua/lua_stub_generator.c
        src/bindings/lua/lua_console.c
+        src/bindings/lua/lua_utils.c
+        src/bindings/lua/lua_state_cloner.c
 )
 
 set(TRACING_SRC
index 42dcb7f..4e9ad79 100644 (file)
@@ -11,7 +11,7 @@ Ideally, its usage would be:
 simsplay platform_file.{xml|lua} deployment_file.{xml|lua} splay_script.lua [simgrid_options...]
 
 I'm not sure about the format of the platform and deployment files yet.
-We could accept both XML and Lua files, since their is a great Lua API to
+We could accept both XML and Lua files, since there is a great Lua API to
 describe platforms, or only the XML ones.
 
 The simsplay executable (which is compiled C) would initialize SimGrid, create
index 51cd58c..358c36f 100644 (file)
@@ -2,10 +2,10 @@ require("sim_splay")
 
 function SPLAYschool()
   log:print("My ip is: "..job.me.ip())
-  for i = 1000,10000 do
+  for i = 1,200 do
     log:print(i)
   end
-
+--[[
   events.sleep(5)
 
   if job.me.ip() == job.nodes[1].ip then
@@ -13,6 +13,7 @@ function SPLAYschool()
   end
   events.sleep(5)
   os.exit()
+  --]]
 end
 
 function call_me(from)
@@ -21,4 +22,5 @@ end
 
 events.thread("SPLAYschool")
 start.loop()
+log:print("Simulation finished")
 
index 9c257c4..9ecddc6 100644 (file)
@@ -6,6 +6,7 @@ function Slave(...)
 local my_mailbox="slave "..arg[1]
 simgrid.info("Hello from lua, I'm a poor slave with mbox: "..my_mailbox)
 
+
 while true do
 
   local tk = simgrid.Task.recv(my_mailbox);
diff --git a/examples/lua/state_cloner/deployment_duplicated_globals.xml b/examples/lua/state_cloner/deployment_duplicated_globals.xml
new file mode 100644 (file)
index 0000000..aca6202
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version='1.0'?>
+<!DOCTYPE platform SYSTEM "http://simgrid.gforge.inria.fr/simgrid.dtd">
+<platform version="3">
+  <process host="Tremblay" function="set_global_string">
+    <argument value="Calling set_global_string() from Tremblay"/>
+  </process>
+  <process host="Bourassa" function="replace">
+    <argument value="Calling replace() from Bourassa"/>
+  </process>
+  <process host="Jupiter" function="please_dont_replace_me">
+    <argument value="Calling please_dont_replace_me() from Jupiter"/>
+  </process>
+</platform>
diff --git a/examples/lua/state_cloner/duplicated_globals.lua b/examples/lua/state_cloner/duplicated_globals.lua
new file mode 100644 (file)
index 0000000..516cc77
--- /dev/null
@@ -0,0 +1,44 @@
+-- This code creates 3 simgrid processes and verifies that the global values
+-- in each Lua world are correctly cloned from maestro and become different
+
+require("simgrid")
+
+global_string = "A global string set by maestro"
+
+-- Assigns to the global string the first argument and prints it
+function set_global_string(...)
+  
+  global_string = arg[1]
+  simgrid.info("Changing the global string")
+  print_global()
+end
+
+-- Replaces the function please_dont_change_me() by set_global_string()
+-- and calls it
+function replace(...)
+
+  simgrid.info("Overwriting function please_dont_replace_me()")
+  please_dont_replace_me = set_global_string
+  please_dont_replace_me(...)
+end
+
+-- Shows a hello message and prints the global string
+function please_dont_replace_me(...)
+
+  simgrid.info("Hello from please_dont_replace_me(). I'm lucky, I still exist in this state.")
+  print_global()
+end
+
+-- Prints the value of global_string
+function print_global()
+
+  simgrid.info("Global string is '"..global_string.."'")
+end
+
+print_global()
+
+simgrid.platform("../../msg/small_platform.xml")
+simgrid.application("deployment_duplicated_globals.xml")
+simgrid.run()
+simgrid.clean()
+
diff --git a/examples/lua/state_cloner/duplicated_globals.tesh b/examples/lua/state_cloner/duplicated_globals.tesh
new file mode 100644 (file)
index 0000000..c8029d7
--- /dev/null
@@ -0,0 +1,11 @@
+# Checks that global values are correctly duplicated in Lua processes
+
+$ lua duplicated_globals.lua
+> [0.000000] [lua/INFO] Global string is 'A global string set by maestro'
+> [Tremblay:set_global_string:(1) 0.000000] [lua/INFO] Changing the global string
+> [Tremblay:set_global_string:(1) 0.000000] [lua/INFO] Global string is 'Calling set_global_string() from Tremblay'
+> [Bourassa:replace:(2) 0.000000] [lua/INFO] Overwriting function please_dont_replace_me()
+> [Bourassa:replace:(2) 0.000000] [lua/INFO] Changing the global string
+> [Bourassa:replace:(2) 0.000000] [lua/INFO] Global string is 'Calling replace() from Bourassa'
+> [Jupiter:please_dont_replace_me:(3) 0.000000] [lua/INFO] Hello from please_dont_replace_me(). I'm lucky, I still exist in this state.
+> [Jupiter:please_dont_replace_me:(3) 0.000000] [lua/INFO] Global string is 'A global string set by maestro'
diff --git a/src/bindings/lua/lua_state_cloner.c b/src/bindings/lua/lua_state_cloner.c
new file mode 100644 (file)
index 0000000..c5abebb
--- /dev/null
@@ -0,0 +1,624 @@
+/* Copyright (c) 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. */
+
+/* SimGrid Lua state management                                             */
+
+#include "lua_state_cloner.h"
+#include "lua_utils.h"
+#include "xbt.h"
+#include "xbt/log.h"
+#include <lauxlib.h>
+#include <lualib.h>
+
+XBT_LOG_NEW_DEFAULT_SUBCATEGORY(lua_state_cloner, bindings, "Lua state management");
+
+static void sglua_add_maestro_table(lua_State* L, int index, void* maestro_table_ptr);
+static void* sglua_get_maestro_table_ptr(lua_State* L, int index);
+static void sglua_get_table_by_ptr(lua_State* L, void* table_ptr);
+static int l_get_from_maestro(lua_State* L);
+
+static void sglua_copy_nil(lua_State* src, lua_State* dst);
+static void sglua_copy_number(lua_State* src, lua_State* dst);
+static void sglua_copy_boolean(lua_State* src, lua_State* dst);
+static void sglua_copy_string(lua_State* src, lua_State* dst);
+static void sglua_copy_table(lua_State* src, lua_State* dst);
+static void sglua_copy_function(lua_State* src, lua_State* dst);
+static void sglua_copy_lightuserdata(lua_State* src, lua_State* dst);
+static void sglua_copy_userdata(lua_State* src, lua_State* dst);
+static void sglua_copy_thread(lua_State* src, lua_State* dst);
+
+/**
+ * @brief Adds a reference to a maestro table to the list of known maestro
+ * tables of a state.
+ *
+ * TODO identify maestro's tables with my own IDs instead of pointers
+ * to Lua internals
+ *
+ * @param L a state (can be maestro itself)
+ * @param index index of the copy of the maestro table in the stack of L
+ * @param maestro_table_ptr pointer to the original table in maestro's world
+ */
+void sglua_add_maestro_table(lua_State* L, int index, void* maestro_table_ptr) {
+
+  /* we will set both [ptr] -> table and [table] -> ptr */
+
+                                  /* ... */
+  lua_pushvalue(L, index);
+                                  /* ... table */
+  lua_pushstring(L, "simgrid.maestro_tables");
+                                  /* ... table "simgrid.maestro_tables" */
+  lua_rawget(L, LUA_REGISTRYINDEX);
+                                  /* ... table maestrotbs */
+  lua_pushvalue(L, -2);
+                                  /* ... table maestrotbs table */
+  lua_pushlightuserdata(L, maestro_table_ptr);
+                                  /* ... table maestrotbs table tableptr */
+  lua_pushvalue(L, -1);
+                                  /* ... table maestrotbs table tableptr tableptr */
+  lua_pushvalue(L, -3);
+                                  /* ... table maestrotbs table tableptr tableptr table */
+  lua_settable(L, -5);
+                                  /* ... table maestrotbs table tableptr */
+  lua_settable(L, -3);
+                                  /* ... table maestrotbs */
+  lua_pop(L, 2);
+                                  /* ... */
+}
+
+/**
+ * @brief For a table in the stack of L, returns a pointer that identifies the
+ * same table in in maestro's world.
+ * @param L a Lua state
+ * @param index index of a table in the stack of L
+ * @return a pointer to maestro's copy of this table, or NULL if this table
+ * did not come from maestro
+ */
+static void* sglua_get_maestro_table_ptr(lua_State* L, int index) {
+
+  void* maestro_table_ptr = NULL;
+                                  /* ... */
+  lua_pushvalue(L, index);
+                                  /* ... table */
+  lua_pushstring(L, "simgrid.maestro_tables");
+                                  /* ... table "simgrid.maestro_tables" */
+  lua_rawget(L, LUA_REGISTRYINDEX);
+                                  /* ... table maestrotbs */
+  lua_pushvalue(L, -2);
+                                  /* ... table maestrotbs table */
+  lua_gettable(L, -2);
+                                  /* ... table maestrotbs tableptr/nil */
+  if (!lua_isnil(L, -1)) {
+                                  /* ... table maestrotbs tableptr */
+    maestro_table_ptr = (void*) lua_topointer(L, -1);
+  }
+
+  lua_pop(L, 3);
+                                  /* ... */
+  return maestro_table_ptr;
+}
+
+/**
+ * @brief Pushes a table knowing a pointer to maestro's copy of this table.
+ *
+ * Pushes nil if L does not know this table in maestro.
+ *
+ * @param L a Lua state
+ * @param maestro_table_ptr pointer that identifies a table in maestro's world
+ */
+static void sglua_get_table_by_ptr(lua_State* L, void* maestro_table_ptr) {
+
+                                  /* ... */
+  lua_pushstring(L, "simgrid.maestro_tables");
+                                  /* ... "simgrid.maestro_tables" */
+  lua_rawget(L, LUA_REGISTRYINDEX);
+                                  /* ... maestrotbs */
+  lua_pushlightuserdata(L, maestro_table_ptr);
+                                  /* ... maestrotbs tableptr */
+  lua_gettable(L, -2);
+                                  /* ... maestrotbs table/nil */
+  lua_remove(L, -2);
+                                  /* ... table/nil */
+}
+
+/**
+ * @brief Pops a value from the stack of a source state and pushes it on the
+ * stack of another state.
+ * If the value is a table, its content is copied recursively.
+ *
+ * This function is similar to lua_xmove() but it allows to move a value
+ * between two different global states.
+ *
+ * @param src the source state (not necessarily maestro)
+ * @param dst the destination state
+ */
+void sglua_move_value(lua_State* src, lua_State *dst) {
+
+  luaL_checkany(src, -1);                  /* check the value to copy */
+
+  int indent = (lua_gettop(dst) - 1) * 6;
+  XBT_DEBUG("%sCopying data %s", sglua_get_spaces(indent), sglua_tostring(src, -1));
+
+  sglua_stack_dump("src before copying a value (should be ... value): ", src);
+  sglua_stack_dump("dst before copying a value (should be ...): ", dst);
+
+  switch (lua_type(src, -1)) {
+
+    case LUA_TNIL:
+      sglua_copy_nil(src, dst);
+      break;
+
+    case LUA_TNUMBER:
+      sglua_copy_number(src, dst);
+      break;
+
+    case LUA_TBOOLEAN:
+      sglua_copy_boolean(src, dst);
+      break;
+
+    case LUA_TSTRING:
+      sglua_copy_string(src, dst);
+      break;
+
+    case LUA_TFUNCTION:
+      sglua_copy_function(src, dst);
+      break;
+
+    case LUA_TTABLE:
+      sglua_copy_table(src, dst);
+      break;
+
+    case LUA_TLIGHTUSERDATA:
+      sglua_copy_lightuserdata(src, dst);
+      break;
+
+    case LUA_TUSERDATA:
+      sglua_copy_userdata(src, dst);
+      break;
+
+    case LUA_TTHREAD:
+      sglua_copy_thread(src, dst);
+      break;
+  }
+
+  /* the value has been copied to dst: remove it from src */
+  lua_pop(src, 1);
+
+  indent -= 2;
+  XBT_DEBUG("%sData copied", sglua_get_spaces(indent));
+
+  sglua_stack_dump("src after copying a value (should be ...): ", src);
+  sglua_stack_dump("dst after copying a value (should be ... value): ", dst);
+}
+
+/**
+ * @brief Copies the nil value on the top of src to the top of dst.
+ * @param src source state
+ * @param dst destination state
+ */
+static void sglua_copy_nil(lua_State* src, lua_State* dst) {
+  lua_pushnil(dst);
+}
+
+/**
+ * @brief Copies the number value on the top of src to the top of dst.
+ * @param src source state
+ * @param dst destination state
+ */
+static void sglua_copy_number(lua_State* src, lua_State* dst) {
+  lua_pushnumber(dst, lua_tonumber(src, -1));
+}
+
+/**
+ * @brief Copies the boolean value on the top of src to the top of dst.
+ * @param src source state
+ * @param dst destination state
+ */
+static void sglua_copy_boolean(lua_State* src, lua_State* dst) {
+  lua_pushboolean(dst, lua_toboolean(src, -1));
+}
+
+/**
+ * @brief Copies the string value on the top of src to the top of dst.
+ * @param src source state
+ * @param dst destination state
+ */
+static void sglua_copy_string(lua_State* src, lua_State* dst) {
+
+  /* no worries about memory: lua_pushstring makes a copy */
+  lua_pushstring(dst, lua_tostring(src, -1));
+}
+
+/**
+ * @brief Copies the table value on the top of src to the top of dst.
+ *
+ * A deep copy of the table is made. If the table has a metatable, the
+ * metatable is also copied.
+ * If the table is already known by the destination state, it is not copied
+ * again.
+ *
+ * @param src source state
+ * @param dst destination state
+ */
+static void sglua_copy_table(lua_State* src, lua_State* dst) {
+
+                                  /* src: ... table
+                                     dst: ... */
+  int indent = lua_gettop(dst) * 6  + 2;
+
+  /* get from maestro the pointer that identifies this table */
+  void* table_ptr = sglua_get_maestro_table_ptr(src, -1);
+  if (table_ptr == NULL) {
+    /* the table didn't come from maestro: nevermind, use the pointer of src */
+    table_ptr = (void*) lua_topointer(src, -1);
+
+    if (!sglua_is_maestro(src)) {
+      XBT_DEBUG("%sMaestro does not know this table",
+          sglua_get_spaces(indent));
+    }
+  }
+
+  if (sglua_is_maestro(src)) {
+    /* register the table in maestro itself */
+    XBT_DEBUG("%sKeeping track of this table in maestro itself",
+        sglua_get_spaces(indent));
+    sglua_add_maestro_table(src, -1, table_ptr);
+    xbt_assert(sglua_get_maestro_table_ptr(src, -1) == table_ptr);
+  }
+
+  /* to avoid infinite recursion, see if this table is already known by dst */
+  sglua_get_table_by_ptr(dst, table_ptr);
+                                  /* dst: ... table/nil */
+  if (!lua_isnil(dst, -1)) {
+    XBT_DEBUG("%sNothing to do: table already known (%p)",
+        sglua_get_spaces(indent), table_ptr);
+                                  /* dst: ... table */
+  }
+  else {
+    XBT_DEBUG("%sFirst visit of this table (%p)", sglua_get_spaces(indent),
+        table_ptr);
+                                  /* dst: ... nil */
+    lua_pop(dst, 1);
+                                  /* dst: ... */
+
+    /* first visit: create the new table in dst */
+    lua_newtable(dst);
+                                  /* dst: ... table */
+
+    /* mark the table as known right now to avoid infinite recursion */
+    sglua_add_maestro_table(dst, -1, table_ptr);
+    /* FIXME: we may have added a table with a non-maestro pointer, is this a
+       problem? */
+    XBT_DEBUG("%sTable marked as known", sglua_get_spaces(indent));
+    xbt_assert(sglua_get_maestro_table_ptr(dst, -1) == table_ptr);
+
+    sglua_stack_dump("dst after marking the table as known (should be ... table): ", dst);
+
+    /* copy the metatable if any */
+    int has_meta_table = lua_getmetatable(src, -1);
+                                  /* src: ... table mt? */
+    if (has_meta_table) {
+      XBT_DEBUG("%sCopying the metatable", sglua_get_spaces(indent));
+                                  /* src: ... table mt */
+      sglua_copy_table(src, dst);
+                                  /* dst: ... table mt */
+      lua_pop(src, 1);
+                                  /* src: ... table */
+      lua_setmetatable(dst, -2);
+                                  /* dst: ... table */
+    }
+    else {
+                                  /* src: ... table */
+      XBT_DEBUG("%sNo metatable", sglua_get_spaces(indent));
+    }
+
+    sglua_stack_dump("src before traversing the table (should be ... table): ", src);
+    sglua_stack_dump("dst before traversing the table (should be ... table): ", dst);
+
+    /* traverse the table of src and copy each element */
+    lua_pushnil(src);
+                                  /* src: ... table nil */
+    while (lua_next(src, -2) != 0) {
+                                  /* src: ... table key value */
+
+      XBT_DEBUG("%sCopying table element %s", sglua_get_spaces(indent),
+          sglua_keyvalue_tostring(src, -2, -1));
+
+      sglua_stack_dump("src before copying table element (should be ... table key value): ", src);
+      sglua_stack_dump("dst before copying table element (should be ... table): ", dst);
+
+      /* copy the key */
+      lua_pushvalue(src, -2);
+                                  /* src: ... table key value key */
+      indent += 2;
+      XBT_DEBUG("%sCopying the key part of the table element",
+          sglua_get_spaces(indent));
+      sglua_move_value(src, dst);
+                                  /* src: ... table key value
+                                     dst: ... table key */
+      XBT_DEBUG("%sCopied the key part of the table element",
+          sglua_get_spaces(indent));
+
+      /* copy the value */
+      XBT_DEBUG("%sCopying the value part of the table element",
+          sglua_get_spaces(indent));
+      sglua_move_value(src, dst);
+                                  /* src: ... table key
+                                     dst: ... table key value */
+      XBT_DEBUG("%sCopied the value part of the table element",
+          sglua_get_spaces(indent));
+      indent -= 2;
+
+      /* set the table element */
+      lua_settable(dst, -3);
+                                  /* dst: ... table */
+
+      /* the key stays on top of src for next iteration */
+      sglua_stack_dump("src before next iteration (should be ... table key): ", src);
+      sglua_stack_dump("dst before next iteration (should be ... table): ", dst);
+
+      XBT_DEBUG("%sTable element copied", sglua_get_spaces(indent));
+    }
+    XBT_DEBUG("%sFinished traversing the table", sglua_get_spaces(indent));
+  }
+}
+
+/**
+ * @brief Copies the function on the top of src to the top of dst.
+ *
+ * It can be a Lua function or a C function.
+ * Copying upvalues is not implemented yet.
+ *
+ * @param src source state
+ * @param dst destination state
+ */
+static void sglua_copy_function(lua_State* src, lua_State* dst) {
+
+  if (lua_iscfunction(src, -1)) {
+    /* it's a C function */
+
+    XBT_DEBUG("It's a C function");
+    sglua_stack_dump("src before copying upvalues: ", src);
+
+    /* get the function pointer */
+    int function_index = lua_gettop(src);
+    lua_CFunction f = lua_tocfunction(src, function_index);
+
+    /* copy the upvalues */
+    int i = 0;
+    const char* upvalue_name = NULL;
+    do {
+      i++;
+      upvalue_name = lua_getupvalue(src, function_index, i);
+
+      if (upvalue_name != NULL) {
+        XBT_DEBUG("Upvalue %s", upvalue_name);
+        sglua_move_value(src, dst);
+      }
+    } while (upvalue_name != NULL);
+
+    sglua_stack_dump("src before copying pointer: ", src);
+
+    /* set the function */
+    lua_pushcclosure(dst, f, i - 1);
+    XBT_DEBUG("Function pointer copied");
+  }
+  else {
+    /* it's a Lua function: dump it from src */
+
+    s_sglua_buffer_t buffer;
+    buffer.capacity = 128; /* an empty function uses 77 bytes */
+    buffer.size = 0;
+    buffer.data = xbt_new(char, buffer.capacity);
+
+    /* copy the binary chunk from src into a buffer */
+    int error = lua_dump(src, sglua_memory_writer, &buffer);
+    xbt_assert(!error, "Failed to dump the function from the source state: error %d",
+        error);
+    XBT_DEBUG("Fonction dumped: %zu bytes", buffer.size);
+
+    /*
+    fwrite(buffer.data, buffer.size, buffer.size, stderr);
+    fprintf(stderr, "\n");
+    */
+
+    /* load the chunk into dst */
+    error = luaL_loadbuffer(dst, buffer.data, buffer.size, "(dumped function)");
+    xbt_assert(!error, "Failed to load the function into the destination state: %s",
+        lua_tostring(dst, -1));
+  }
+}
+
+/**
+ * @brief Copies the light userdata on the top of src to the top of dst.
+ * @param src source state
+ * @param dst destination state
+ */
+static void sglua_copy_lightuserdata(lua_State* src, lua_State* dst) {
+  lua_pushlightuserdata(dst, lua_touserdata(src, -1));
+}
+
+/**
+ * @brief Copies the full userdata on the top of src to the top of dst.
+ *
+ * If the userdata has a metatable, the metatable is also copied.
+ *
+ * @param src source state
+ * @param dst destination state
+ */
+static void sglua_copy_userdata(lua_State* src, lua_State* dst) {
+
+  int indent = lua_gettop(dst) * 6  + 2;
+
+  /* copy the data */
+                                  /* src: ... udata
+                                     dst: ... */
+  size_t size = lua_objlen(src, -1);
+  void* src_block = lua_touserdata(src, -1);
+  void* dst_block = lua_newuserdata(dst, size);
+                                  /* dst: ... udata */
+  memcpy(dst_block, src_block, size);
+
+  /* copy the metatable if any */
+  int has_meta_table = lua_getmetatable(src, -1);
+                                  /* src: ... udata mt? */
+  if (has_meta_table) {
+    XBT_DEBUG("%sCopying metatable of userdata (%p)",
+        sglua_get_spaces(indent), lua_topointer(src, -1));
+                                  /* src: ... udata mt */
+    sglua_copy_table(src, dst);
+                                  /* src: ... udata mt
+                                     dst: ... udata mt */
+    lua_pop(src, 1);
+                                  /* src: ... udata */
+    lua_setmetatable(dst, -2);
+                                  /* dst: ... udata */
+
+    XBT_DEBUG("%sMetatable of userdata copied", sglua_get_spaces(indent));
+  }
+  else {
+    XBT_DEBUG("%sNo metatable for this userdata",
+        sglua_get_spaces(indent));
+                                  /* src: ... udata */
+  }
+}
+
+/**
+ * @brief This operation is not supported (yet?) so it just pushes nil.
+ *
+ * @param src source state
+ * @param dst destination state
+ */
+static void sglua_copy_thread(lua_State* src, lua_State* dst) {
+
+  XBT_WARN("Copying a thread from another state is not implemented (yet?).");
+  lua_pushnil(dst);
+}
+
+/**
+ * @brief Copies a global value or a registry value from the maestro state.
+ *
+ * The state L must have been created by sglua_clone_maestro_state().
+ * This function is meant to be an __index metamethod.
+ * Consequently, it assumes that the stack has two elements:
+ * a table (either the environment or the registry of L) and the string key of
+ * a value that does not exist yet in this table. It copies the corresponding
+ * value from maestro and pushes it on the stack of L.
+ * If the value does not exist in maestro state either, nil is pushed.
+ *
+ * TODO: make this function thread safe. If the simulation runs in parallel,
+ * several simulated processes may trigger this __index metamethod at the same
+ * time and get globals from maestro.
+ *
+ * @param L the current state
+ * @return number of return values pushed (always 1)
+ */
+static int l_get_from_maestro(lua_State *L) {
+
+  /* check the arguments */
+  luaL_checktype(L, 1, LUA_TTABLE);
+  const char* key = luaL_checkstring(L, 2);
+                                  /* L:      table key */
+  XBT_DEBUG("__index of '%s' begins", key);
+
+  /* want a global or a registry value? */
+  int pseudo_index;
+  if (lua_equal(L, 1, LUA_REGISTRYINDEX)) {
+    /* registry */
+    pseudo_index = LUA_REGISTRYINDEX;
+    XBT_DEBUG("Will get the value from the registry of maestro");
+  }
+  else {
+    /* global */
+    pseudo_index = LUA_GLOBALSINDEX;
+    XBT_DEBUG("Will get the value from the globals of maestro");
+  }
+
+  /* get the father */
+  lua_State* maestro = sglua_get_maestro();
+
+                                  /* L:      table key */
+
+  /* get the value from maestro */
+  lua_getfield(maestro, pseudo_index, key);
+                                  /* maestro: ... value */
+
+  /* push the value onto the stack of L */
+  sglua_move_value(maestro, L);
+                                  /* maestro: ...
+                                     L:      table key value */
+
+  /* prepare the return value of __index */
+  lua_pushvalue(L, -1);
+                                  /* L:      table key value value */
+  lua_insert(L, 1);
+                                  /* L:      value table key value */
+
+  /* save the copied value in the table for subsequent accesses */
+  lua_settable(L, -3);
+                                  /* L:      value table */
+  lua_settop(L, 1);
+                                  /* L:      value */
+
+  XBT_DEBUG("__index of '%s' returns %s", key, sglua_tostring(L, -1));
+
+  return 1;
+}
+
+/**
+ * @brief Creates a new Lua state and get its environment from the maestro
+ * state.
+ *
+ * The state created is independent from maestro and has its own copies of
+ * global and registry values.
+ * However, the global and registry values are not copied right now from
+ * the original state; they are copied only the first time they are accessed.
+ * This behavior saves time and memory, and is okay for Simgrid's needs.
+ *
+ * TODO: if the simulation runs in parallel, copy everything right now?
+ *
+ * @return the state created
+ */
+lua_State* sglua_clone_maestro(void) {
+
+  /* create the new state */
+  lua_State *L = luaL_newstate();
+
+  /* set its environment and its registry:
+   * - create a table newenv
+   * - create a metatable mt
+   * - set mt.__index = a function that copies the global from the father state
+   * - set mt as the metatable of the registry
+   * - set mt as the metatable of newenv
+   * - set newenv as the environment of the new state
+   */
+  lua_pushthread(L);                        /* thread */
+  lua_newtable(L);                          /* thread newenv */
+  lua_newtable(L);                          /* thread newenv mt */
+  lua_pushvalue(L, LUA_REGISTRYINDEX);      /* thread newenv mt reg */
+  lua_pushcfunction(L, l_get_from_maestro); /* thread newenv mt reg f */
+  lua_setfield(L, -3, "__index");           /* thread newenv mt reg */
+  lua_pushvalue(L, -2);                     /* thread newenv mt reg mt */
+  lua_setmetatable(L, -2);                  /* thread newenv mt reg */
+  lua_pop(L, 1);                            /* thread newenv mt */
+  lua_setmetatable(L, -2);                  /* thread newenv */
+  lua_setfenv(L, -2);                       /* thread */
+  lua_pop(L, 1);                            /* -- */
+
+  /* create the table of known tables from maestro */
+  lua_pushstring(L, "simgrid.maestro_tables");
+                                            /* "simgrid.maestro_tables" */
+  lua_newtable(L);                          /* "simgrid.maestro_tables" maestrotbs*/
+  lua_rawset(L, LUA_REGISTRYINDEX);
+                                            /* -- */
+
+  /* opening the standard libs is not necessary as they are
+   * inherited like any global values */
+  /* luaL_openlibs(L); */
+
+  XBT_DEBUG("New state created");
+
+  return L;
+}
diff --git a/src/bindings/lua/lua_state_cloner.h b/src/bindings/lua/lua_state_cloner.h
new file mode 100644 (file)
index 0000000..194cbb1
--- /dev/null
@@ -0,0 +1,13 @@
+/* Copyright (c) 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. */
+
+/* SimGrid Lua state management                                             */
+#include <lua.h>
+
+int sglua_is_maestro(lua_State* L);
+lua_State* sglua_get_maestro(void);
+lua_State* sglua_clone_maestro(void);
+void sglua_move_value(lua_State* src, lua_State* dst);
diff --git a/src/bindings/lua/lua_utils.c b/src/bindings/lua/lua_utils.c
new file mode 100644 (file)
index 0000000..84b8ca2
--- /dev/null
@@ -0,0 +1,161 @@
+/* Copyright (c) 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. */
+
+/* SimGrid Lua helper functions                                             */
+
+#include "lua_utils.h"
+#include "xbt.h"
+#include "xbt/log.h"
+
+XBT_LOG_NEW_DEFAULT_SUBCATEGORY(lua_utils, bindings, "Lua helper functions");
+
+/**
+ * @brief Returns a string representation of a value in the Lua stack.
+ *
+ * This function is for debugging purposes.
+ * It always returns the same pointer.
+ *
+ * @param L the Lua state
+ * @param index index in the stack
+ * @return a string representation of the value at this index
+ */
+const char* sglua_tostring(lua_State* L, int index) {
+
+  static char buff[64];
+
+  switch (lua_type(L, index)) {
+
+    case LUA_TNIL:
+      sprintf(buff, "nil");
+      break;
+
+    case LUA_TNUMBER:
+      sprintf(buff, "%.3f", lua_tonumber(L, index));
+      break;
+
+    case LUA_TBOOLEAN:
+      sprintf(buff, "%s", lua_toboolean(L, index) ? "true" : "false");
+      break;
+
+    case LUA_TSTRING:
+      snprintf(buff, 63, "'%s'", lua_tostring(L, index));
+      break;
+
+    case LUA_TFUNCTION:
+      if (lua_iscfunction(L, index)) {
+        sprintf(buff, "C-function");
+      }
+      else {
+        sprintf(buff, "function");
+      }
+      break;
+
+    case LUA_TTABLE:
+      sprintf(buff, "table(%p)", lua_topointer(L, index));
+      break;
+
+    case LUA_TLIGHTUSERDATA:
+    case LUA_TUSERDATA:
+      sprintf(buff, "userdata(%p)", lua_touserdata(L, index));
+      break;
+
+    case LUA_TTHREAD:
+      sprintf(buff, "thread");
+      break;
+  }
+  return buff;
+}
+
+/**
+ * @brief Returns a string representation of a key-value pair.
+ *
+ * This function is for debugging purposes.
+ * It always returns the same pointer.
+ *
+ * @param L the Lua state
+ * @param key_index index of the key
+ * @param value_index index of the value
+ * @return a string representation of the key-value pair
+ */
+const char* sglua_keyvalue_tostring(lua_State* L, int key_index, int value_index) {
+
+  static char buff[64];
+  /* value_tostring also always returns the same pointer */
+  int len = snprintf(buff, 63, "[%s] -> ", sglua_tostring(L, key_index));
+  snprintf(buff + len, 63 - len, "%s", sglua_tostring(L, value_index));
+  return buff;
+}
+
+/**
+ * @brief Returns a string composed of the specified number of spaces.
+ *
+ * This function can be used to indent strings for debugging purposes.
+ * It always returns the same pointer.
+ *
+ * @param length length of the string
+ * @return a string of this length with only spaces
+ */
+const char* sglua_get_spaces(int length) {
+
+  static char spaces[128];
+
+  xbt_assert(length < 128);
+  memset(spaces, ' ', length);
+  spaces[length] = '\0';
+  return spaces;
+}
+
+/**
+ * @brief Dumps the Lua stack if debug logs are enabled.
+ * @param msg a message to print
+ * @param L a Lua state
+ */
+void sglua_stack_dump(const char* msg, lua_State* L)
+{
+  if (XBT_LOG_ISENABLED(lua_utils, xbt_log_priority_debug)) {
+    char buff[2048];
+    char* p = buff;
+    int i;
+    int top = lua_gettop(L);
+
+    //if (1) return;
+
+    fflush(stdout);
+
+    p[0] = '\0';
+    for (i = 1; i <= top; i++) {  /* repeat for each level */
+
+      p += sprintf(p, "%s", sglua_tostring(L, i));
+      p += sprintf(p, " ");       /* put a separator */
+    }
+    XBT_DEBUG("%s%s", msg, buff);
+  }
+}
+
+/**
+ * @brief Writes the specified data into a memory buffer.
+ *
+ * This function is a valid lua_Writer that writes into a memory buffer passed
+ * as userdata.
+ *
+ * @param L a lua state
+ * @param source some data
+ * @param sz number of bytes of data
+ * @param user_data the memory buffer to write
+ */
+int sglua_memory_writer(lua_State* L, const void* source, size_t size,
+    void* userdata) {
+
+  sglua_buffer_t buffer = (sglua_buffer_t) userdata;
+  while (buffer->capacity < buffer->size + size) {
+    buffer->capacity *= 2;
+    buffer->data = xbt_realloc(buffer->data, buffer->capacity);
+  }
+  memcpy(buffer->data + buffer->size, source, size);
+  buffer->size += size;
+
+  return 0;
+}
diff --git a/src/bindings/lua/lua_utils.h b/src/bindings/lua/lua_utils.h
new file mode 100644 (file)
index 0000000..49f8e4d
--- /dev/null
@@ -0,0 +1,26 @@
+/* Copyright (c) 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. */
+
+/* SimGrid Lua helper functions                                             */
+
+#include <lua.h>
+
+/**
+ * @brief A chunk of memory.
+ *
+ * This structure is used as the userdata parameter of lua_Writer.
+ */
+typedef struct s_sglua_buffer {
+  char* data;
+  size_t size;
+  size_t capacity;
+} s_sglua_buffer_t, *sglua_buffer_t;
+
+const char* sglua_tostring(lua_State* L, int index);
+const char* sglua_keyvalue_tostring(lua_State* L, int key_index, int value_index);
+void sglua_stack_dump(const char *msg, lua_State* L);
+const char* sglua_get_spaces(int length);
+int sglua_memory_writer(lua_State* L, const void* source, size_t size, void* userdata);
index 2002bea..09dd183 100644 (file)
@@ -1,11 +1,14 @@
-/* SimGrid Lua bindings                                                     */
-
 /* Copyright (c) 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. */
+
+/* SimGrid Lua bindings                                                     */
+
 #include "simgrid_lua.h"
+#include "lua_state_cloner.h"
+#include "lua_utils.h"
 
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(lua, bindings, "Lua Bindings");
 
@@ -19,62 +22,27 @@ static lua_State *lua_maestro_state;
 #define AS_MODULE_NAME "simgrid.AS"
 #define TRACE_MODULE_NAME "simgrid.Trace"
 
-static void stack_dump(const char *msg, lua_State *L);
-static m_task_t check_task(lua_State *L, int index);
 static void register_c_functions(lua_State *L);
 
-/* ********************************************************************************* */
-/*                            helper functions                                       */
-/* ********************************************************************************* */
-
-/**
- * @brief Dumps the Lua stack
- * @param msg a message to print
- * @param L a Lua state
- */
-static void stack_dump(const char *msg, lua_State *L)
-{
-  char buff[2048];
-  char *p = buff;
-  int i;
-  int top = lua_gettop(L);
-
-  fflush(stdout);
-  p += sprintf(p, "STACK(top=%d): ", top);
-
-  for (i = 1; i <= top; i++) {  /* repeat for each level */
-    int t = lua_type(L, i);
-    switch (t) {
+static void *my_checkudata (lua_State *L, int ud, const char *tname) {
 
-    case LUA_TSTRING:          /* strings */
-      p += sprintf(p, "`%s'", lua_tostring(L, i));
-      break;
+  XBT_DEBUG("Checking the task: ud = %d", ud);
+  sglua_stack_dump("my_checkudata: ", L);
+  void *p = lua_touserdata(L, ud);
+  lua_getfield(L, LUA_REGISTRYINDEX, tname);
+  const void* correct_mt = lua_topointer(L, -1);
 
-    case LUA_TBOOLEAN:         /* booleans */
-      p += sprintf(p, lua_toboolean(L, i) ? "true" : "false");
-      break;
+  int has_mt = lua_getmetatable(L, ud);
+  XBT_DEBUG("Checking the task: has metatable ? %d", has_mt);
+  const void* actual_mt = NULL;
+  if (has_mt) { actual_mt = lua_topointer(L, -1); lua_pop(L, 1); }
+  XBT_DEBUG("Checking the task's metatable: expected %p, found %p", correct_mt, actual_mt);
+  sglua_stack_dump("my_checkudata: ", L);
 
-    case LUA_TNUMBER:          /* numbers */
-      p += sprintf(p, "%g", lua_tonumber(L, i));
-      break;
-
-    case LUA_TTABLE:
-      p += sprintf(p, "Table");
-      break;
-
-    default:                   /* other values */
-      p += sprintf(p, "???");
-/*      if ((ptr = luaL_checkudata(L,i,TASK_MODULE_NAME))) {
-        p+=sprintf(p,"task");
-      } else {
-        p+=printf(p,"%s", lua_typename(L, t));
-      }*/
-      break;
-
-    }
-    p += sprintf(p, "  ");      /* put a separator */
-  }
-  XBT_INFO("%s%s", msg, buff);
+  if (p == NULL || !lua_getmetatable(L, ud) || !lua_rawequal(L, -1, -2))
+    luaL_typerror(L, ud, tname);
+  lua_pop(L, 2);
+  return p;
 }
 
 /**
@@ -87,9 +55,12 @@ static void stack_dump(const char *msg, lua_State *L)
 static m_task_t checkTask(lua_State * L, int index)
 {
   m_task_t *pi, tk;
+  XBT_DEBUG("Lua task: %s", sglua_tostring(L, index));
   luaL_checktype(L, index, LUA_TTABLE);
   lua_getfield(L, index, "__simgrid_task");
-  pi = (m_task_t *) luaL_checkudata(L, -1, TASK_MODULE_NAME);
+
+  pi = (m_task_t *) luaL_checkudata(L, lua_gettop(L), TASK_MODULE_NAME);
+
   if (pi == NULL)
     luaL_typerror(L, index, TASK_MODULE_NAME);
   tk = *pi;
@@ -179,7 +150,7 @@ static int Task_destroy(lua_State * L)
 
 static int Task_send(lua_State * L)
 {
-  //stackDump("send ",L);
+  //stack_dump("send ", L);
   m_task_t tk = checkTask(L, 1);
   const char *mailbox = luaL_checkstring(L, 2);
   lua_pop(L, 1);                // remove the string so that the task is on top of it
@@ -216,7 +187,7 @@ static int Task_recv_with_timeout(lua_State *L)
 
   if (res == MSG_OK) {
     lua_State *sender_stack = MSG_task_get_data(tk);
-    lua_xmove(sender_stack, L, 1);        // copy the data directly from sender's stack
+    sglua_move_value(sender_stack, L);        // copy the data directly from sender's stack
     MSG_task_set_data(tk, NULL);
   }
   else {
@@ -285,7 +256,7 @@ static m_host_t checkHost(lua_State * L, int index)
   m_host_t *pi, ht;
   luaL_checktype(L, index, LUA_TTABLE);
   lua_getfield(L, index, "__simgrid_host");
-  pi = (m_host_t *) luaL_checkudata(L, -1, HOST_MODULE_NAME);
+  pi = (m_host_t *) luaL_checkudata(L, lua_gettop(L), HOST_MODULE_NAME);
   if (pi == NULL)
     luaL_typerror(L, index, HOST_MODULE_NAME);
   ht = *pi;
@@ -343,13 +314,19 @@ static int Host_at(lua_State * L)
 
 static int Host_self(lua_State * L)
 {
+                                  /* -- */
   m_host_t host = MSG_host_self();
   lua_newtable(L);
-  m_host_t *lua_host =(m_host_t *)lua_newuserdata(L,sizeof(m_host_t));
+                                  /* table */
+  m_host_t* lua_host = (m_host_t*) lua_newuserdata(L, sizeof(m_host_t));
+                                  /* table ud */
   *lua_host = host;
   luaL_getmetatable(L, HOST_MODULE_NAME);
+                                  /* table ud mt */
   lua_setmetatable(L, -2);
+                                  /* table ud */
   lua_setfield(L, -2, "__simgrid_host");
+                                  /* table */
   return 1;
 }
 
@@ -555,11 +532,10 @@ static int run_lua_code(int argc, char **argv)
 {
   XBT_DEBUG("Run lua code %s", argv[0]);
 
-  lua_State *L = lua_newthread(lua_maestro_state);
-  int ref = luaL_ref(lua_maestro_state, LUA_REGISTRYINDEX);     /* protect the thread from being garbage collected */
+  lua_State *L = sglua_clone_maestro();
   int res = 1;
 
-  /* start the co-routine */
+  /* start the function */
   lua_getglobal(L, argv[0]);
   xbt_assert(lua_isfunction(L, -1),
               "The lua function %s does not seem to exist", argv[0]);
@@ -581,8 +557,6 @@ static int run_lua_code(int argc, char **argv)
     lua_pop(L, 1);              /* pop returned value */
   }
 
-  /* cleanups */
-  luaL_unref(lua_maestro_state, LUA_REGISTRYINDEX, ref);
   XBT_DEBUG("Execution of Lua code %s is over", (argv ? argv[0] : "(null)"));
 
   return res;
@@ -772,11 +746,32 @@ int luaopen_simgrid(lua_State *L)
   /* Keep the context mechanism informed of our lua world today */
   lua_maestro_state = L;
 
+  /* initialize access to my tables by children Lua states */
+  lua_newtable(L);
+  lua_setfield(L, LUA_REGISTRYINDEX, "simgrid.maestro_tables");
+
   register_c_functions(L);
 
   return 1;
 }
 
+/**
+ * @brief Returns whether a Lua state is the maestro state.
+ * @param L a Lua state
+ * @return true if this is maestro
+ */
+int sglua_is_maestro(lua_State* L) {
+  return L == lua_maestro_state;
+}
+
+/**
+ * @brief Returns the maestro state.
+ * @return true the maestro Lua state
+ */
+lua_State* sglua_get_maestro(void) {
+  return lua_maestro_state;
+}
+
 /**
  * Makes the appropriate Simgrid functions available to the Lua world.
  * @param L a Lua world
index 9177495..4a04ae8 100644 (file)
@@ -22,7 +22,6 @@
 #include "portable.h"           /* Needed for the time of the SIMIX convertion */
 #include <stdarg.h>
 
-
 extern xbt_dict_t process_function_set;
 extern xbt_dynar_t process_list;
 extern xbt_dict_t machine_set;