首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >由于多个释放,从SIGSEV停止C指针的std::shared_ptr包装器

由于多个释放,从SIGSEV停止C指针的std::shared_ptr包装器
EN

Stack Overflow用户
提问于 2016-01-07 07:12:56
回答 1查看 134关注 0票数 0

这个问题与我一年前问过的一个previous question有关。我使用的是WordNet 3.0,它是很久以前用C编写的(我想大概是1996年左右)。

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

一个reference table显示了WordNet3.0函数,对the actual header的检查表明,函数traceptrs_dsfindtheinfo_ds返回的SynsetPtr是指向ssSynset的指针。

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

代码语言:javascript
运行
复制
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遍历树列表。这是两个循环(过于简单):

代码语言:javascript
运行
复制
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都会迭代这个链表。我认为最好的方法是只包装一个将调用Synset析构函数free_synsetstd::shared_ptr,而不是多次这样做。

我如何才能阻止这种情况的发生?

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

valgrind输出:

代码语言:javascript
运行
复制
==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)
EN

Stack Overflow用户

发布于 2016-01-08 03:34:54

嗯,经过相当多的搜索,我找到了使用struct functors和lambda的解决方案。诚然,它不是很漂亮(事实上它相当丑陋),但看起来很管用:

代码语言:javascript
运行
复制
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的正确迭代方式是:

代码语言:javascript
运行
复制
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);
}
票数 1
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34644634

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档