【问题标题】:stopping std::shared_ptr wrappers of C pointers from SIGSEV due to multiple frees由于多次释放,从 SIGSEGV 停止 C 指针的 std::shared_ptr 包装器
【发布时间】:2016-04-11 05:20:41
【问题描述】:

这个问题与我一年前问的previous question 有关。 我正在使用很久以前用 C 语言编写的 WordNet 3.0(我认为大约是 1996 年)。

为了尽量远离内存管理,我询问如何将 std::shared_ptr 包裹在 WordNet 使用的旧 struct Synset 周围。

reference table 显示 WordNet-3.0 功能,检查the actual header 显示SynsetPtrtraceptrs_dsfindtheinfo_ds 返回的函数是指向 ssSynset 的指针。

在我的previous question 中我选择移除指针, 然后将其包裹在 shared_ptr 周围,如下所示:

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);

我有两个循环,一个通过nextss 迭代senses,另一个通过调用traceptrs_ds 遍历树列表。 这是两个循环(过于简单):

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);
    }
}

据我了解(请随时纠正我)许多std::shared_ptr 都围绕着相同的记忆, 当调用它们的析构函数时,它们会尝试释放相同的内存。

如何防止这种情况发生?

我知道findtheinfo_ds 返回一个链接列表,并且每次调用traceptrs_ds 似乎都会迭代该列表。 我认为最好的方法是只包装一个std::shared_ptr,它将调用Synset 析构函数free_synset,而不是多次这样做。

我怎样才能阻止这种情况发生?

假设没有解决方案可以防止这种情况发生, 映射到同一个区域的众多std::shared_ptr 的析构函数如何只释放一次? 我想这是一种糟糕/丑陋的方式,但它可能更容易?

valgrind 输出:

==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)

【问题讨论】:

    标签: c++ pointers c++11 shared-ptr wordnet


    【解决方案1】:

    好吧,经过一番搜索,我找到了使用 struct functors 和 lambdas 的解决方案。虽然它不漂亮(实际上它很丑)但似乎有效:

    struct sense_del
    {
        void operator()(Synset *ptr)
        {
            if (ptr)
                free_syns(ptr);
        }
    };
    
    struct trace_del
    {
        void operator()(Synset *ptr)
        {
            if (ptr)
                free_synset(ptr);
        }
    };
    
    std::shared_ptr<Synset>
    shared_findtheinfo_ds(std::string key, int gram_type, int ptr_type, int sense_num)
    {
        wn_init_once();
        std::shared_ptr<Synset> sp(findtheinfo_ds(const_cast<char*>(key.c_str()), gram_type, ptr_type, sense_num),
                                   [](Synset * ptr){sense_del()(ptr);});
        return sp;
    }
    
    std::shared_ptr<Synset> 
    shared_traceptrs_ds(SynsetPtr synptr, int ptr_type, int gram_type, int depth)
    {
        assert(synptr);
        std::shared_ptr<Synset> sp(traceptrs_ds(synptr, ptr_type, gram_type, depth),
                                  [](Synset *ptr){trace_del()(ptr);});
        return sp;
    }
    

    仅供参考,对于在 Wordnet 上工作的任何人,Hypernyms 的正确迭代方式是这样的:

    if (auto ss_ptr = findtheinfo_ds(const_cast<char*>(key.c_str()), lexical, HYPERPTR, ALLSENSES))
    {
        while(ss_ptr)
        {
            if (SynsetPtr trc_ptr = traceptrs_ds(ss_ptr, HYPERPTR, lexical, 1))
            {
                do_smth_with_it(trc_ptr);
                free_syns(trc_ptr);
            }
            // get next sense 
            ss_ptr = ss_ptr->nextss;
        }
        free_syns(ss_ptr);
    }
    

    【讨论】:

      猜你喜欢
      • 2010-12-04
      • 2017-08-10
      • 2012-09-07
      • 2020-12-06
      • 1970-01-01
      • 2012-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多