I've been digging into the Lua on Parrot source code to try to find out why 
there are GC problems (partly to fix them, partly so I can profile Lua 
programs and speed up the system).  I ran into something suspicious in the 
LuaTable PMC.

It uses a custom function to mark all contained keys and values as live, if 
they're GC-able PObjs:

static void lua_mark_table(PARROT_INTERP, Hash *t, STRING *mode)
{
    Node *v = t->node;
    int i;
    int mark_key = 1;
    int mark_val = 1;
    if (mode) {
        mark_key = string_str_index(interp, mode, const_string(interp, "k"), 
0) < 0;
        mark_val = string_str_index(interp, mode, const_string(interp, "v"), 
0) < 0;
    }
    for (i = 0; i < t->size; i++) {
        if (v[i].val != NULL) {
            if (mark_key)
                pobject_lives(interp, (PObj *)v[i].key);
            if (mark_val)
                pobject_lives(interp, (PObj *)v[i].val);
        }
    }
}

However, the node member of a Hash isn't an array:

typedef struct Node {
    PMC *key;
    PMC *val;
    struct Node *next;  /* for chaining */
} Node;

typedef struct Hash {
    Node *node;
    int size;
    Node *firstfree;  /* this position is free; all positions after it are 
full */
    PMC *container;
} Hash;

Now it may indeed be that the linked list happens to be the right size that 
this pointer math might work... but I'm not convinced.  Instead, I expect the 
correct approach to look something more like:

static void lua_mark_table(PARROT_INTERP, Hash *t, STRING *mode)
{
    Node *v      = t->node;
    int mark_key = 1;
    int mark_val = 1;
    if (mode) {
        mark_key = string_str_index(interp, mode, const_string(interp, "k"), 
0) < 0;
        mark_val = string_str_index(interp, mode, const_string(interp, "v"), 
0) < 0;
    }
    
    while (v) { 
        if (v->val) {
            if (mark_key)
                pobject_lives(interp, (PObj *)v->key);
            if (mark_val)
                pobject_lives(interp, (PObj *)v->val);
        }
        v = v->next;
    }
}

Of course, that makes segfaults elsewhere, which may mean that it's wrong, or 
it may mean that there's more hash cleanup necessary in that PMC.  At least 
when I run individual Lua files without --no-gc, they no longer segfault in 
DOD after saying "Method 'setfenv' not found".

Patch (for experimentation, not commit) attached.

-- c
=== pmc/luatable.pmc
==================================================================
--- pmc/luatable.pmc	(revision 6305)
+++ pmc/luatable.pmc	(local)
@@ -270,21 +270,22 @@
 
 static void lua_mark_table(PARROT_INTERP, Hash *t, STRING *mode)
 {
-    Node *v = t->node;
-    int i;
+    Node *v      = t->node;
     int mark_key = 1;
     int mark_val = 1;
     if (mode) {
         mark_key = string_str_index(interp, mode, const_string(interp, "k"), 0) < 0;
         mark_val = string_str_index(interp, mode, const_string(interp, "v"), 0) < 0;
     }
-    for (i = 0; i < t->size; i++) {
-        if (v[i].val != NULL) {
+
+    while (v) {
+        if (v->val) {
             if (mark_key)
-                pobject_lives(interp, (PObj *)v[i].key);
+                pobject_lives(interp, (PObj *)v->key);
             if (mark_val)
-                pobject_lives(interp, (PObj *)v[i].val);
+                pobject_lives(interp, (PObj *)v->val);
         }
+        v = v->next;
     }
 }
 

Reply via email to