Mutex that works across std implementations

为君一笑 提交于 2019-12-08 11:14:51

问题


Related to this question, I need a mutex that works across std implementations, or a way to atomically write and read a pointer. One thread is spawned by code compiled with mingw-w64 and the other one is Visual Studio 2019 code in a static/dynamic library.


回答1:


Export from your main executable (mingw-w64) to your DLL (VC++) - compiled with separate compilers - a synchronization/mutex "handle" (an opaque pointer, typically, though it can be an index into something too) and a pair of C-style functions (if you want, you can wrap them into classes like std::mutex and std::lock, exposing the same API - that'd be the safest thing to do) lock and unlock that take that handle. They can be just as bare as that, or they might include additional functionality like timeout, or try-lock - these are quite useful but not required. You can also export handle_t create() and void delete(handle_t handle) functions.

The point is that the sync object itself (the mutex or whatever) is always manipulated by those indirection functions to avoid errors in usage, and these functions are, depending on the compiler (that can easily be detected by the preprocessor), backed by compiler-specific atomic operation intrinsics or CRT functions, like the perfectly fitting InterlockedCompareExchange (it works under mingw-w64 too) and its Visual C++ specific compiler intrinsic variant, or GCC's __atomic (more specifically, __atomic_compare_exchange).




回答2:


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;
};

this assumes ABI compatibility of vtables and std::unique_ptr, which is plausible.

If not, replace unique ptr with something custom, and replace the virtual methods with function pointers taking a void pointer.

The point is, the mutex is created and destroyed in one library's code.

Here is a pure function pointer one. It relies that a struct containing two-three ptrs has the same layout, and that thr C calling convention is the same.

Whichever library makes the mymutex, both can then use it.

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;
};

This is basically implementing a simple case of C++ interface inheritance using C-like implementation, then wrapping it up in classes and templates so the user won't notice.



来源:https://stackoverflow.com/questions/56728061/mutex-that-works-across-std-implementations

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!