Boost interprocess_condition调用wait()的多个线程失败

时间:2016-02-10 01:22:12

标签: c++ multithreading boost osx-elcapitan

遇到一个非常奇怪的问题,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上试过它。

2 个答案:

答案 0 :(得分:1)

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

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

  2. 在OSX的pthread实现中,condition变量存储一个指向互斥变量的原始指针。在第一个进程中第一次调用pthread_wait()会设置此指针。第二次调用pthread_wait()检查这个存储的互斥指针是否反对传递给pthread_wait()的互斥锁指针。 {可以在此处找到来源:https://opensource.apple.com/source/libpthread/libpthread-137.1.1/src/pthread_cond.c}

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

  4. 因此,使这项工作的两个选项如下:

    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()处理新进程,这意味着子进程将拥有原始段管理器的副本,映射到同一地址,因此原始指针将起作用。 / p>

    3. 我没有深入研究glibc pthreads代码,看看它们与Apple有什么不同,所以我不确定为什么原始示例适用于Linux但不适用于OSX。

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

答案 1 :(得分:1)

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

在boost / interprocess / detail / workaround.hpp中,您会找到一条评论说:

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

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

#define BOOST_INTERPROCESS_BUGGY_POSIX_PROCESS_SHARED