Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

memory smashing with container wrapping and coroutine #1400

Open
otristan opened this issue Sep 9, 2022 · 0 comments
Open

memory smashing with container wrapping and coroutine #1400

otristan opened this issue Sep 9, 2022 · 0 comments

Comments

@otristan
Copy link

otristan commented Sep 9, 2022

Hi,

The following code leads to some memory smashing from time to time.
It seems related to a custom wrapping of const std::vectorstd::shared_ptr

Using lua 5.2 on Xcode Version 12.2 (12B45b)
Probably similar to
#1315

#define SOL_ALL_SAFETIES_ON 1
#define SOL_NO_CHECK_NUMBER_PRECISION 1
#define SOL_EXCEPTIONS_SAFE_PROPAGATION 1
#define SOL_USING_CXX_LUA 1

#include "sol/sol.hpp"
#include <vector>
#include <memory>

struct A
{
public:
  A(int value_) : value(value_) { }
  int value;
  
  int getValue() { return value; }
  std::vector<std::shared_ptr<A>> children;
};

struct ArrayProxy
{
  using value_type = std::weak_ptr<A>;
  
  struct iterator
  {
    using iterator_category = std::random_access_iterator_tag;
    using difference_type   = std::ptrdiff_t;
    using value_type        = std::weak_ptr<A>;
    using pointer           = std::weak_ptr<A>*;  // or also value_type*
    using reference         = std::weak_ptr<A>&;  // or also value_type&
    
    const ArrayProxy &a;
    size_t index;
    
    iterator(const ArrayProxy &a_, size_t index_) : a(a_), index(index_) {}
    
    value_type operator*() const
    {
      size_t size = a.mpParent.children.size();
      if(index >= 0 && index < size)
      {
        return a.mpParent.children[index];
      }
      return std::weak_ptr<A>();
    }
    
    // Operators : arithmetic
    inline iterator& operator++() {++index; return *this;}
    inline iterator& operator--() {--index; return *this;}
    inline iterator& operator+=(const size_t& rhs) {index += rhs; return *this;}
    inline iterator& operator-=(const size_t& rhs) {index -= rhs; return *this;}
    
    // Operators : comparison
    inline bool operator==(const iterator& rhs) {return index == rhs.index;}
    inline bool operator!=(const iterator& rhs) {return index != rhs.index;}
    inline bool operator>(const iterator& rhs) {return index > rhs.index;}
    inline bool operator<(const iterator& rhs) {return index < rhs.index;}
    inline bool operator>=(const iterator& rhs) {return index >= rhs.index;}
    inline bool operator<=(const iterator& rhs) {return index <= rhs.index;}
  };
  
  ArrayProxy(const A &parent)
  : mpParent(parent)
  {
  }
  
  ~ArrayProxy()
  {
  }
  
  auto begin() const
  {
    return iterator(*this, 0);
  }
  auto end() const
  {
    return iterator(*this, mpParent.children.size());
  }
  size_t size() const
  {
    return mpParent.children.size();
  }
  
  const A &mpParent;
};

namespace sol {
  template<typename T>
  struct unique_usertype_traits<std::weak_ptr<T>> {
    static T* get(lua_State*, const std::weak_ptr<T>& ptr) noexcept {
      return ptr.lock().get();
    }
    
    static bool is_null(lua_State*, const std::weak_ptr<T>& ptr) noexcept {
      return ptr.expired();
    }
  };

  template <>
  struct is_container<ArrayProxy> : std::true_type { };
}


static ArrayProxy getChildren(const A &a)
{
  return ArrayProxy(a);
}

