并行测试与地理相交时的段错误

Posted

技术标签:

【中文标题】并行测试与地理相交时的段错误【英文标题】:Segfault when testing the intersection with geos in parallel 【发布时间】:2017-11-08 14:22:31 【问题描述】:

我正在使用 GEOS 的 C API 来管理并行模拟器中的几何图形:我需要确保对象保持在受限区域内。 为此,当我的对象迈出一步时,我检查其先前位置和当前位置之间的 line 是否与环境边界相交(environment_manager_->get_border(),它返回一个 const GEOSPreparedGeometry*)。

代码与多个 OpenMP 线程并行运行。我在开始模拟之前通过

初始化了GEOS
initGEOS_r(notice, log_and_exit);
context_handler_ = GEOS_init_r();

所以我对所有线程只有一个处理程序。 使用多个线程运行模拟时,如果我没有将 GEOSPreparedIntersects_r 包装在临界区中,代码会在 env_intersect 内部崩溃并出现段错误。

bool SpaceManager::env_intersect(const GEOSGeometry * line) const

    bool intersect;
    // #pragma omp critical // adding prevents segfault
    // 
        intersect = GEOSPreparedIntersects_r(
            context_handler_, environment_manager_->get_border(), line);
    // 
    return intersect;

有人可以指出我做错了什么吗?是否需要以不同方式进行 GEOS 的初始化才能与 OpenMP 一起使用?它是否来自environment_manager_unique_ptr 的事实?这些都没有?


段错误的回溯给出:

Thread 19 "python" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffd63da700 (LWP 4374)]
0x00007ffff78816b2 in tcache_get () from /usr/lib/libc.so.6
(gdb) bt
#0  0x00007ffff78816b2 in tcache_get () at /usr/lib/libc.so.6
#1  0x00007ffff5dbf0a9 in operator new(unsigned long) (sz=sz@entry=32)
    at /build/gcc-multilib/src/gcc/libstdc++-v3/libsupc++/new_op.cc:50
#2  0x00007fffc8468273 in geos::geom::LineString::computeEnvelopeInternal() const (this=0x555555f10170) at LineString.cpp:278
#3  0x00007fffc845b95f in geos::geom::Geometry::getEnvelopeInternal() const (this=0x555555f10180) at Geometry.cpp:276
#4  0x00007fffc84612ad in geos::geom::GeometryCollection::computeEnvelopeInternal() const (this=0x555555f15980) at GeometryCollection.cpp:257
#5  0x00007fffc845b95f in geos::geom::Geometry::getEnvelopeInternal() const (this=0x555555f15998) at Geometry.cpp:276
#6  0x00007ffff53cea66 in geos::geom::prep::BasicPreparedGeometry::envelopesIntersect(geos::geom::Geometry const*) const () at /usr/lib/libgeos-3.6.2.so
#7  0x00007ffff53cef82 in geos::geom::prep::PreparedLineString::intersects(geos::geom::Geometry const*) const () at /usr/lib/libgeos-3.6.2.so
#8  0x00007ffff5907380 in GEOSPreparedIntersects_r ()
    at /usr/lib/libgeos_c.so.1
#9  0x00007ffff61d7938 in growth::SpaceManager::env_intersect(GEOSGeom_t const*) const (this=0x555555fb3b78, line=0x7fff7c045c20)

[编辑]为每个 OMP 线程制作一个 const GEOSPreparedGeometry* 环境副本并仅访问特定于线程的副本成功地防止了缺少 critical 部分的段错误。代码变为:

intersect = GEOSPreparedIntersects_r(
        context_handler_, environment_manager_->get_border(omp_id), line);

其中environment_manager_->get_border(omp_id) 返回特定于线程的const GEOSPreparedGeometry*。这意味着问题来自我之前使用单个指针时对共享数据的并发访问。这看起来很奇怪,因为该函数应该是线程安全的......

【问题讨论】:

你为什么认为你做错了什么?通过快速搜索,我找不到任何注意到线程安全的文档。 unique_ptr 应该在发布版本中优化掉,所以不是问题的原因。 如果我理解正确that post,所有_r 函数都应该是线程安全的,但也许我没有以正确的方式使用它们...... 崩溃发生在 operator new 内部,这通常意味着内存已被覆盖(即,访问某些对象或数组的分配范围之外,或写入已删除的对象)。 我明白了,谢谢你的洞察力,我会调查的。 【参考方案1】:

显然线程安全的实现还没有准备好(见mailing list exchange)。 我通过在每个线程上使用全局环境几何体的副本解决了这个问题(这显然也允许完全并发测试)。

【讨论】:

以上是关于并行测试与地理相交时的段错误的主要内容,如果未能解决你的问题,请参考以下文章

调用 glDrawElements 时的段错误

使用 CUDA 流时的段错误

推送到成员向量时的段错误

未指定 lambda 函数的返回类型时的段错误

将结构从 main() 传递给工作函数时的段错误

Struct 上下文中未使用 printf 时的段错误