Git Product home page Git Product logo

Comments (14)

kunitoki avatar kunitoki commented on June 29, 2024 1

When registering the constructor, i was passing the callable to a function "unpacker" (what we use for normal functions), which by default is pushing the result if the call to the stack (in this case the result of the constructor is a T*), but the constructor was already pushing the newly created pointer on the stack via an owning userdata, that was then lost. The fix was to use a special unpacker that is just calling the constructor and not push again a raw pointer on the stack.

from luabridge3.

rpatters1 avatar rpatters1 commented on June 29, 2024

Another issue I'm seeing is that when my Lua code calls the constructor with

local foo = foobar.foo()

Inside the proxy constructor this happens:

int numArgs = lua_gettop(L);

I get numArgs == 2. Based on the docs, I was expecting numArgs == 1 (since it says the arguments start at stack location 2 if they are there). What is the correct method to get the number of arguments passed to a proxy constructor?

EDIT:

Lemme guess: the 1st argument is the void ptr, the last argument is the Lua state, and the arguments to my function are in the middle.

from luabridge3.

rpatters1 avatar rpatters1 commented on June 29, 2024

I'm closing this issue, because whatever is going on is more complicated than I thought. Will reopen a new issue if appropriate to do so.

from luabridge3.

Riflio avatar Riflio commented on June 29, 2024

Hi! I have a same problem, hove you solve it?

from luabridge3.

rpatters1 avatar rpatters1 commented on June 29, 2024

No, I'm trying to come up with a test case that doesn't require a lot of dependencies in my code. So far no luck. But I have a situation that is very consistent:

  • if it has a traditional constructor the code works
  • if it has a proxy constructor the object gets garbage collected in mid-coroutine and the program crashes.

from luabridge3.

rpatters1 avatar rpatters1 commented on June 29, 2024

If you compile this test case so that the traditional constructor is selected (perhaps by upping the LUABRIDGE_MAJOR_VERSION comparison value to 4) then the script runs without errors in both main line and coroutine. If you change the comparison value to 3 so that the proxy constructor is used, the do_foo function usually fails to get past printing 30000 before the system crashes.

The problem appears to be that something about the proxy constructor makes Lua think foo can be garbage-collected, so the foo variable is getting destroyed out from under the loop.

Here is a simplified class layout for my test case. (DoublyLinkedList drafted by ChatGPT.):

class Node {
public:
    int data;
    Node* next;
    Node* prev;

    Node(int value) {
        data = value;
        next = nullptr;
        prev = nullptr;
    }
   
   int val() { return data; }
   Node* next_node() { return next; }
};

class DoublyLinkedList {
public:
    DoublyLinkedList(int numToAdd = 0) {
        head = nullptr;
        tail = nullptr;
       for (int value = 1; value<=numToAdd; value++) {
            addBack(value);
        }
    }
   ~DoublyLinkedList()
   {
      while(head) removeBack();
   }

    void addBack(int value) {
        Node* newNode = new Node(value);
        if (tail == nullptr) {
            head = newNode;
            tail = newNode;
        }
        else {
            newNode->prev = tail;
            tail->next = newNode;
            tail = newNode;
        }
    }
    void removeBack() {
        if (tail == nullptr) {
            std::cout << "List is empty\n";
            return;
        }
        Node* temp = tail;
        tail = tail->prev;
        if (tail != nullptr) {
            tail->next = nullptr;
        }
        else {
            head = nullptr;
        }
        delete temp;
    }
   
   Node* first() { return head; }
   
private:
    Node* head;
    Node* tail;
};

class foo
{
   DoublyLinkedList list;
   
public:
   foo(int size = 5000) : list(size) {}
   Node* first() { return list.first(); }
   Node* next(Node* curr) { return curr->next_node(); }
};

class bar
{
   foo* foo_;
public:
   bar(foo* f) : foo_(f) {}
   Node* first() { return foo_->first(); }
   Node* next(Node* curr) { return foo_->next(curr); }
};

Here is the LB3 registration:

   luabridge::getGlobalNamespace(L)
      .beginNamespace("foobar")
         .beginClass<Node>("Node")
            .addFunction("val", &Node::val)
         .endClass()
         .beginClass<foo>("foo")
#if LUABRIDGE_MAJOR_VERSION >= 3
            .addConstructor([](void*p, lua_State* L)
               {
                  const int numArgs = lua_gettop(L);
                  const int size = (numArgs > 2) ? luabridge::Stack<int>::get(L, 2).value() : 5000;
                  return new(p) foo(size);
               })
#else
            .addConstructor<void(*)(size_t)>()
#endif
            .addFunction("first", &foo::first)
            .addFunction("next", &foo::next)
         .endClass()
         .beginClass<bar>("bar")
            .addConstructor<void(*)(foo *)>()
            .addFunction("first", &bar::first)
            .addFunction("next", &bar::next)
         .endClass()
      .endNamespace();

Here is the lua code to run it:

local function do_foo(header)
    print ("********** "..header.." **********")
    local foo = foobar.foo(500000)
    local bar = foobar.bar(foo)
    local next = bar:first()
    while next do
        local v = next:val()
        if v % 10000 == 0 then
            print("v: "..tostring(v))
            if header == "coroutine" then
                --coroutine.yield()
            end
            --collectgarbage("collect")
        end
        next = bar:next(next)
    end
    print ("finished "..header)
end

do_foo("main function")

local my_coroutine = coroutine.create(function()
        do_foo("coroutine")
    end)

while true do
    local success, errmsg = coroutine.resume(my_coroutine)
    if not success then
        error(errmsg)
    end
    if coroutine.status(my_coroutine) == "dead" then
        break
    end
end

from luabridge3.

kunitoki avatar kunitoki commented on June 29, 2024

Thanks for the repro case, will give it a spin.

from luabridge3.

kunitoki avatar kunitoki commented on June 29, 2024

After checking, it seems it has nothing to do with coroutines.

from luabridge3.

Riflio avatar Riflio commented on June 29, 2024

Thanks.
I'm guessing it might have something to do with ownership,
because when i create a class via
.addStaticFunction("new", [](int i){ return new Bar(i); })
the problem does not occur.

from luabridge3.

kunitoki avatar kunitoki commented on June 29, 2024

It's strange, because when i invoke the 2 constructors, they do exactly the same, but one is passing the void* to an internal lambda, the other exposes the pointer to the registered lambda. It's weird that this is messing with the lifetime (the object has the same semantic in luabridge, regardless of the constructors).

from luabridge3.

rpatters1 avatar rpatters1 commented on June 29, 2024

I have only tried this with Lua 5.4. Have you tried it with earlier versions? I know there was a rewrite of gc in 5.4. May this is a Lua bug.

from luabridge3.

rpatters1 avatar rpatters1 commented on June 29, 2024

I can save you the trouble of trying a different Lua version. It still fails in Lua 5.2.

from luabridge3.

rpatters1 avatar rpatters1 commented on June 29, 2024

For context, two possibly important features of the test case are:

  • The class that is being gc'd (foo) is only referenced once at the top of the function. It is then passed to a C class and goes out of the knowledge of Lua. The C classes dish up its contents from there on out.
  • The function churns a ton of Lua local variables, prompting gc in mid-stream.

It wasn't until I built both of these features into the test case that I could get it to consistently fail.

I also had to jack up the number of reps. It didn't fail at 5000 at all, and it only failed sporadically at 50000. But 500000 did the trick.

from luabridge3.

rpatters1 avatar rpatters1 commented on June 29, 2024

I'm thrilled you found a fix. I would be interested in a summary of what the problem was, if you've got the time.

from luabridge3.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.