【问题标题】:Boost interprocess_condition multiple threads calling wait() failsBoost interprocess_condition 多线程调用 wait() 失败
【发布时间】:2016-02-10 01:22:11
【问题描述】:

遇到一个非常奇怪的问题,有 2 个以上的线程在等待 interprocess_condition 变量。

提升 1.60.0

  • 使用 1 个线程调用 wait() 和第 2 个调用 notify_all(),一切正常。
  • 当有 2 次以上调用 wait() 时,do_wait() 上的断言失败,进程退出。

Test.cpp:


#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/sync/interprocess_condition.hpp>
#include <iostream>

using namespace boost::interprocess;

struct Data {
    interprocess_mutex mux_;
    interprocess_condition cond_;
};

int main(int argc, char *argv[]) {
    if (argc > 1 && atoi(argv[1]) == 0) {
        struct shm_remove {
            shm_remove() { shared_memory_object::remove("MySharedMemory"); }
            ~shm_remove() { shared_memory_object::remove("MySharedMemory"); }
        } remover;

        managed_shared_memory seg(create_only, "MySharedMemory", 65536);
        Data *const d = seg.construct<Data>(unique_instance)();
        scoped_lock<interprocess_mutex> lock(d->mux_);
        std::cout << "Waiting" << std::endl;
        d->cond_.wait(lock);
    } else if (argc > 1 && atoi(argv[1]) == 1) {
        managed_shared_memory seg(open_only, "MySharedMemory");
        std::pair<Data *, std::size_t> res = seg.find<Data>(unique_instance);
        scoped_lock<interprocess_mutex> lock(res.first->mux_);
        std::cout << "Waiting" << std::endl;
        res.first->cond_.wait(lock);
    } else {
        managed_shared_memory seg(open_only, "MySharedMemory");
        std::pair<Data *, std::size_t> res = seg.find<Data>(unique_instance);
        scoped_lock<interprocess_mutex> lock(res.first->mux_);
        std::cout << "Notifying" << std::endl;
        res.first->cond_.notify_all();
    }
}

编译为:

$ clang++ -I/usr/local/include test.cpp

使用 1 个 wait() 和 1 个 notify() 运行:

$ ./a.out 0&
[8] 25889
Waiting

$ ./a.out 2&
[9] 25901
Notifying
[8]-  Done                    ./a.out 0
[9]+  Done                    ./a.out 2

运行 2 次等待:

$ ./a.out 0&
[8] 25986
Waiting
$ ./a.out 1&
[9] 25998
Waiting
Assertion failed: (res == 0), function do_wait, file /usr/local/include/boost/interprocess/sync/posix/condition.hpp, line 175.

在 OSX El Capitan 上测试

$ uname -a
Darwin LUS-JOHUGHES2 15.3.0 Darwin Kernel Version 15.3.0: Thu Dec 10 18:40:58 PST 2015; root:xnu-3248.30.4~1/RELEASE_X86_64 x86_64

我还在 Ubuntu Trusty 机器上尝试了上述示例,所有示例都按预期工作,这让我相信 OSX 实现存在问题。我还没有在 Windows 上尝试过。

【问题讨论】:

  • 同意这看起来像是一个特定于平台的怪癖。在我的 Ubuntu 机器上,一切看起来都很好。这里是 Live On Coliru(由于 Coliru 限制,使用 managed_mapped_file

标签: c++ multithreading boost osx-elcapitan


【解决方案1】:

进行了一些挖掘并找到了问题的明确答案。

  1. 当第二个进程调用 do_wait() 时,上面的 boost 断言错误失败,它调用 pthread_wait(),它立即返回 EINVAL(而不是成功的 0)。

  2. 在 OSX 的 pthread 实现中,条件变量存储指向互斥变量的原始指针。在第一个进程中对 pthread_wait() 的第一次调用设置了这个指针。第二次调用 pthread_wait() 检查存储的互斥指针与传递给 pthread_wait() 的互斥指针。 {可以在这里找到源代码:https://opensource.apple.com/source/libpthread/libpthread-137.1.1/src/pthread_cond.c}

  3. 由于两个进程已将共享互斥锁和条件变量映射到不同的地址空间,第二次调用 pthread_wait() 将永远无法工作,因为它会比较原始指针。

因此,完成这项工作的 2 个选项如下:

  1. 使用固定地址映射:http://www.boost.org/doc/libs/1_60_0/doc/html/interprocess/sharedmemorybetweenprocesses.html#interprocess.sharedmemorybetweenprocesses.mapped_region.mapped_region_fixed_address_mapping,它保证映射的区域将位于相同的地址,因此原始指针将起作用,或者

  2. 使用 fork() 代替 exec()'ing 新进程,这意味着子进程将拥有原始段管理器的副本,映射到相同的地址,因此原始指针将起作用。

我没有深入研究 glibc pthreads 代码以了解它们与 Apple 的不同之处,因此我不确定为什么原始示例适用于 Linux 而不是 OSX。

我认为 Boost 文档肯定会受益于讨论这个陷阱的段落。

【讨论】:

  • 很好的发现。我不会将此作为某种怪癖添加到 boost 文档中,而是认为这是 Boost Interprocess 中的一个错误。该库旨在提供可移植的进程间通信(和同步),在这个地方,实现不是可移植的。 请在Trac 报告错误,因为report doesn't appear to exist
【解决方案2】:

这是 Darwin 的 C 库和 Boost.Interprocess 中的一个错误。首先,该 C 库声称它符合 posix 并支持进程共享内存条件变量,这是错误的(因为它使用原始指针来存储互斥体的地址)。其次,Boost.Interprocess 应该将此平台检测为有问题的平台,应该禁用 pthread 并回退到仿真。

在 boost/interprocess/detail/workaround.hpp 中,你会发现一条评论说:

//Mac Os X &lt; Lion (10.7) might define _POSIX_THREAD_PROCESS_SHARED but there is no real support.

一些旧报道声称较新的macos版本确实支持进程共享条件变量,但这种说法是错误的,所以__APPLE__部分应该只是:

#define BOOST_INTERPROCESS_BUGGY_POSIX_PROCESS_SHARED

【讨论】:

  • 很酷的补充。感谢您添加这些
猜你喜欢
  • 1970-01-01
  • 2013-05-02
  • 1970-01-01
  • 2020-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-11
  • 1970-01-01
相关资源
最近更新 更多