int main(void)
{
  A a(0);
  for (int i = 0; i < 100; i++)
  {
    auto child = std::make_shared<A>(i*100);
    for (int j = 0; j < 100; j++)
    {
      auto child2 = std::make_shared<A>(i*100+j);
      child->children.push_back(child2);
    }
    a.children.push_back(child);
  }
  sol::state lua;
  lua.open_libraries(sol::lib::base,
                     sol::lib::package,
                     sol::lib::table,
                     sol::lib::debug,
                     sol::lib::string,
                     sol::lib::math,
                     sol::lib::bit32,
                     sol::lib::coroutine
                     );
  
  lua.new_usertype<A>("A",
                      "value", sol::property(&A::getValue),
                      "children",  sol::property(&getChildren));
  
  lua.globals()["A"] = &a;
  
  const auto& code = R"(
      print(A.value)
  for i=1, 10 do
  co = coroutine.create( function()
  for _, child in pairs(A.children) do
    for _, child2 in pairs(child.children) do
      print(child2.value)
    end
  end
  end)
  coroutine.resume(co)
  end

    )";
  
  // call lua code directly
  lua.script(code);
  
  return 0;
}

Don't hesitate if you need further detail.

Thanks !

ThePhD pushed a commit that referenced this issue Jul 17, 2023
— 📝 Fixes #1315, #1374, and #1400.
— 📝 Lifetime in iterators was referencing the wrong stack (the main thread) rather than the coroutine's stack at time of creation.
— 📝 Using main_reference/main_* objects was a suitable enough fix for most of these problems.
— ⚡🛠 Prevent performance and usability issues from changing containers by storing the being/end iterator separately, rather than continually invoking `deferred_uc::end(…)` every time.
— 🛠 Improve sizes for stored iterators in select cases.
— 🛠 Allow for sentinel-style C++20-and-beyond ranges.
— 🔧 Improve single file generation CMake.
— 👷‍♀️ Fix up internal Lua build system issues.
ThePhD pushed a commit to soasis/sol2 that referenced this issue Jul 18, 2023
— 📝 Fixes ThePhD#1315, ThePhD#1374, and ThePhD#1400.
— 📝 Lifetime in iterators was referencing the wrong stack (the main thread) rather than the coroutine's stack at time of creation.
— 📝 Using main_reference/main_* objects was a suitable enough fix for most of these problems.
— ⚡🛠 Prevent performance and usability issues from changing containers by storing the being/end iterator separately, rather than continually invoking `deferred_uc::end(…)` every time.
— 🛠 Improve sizes for stored iterators in select cases.
— 🛠 Allow for sentinel-style C++20-and-beyond ranges.
— 🔧 Improve single file generation CMake.
— 👷‍♀️ Fix up internal Lua build system issues.
ThePhD pushed a commit that referenced this issue Jul 18, 2023
— 📝 Fixes #1315, #1374, and #1400.
— 📝 Lifetime in iterators was referencing the wrong stack (the main thread) rather than the coroutine's stack at time of creation.
— 📝 Using main_reference/main_* objects was a suitable enough fix for most of these problems.
— ⚡🛠 Prevent performance and usability issues from changing containers by storing the being/end iterator separately, rather than continually invoking `deferred_uc::end(…)` every time.
— 🛠 Improve sizes for stored iterators in select cases.
— 🛠 Allow for sentinel-style C++20-and-beyond ranges.
— 🔧 Improve single file generation CMake.
— 👷‍♀️ Fix up internal Lua build system issues.
phwissmann pushed a commit to phwissmann/sol2 that referenced this issue Mar 4, 2024
— 📝 Fixes ThePhD#1315, ThePhD#1374, and ThePhD#1400.
— 📝 Lifetime in iterators was referencing the wrong stack (the main thread) rather than the coroutine's stack at time of creation.
— 📝 Using main_reference/main_* objects was a suitable enough fix for most of these problems.
— ⚡🛠 Prevent performance and usability issues from changing containers by storing the being/end iterator separately, rather than continually invoking `deferred_uc::end(…)` every time.
— 🛠 Improve sizes for stored iterators in select cases.
— 🛠 Allow for sentinel-style C++20-and-beyond ranges.
— 🔧 Improve single file generation CMake.
— 👷‍♀️ Fix up internal Lua build system issues.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant