mercredi 6 janvier 2016

stopping std::shared_ptr wrappers of C SIGSEV due to multiple frees

This question relates to a previous question I asked a year ago. I am using WordNet 3.0 which was written a long time ago in C (circa 1996 I think).

In order to try and stay away from memory management, I asked how I could wrap std::shared_ptr around the old struct Synset used by WordNet.

A reference table shows the WordNet-3.0 functions, and inspection of the actual header shows that the SynsetPtr which functions traceptrs_ds and findtheinfo_ds are simply pointers to ss or Synset.

In my previous question I chose to remove the pointer, and then wrap it around a shared_ptr like so:

std::shared_ptr<std::remove_pointer<SynsetPtr>::type>
               (findtheinfo_ds(searchstr, pos, ptr_type, sense_num),
                free_syns);

std::shared_ptr<std::remove_pointer<SynsetPtr>::type>
               (traceptrs_ds(synptr, ptr_type, pos, depth),
                free_synset);

I have two loops, one which iterates senses by following the nextss, and one which traverses a tree list by calling traceptrs_ds. Those are the two loops (oversimplified):

std::shared_ptr<Synset> synsets;
synsets = std::shared_ptr<std::remove_pointer<SynsetPtr>::type>
          (findtheinfo_ds(const_cast<char*>(key.c_str()), lexical, HYPERPTR, ALLSENSES),free_syns);
if (synsets)
{
    std::shared_ptr<Synset> sense_ptr = synsets;
    while(sense_ptr)
    {
        graph g = graph();
        iterate(sense_ptr, nullptr, g, 0, lexical);
        sense_ptr = std::shared_ptr<std::remove_pointer<SynsetPtr>::type>(sense_ptr->nextss);
    }
}

iterate(
          std::shared_ptr<Synset> ptr,
          std::shared_ptr<layer> last,
          graph & rhs,
          int i,
          int lexical
       )
{
    while (ptr)
    {
        i++;
        ptr  = std::shared_ptr<std::remove_pointer<SynsetPtr>::type>
               (traceptrs_ds(ptr.get(), HYPERPTR, lexical, 1), free_synset);
    }
}

From what I understand (feel free to correct me) many std::shared_ptr are wrapping around the same memory, and when their destructor is called, they try to free the same memory.

How can I prevent this from happening?

I know that findtheinfo_ds returns a linked list, and that each call to traceptrs_ds appears to iterate that list. I image that the best approach would be to wrap only one std::shared_ptr which is going to call the Synset destructor free_synset, instead of doing it many times.

How can I stop this from happening?

Assuming there is no solution to prevent this from happening, how can the destructors of the many std::shared_ptr that map to the same area, only free once? I guess this is a bad/ugly way, but it may be easier?

The valgrind output:

==3066== Invalid read of size 8
==3066==    at 0x52C3B4C: free_synset (in /usr/lib/libwordnet-3.0.so)
==3066==    by 0x4E6E2D9: std::_Sp_counted_deleter<ss*, void (*)(ss*), std::allocator<void>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr_base.h:463)
==3066==    by 0x403C5F: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (shared_ptr_base.h:149)
==3066==    by 0x403AD8: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (shared_ptr_base.h:666)
==3066==    by 0x4E61DB7: std::__shared_ptr<ss, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (shared_ptr_base.h:914)
==3066==    by 0x4E61DD1: std::shared_ptr<ss>::~shared_ptr() (shared_ptr.h:93)
==3066==    by 0x4E6161A: smnet::hyper_handler::operator()(std::string, int) (hyper_handler.cpp:22)
==3066==    by 0x4036D6: main (hypernym.cpp:16)
==3066==  Address 0x10 is not stack'd, malloc'd or (recently) free'd

==3066== Process terminating with default action of signal 11 (SIGSEGV)
==3066==  Access not within mapped region at address 0x10
==3066==    at 0x52C3B4C: free_synset (in /usr/lib/libwordnet-3.0.so)
==3066==    by 0x4E6E2D9: std::_Sp_counted_deleter<ss*, void (*)(ss*), std::allocator<void>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr_base.h:463)
==3066==    by 0x403C5F: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (shared_ptr_base.h:149)
==3066==    by 0x403AD8: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (shared_ptr_base.h:666)
==3066==    by 0x4E61DB7: std::__shared_ptr<ss, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (shared_ptr_base.h:914)
==3066==    by 0x4E61DD1: std::shared_ptr<ss>::~shared_ptr() (shared_ptr.h:93)
==3066==    by 0x4E6161A: smnet::hyper_handler::operator()(std::string, int) (hyper_handler.cpp:22)
==3066==    by 0x4036D6: main (hypernym.cpp:16)

==3066== LEAK SUMMARY:
==3066==    definitely lost: 0 bytes in 0 blocks
==3066==    indirectly lost: 0 bytes in 0 blocks
==3066==      possibly lost: 251 bytes in 8 blocks
==3066==    still reachable: 19,964 bytes in 90 blocks
==3066==         suppressed: 0 bytes in 0 blocks
==3066== Reachable blocks (those to which a pointer was found) are not shown.
==3066== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==3066== 
==3066== For counts of detected and suppressed errors, rerun with: -v
==3066== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
fish: “valgrind --leak-check=full ./hy…” terminated by signal SIGSEGV (Address boundary error)

Aucun commentaire:

Enregistrer un commentaire