跨 std 实现工作的互斥体

Mutex that works across std implementations

this question 相关,我需要一个跨 std 实现工作的互斥锁,或者一种自动写入和读取指针的方法。一个线程由使用 mingw-w64 编译的代码生成,另一个线程是 static/dynamic 库中的 Visual Studio 2019 代码。

从您的主要可执行文件 (mingw-w64) 导出到您的 DLL (VC++) - 使用单独的编译器编译 - synchronization/mutex "handle"(一个不透明的指针,通常,虽然它也可以是某些东西的索引)和一对 C-style 函数(如果你愿意,你可以将它们包装到 类 中,比如 std::mutexstd::lock,暴露相同的 API - 这是最安全的做法)锁定和解锁那个手柄。它们可以像那样简单,或者它们可能包含额外的功能,如超时或 try-lock - 这些非常有用但不是必需的。您还可以导出 handle_t create()void delete(handle_t handle) 函数。

关键是同步对象本身(互斥量或其他)总是由那些间接函数操作以避免使用错误,这些函数取决于编译器(可以很容易地被预处理器检测到) ),由 compiler-specific 原子操作内在函数或 CRT 函数支持,例如完美拟合的 InterlockedCompareExchange (it works under mingw-w64 too) and its Visual C++ specific compiler intrinsic variant, or GCC's __atomic(更具体地说,__atomic_compare_exchange)。

struct imutex {
  virtual void lock() = 0;
  virtual void unlock() = 0;
  virtual ~imutex(){}
};
template<class M>
struct imp_mutex: imutex {
  M m;
  void lock() final override { m.lock(); }
  void unlock() final override { m.unlock(); }
};

struct mymutex {
  using up=std::unique_ptr<imutex, void(*)(imutex*)>;
  mymutex( up m_in ):m(std::move(m_in)){}
  mymutex():mymutex(up(new imp_mutex<std::mutex>{}, [](imutex* m){ delete m; })) {}
  void lock(){ m->lock(); }
  void unlock(){ m->unlock(); }
  mymutex(mymutex&&)=delete;
private:
  up m;
};

这假定 vtables 和 std::unique_ptr 的 ABI 兼容性,这是合理的。

如果不是,请将 unique ptr 替换为自定义内容,并将虚方法替换为采用 void 指针的函数指针。

重点是,互斥量是在一个库的代码中创建和销毁的。

这里是纯函数指针之一。它依赖于包含 two-three ptrs 的结构具有相同的布局,并且 C 调用约定是相同的。

无论哪个库制作了 mymutex,都可以使用它。

struct imutex_vtable {
  void (*lock)(void*) = 0;
  void (*unlock)(void*) = 0;
  void (*dtor)(void*)=0;
};
template<class M>
imutex_vtable const* get_imutex_vtable(){
  static const imutex_vtable vtable = {
    [](void* m){ static_cast<M*>(m)->lock(); },
    [](void* m){ static_cast<M*>(m)->unlock(); },
    [](void* m){ delete static_cast<M*>(m); }
  };
  return &vtable;
}

struct mymutex {
  mymutex( imutex_vtable const* vt, void* pm ):vtable(vt), pv(pm){}
  template<class M>
  explicit mymutex(std::unique_ptr<M> m):mymutex( get_imutex_vtable<M>(), m.release() ) {}
  mymutex():mymutex(std::make_unique<std::mutex>()) {}
  void lock(){ vtable->lock(pv); }
  void unlock(){ vtable->unlock(pv); }
  ~mymutex(){ vtable->dtor(pv); }
  mymutex(mymutex&&)=delete;
private:
  imutex_vtable const* vtable=0;
  void* pv=0;
};

这基本上是使用 C-like 实现实现 C++ 接口继承的简单案例,然后将其包装在 类 和模板中,这样用户就不会注意到。