Ускоренный питон, не видящий память, принадлежит умному указателю - PullRequest
3 голосов
/ 27 июня 2019

Я получаю триггер ошибки сегмента, когда деструктор ниже уничтожает свои векторные элементы.Первоначально это был vector<Parent>, но я изменил его на vector<unique_ptr<Parent>>, и с тех пор сбой происходит каждый раз:

class Owner
{
    Owner() = default;
    ~Owner() = default;    // Seg faults

    void assignVec(std::vector<std::unique_ptr<Parent>>& vec)
    {
        _vec = std::move(vec);
    }

    std::vector<std::unique_ptr<Parent>> _vec;
};

Каждый подтип векторного элемента является полиморфным классом, также наследуемым от boost :: python:: wrapper

class Child: public Parent, public boost::python::wrapper<Parent>
{
    Child();
    virtual ~Child() = default;
};

, где:

class Parent
{
    Parent() = default;
    virtual ~Parent() = default;
};

Таким образом, во всей иерархии наследования есть виртуальные деструкторы.

Обратный след GDB показывает:

#0  0x00007ffff636b207 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:55
#1  0x00007ffff636c8f8 in __GI_abort () at abort.c:90
#2  0x00007ffff63add27 in __libc_message (do_abort=do_abort@entry=2, fmt=fmt@entry=0x7ffff64bf678 "*** Error in `%s': %s: 0x%s ***\n") at ../sysdeps/unix/sysv/linux/libc_fatal.c:196
#3  0x00007ffff63b6489 in malloc_printerr (ar_ptr=0x7ffff66fb760 <main_arena>, ptr=<optimized out>, str=0x7ffff64bcd31 "free(): invalid pointer", action=3) at malloc.c:5004
#4  _int_free (av=0x7ffff66fb760 <main_arena>, p=<optimized out>, have_lock=0) at malloc.c:3843
#5  0x00007fffc373972f in Child::~Child (this=0x2742b10, __in_chrg=<optimized out>) at Child.h:23
#6  0x000000000045694e in std::default_delete<Parent>::operator() (this=0x11922e0, __ptr=0x2742b10) at /opt/gcc-8.2.0/include/c++/8.2.0/bits/unique_ptr.h:81
#7  0x0000000000454c27 in std::unique_ptr<Parent, std::default_delete<Parent> >::~unique_ptr (this=0x11922e0, __in_chrg=<optimized out>) at /opt/gcc-8.2.0/include/c++/8.2.0/bits/unique_ptr.h:274
#8  0x000000000045a882 in std::_Destroy<std::unique_ptr<Parent, std::default_delete<Parent> > > (__pointer=0x11922e0) at /opt/gcc-8.2.0/include/c++/8.2.0/bits/stl_construct.h:98
#9  0x0000000000458f67 in std::_Destroy_aux<false>::__destroy<std::unique_ptr<Parent, std::default_delete<Parent> >*> (__first=0x11922e0, __last=0x11922e8) at /opt/gcc-8.2.0/include/c++/8.2.0/bits/stl_construct.h:108
#10 0x0000000000457636 in std::_Destroy<std::unique_ptr<Parent, std::default_delete<Parent> >*> (__first=0x11922e0, __last=0x11922e8) at /opt/gcc-8.2.0/include/c++/8.2.0/bits/stl_construct.h:137
#11 0x000000000045584d in std::_Destroy<std::unique_ptr<Parent, std::default_delete<Parent> >*, std::unique_ptr<Parent, std::default_delete<Parent> > > (__first=0x11922e0, __last=0x11922e8)
    at /opt/gcc-8.2.0/include/c++/8.2.0/bits/stl_construct.h:206
#12 0x000000000049b53d in std::vector<std::unique_ptr<Parent, std::default_delete<Parent> >, std::allocator<std::unique_ptr<Parent, std::default_delete<Parent> > > >::~vector (this=0x7fffffffc4a8, __in_chrg=<optimized out>)
    at /opt/gcc-8.2.0/include/c++/8.2.0/bits/stl_vector.h:567
#13 0x000000000048c677 in Owner::~Owner (this=0x7fffffffc4a8, __in_chrg=<optimized out>)

Печать this в кадре 5 показывает действительный объект.Исходный код кадра 4 для free ():

static void _int_free(mstate av, mchunkptr p, int have_lock)                      
{                                                                     
  INTERNAL_SIZE_T size;        /* its size */                         
  mfastbinptr*    fb;          /* associated fastbin */               
  mchunkptr       nextchunk;   /* next contiguous chunk */            
  INTERNAL_SIZE_T nextsize;    /* its size */                         
  int             nextinuse;   /* true if nextchunk is used */        
  INTERNAL_SIZE_T prevsize;    /* size of previous contiguous chunk */
  mchunkptr       bck;         /* misc temp for linking */            
  mchunkptr       fwd;         /* misc temp for linking */            

  const char *errstr = NULL;
  int locked = 0;

  size = chunksize(p);

  /* Little security check which won't hurt performance: the          
     allocator never wrapps around at the end of the address space.   
     Therefore we can exclude some size values which might appear
     here by accident or by "design" from some intruder.  */        
  if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)    
      || __builtin_expect (misaligned_chunk (p), 0))
    {  
      errstr = "free(): invalid pointer";
    errout:      
      if (have_lock || locked)
        (void)mutex_unlock(&av->mutex);                               
      malloc_printerr (check_action, errstr, chunk2mem(p), av);      // CRASHES HERE

У кого-нибудь есть советы, как продолжить отладку?

ОБНОВЛЕНИЕ:

Я создал небольшой пример в модульном тесте, создающий Owner и вектор, вызывающий assignVec(), и проблема не возникает.Однако, как только вектор передается, ничто другое не получает родительскую память.

UPDATE2:

Мы думаем, что проблема заключается в том, что boost python нужно информировать оумный указательОчевидно, Boost Python не поддерживает unique_ptr, и мы изо всех сил пытаемся заставить его распознавать shared_ptr (как Boost, так и std), даже используя обычную технику регистров.

1 Ответ

0 голосов
/ 28 июня 2019

Исходя из кода, который вы даете, и с предположением, что boost::python не содержит ошибок, я думаю, что причиной этого может быть использование вами движущейся семантики:

void assignVec(std::vector<std::unique_ptr<Parent>>& vec)
    {
        _vec = std::move(vec);
    }

Здесь вы переходите от l-value-reference к вектору, vector& к вашему члену. Проблема в том, что обычно можно перейти только от ссылок r-значений (vector&&), поскольку они больше не доступны, потому что единственная привязка к временным файлам или если она явно подтверждает перемещение, создавая ссылку на r / x-значения, такую ​​как вы сделал с помощью std::move.

Проблема в том, что я держу пари, что вызывающая сторона assignVec может не знать об этом, потому что тогда, почему вы еще не использовали ссылку на r-значение в подписи, так что вызывающая сторона должна явно указать std::move? Я предполагаю, что вы, звонящий по телефону, этого не делаете, и делаете больше, чем единственное, что является законным для перемещения из ценностей: уничтожьте их.

Конечно, вы спрашиваете себя, почему это ломается тогда в деструкторе? По моему опыту, ошибки сегментации происходят после выражения причины, в этом случае я бы сказал неопределенное поведение вызывающей стороны assignVec, продолжая использовать вектор, заданный assignVec.

...