跨 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::mutex
和 std::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++ 接口继承的简单案例,然后将其包装在 类 和模板中,这样用户就不会注意到。
与 this question 相关,我需要一个跨 std
实现工作的互斥锁,或者一种自动写入和读取指针的方法。一个线程由使用 mingw-w64 编译的代码生成,另一个线程是 static/dynamic 库中的 Visual Studio 2019 代码。
从您的主要可执行文件 (mingw-w64) 导出到您的 DLL (VC++) - 使用单独的编译器编译 - synchronization/mutex "handle"(一个不透明的指针,通常,虽然它也可以是某些东西的索引)和一对 C-style 函数(如果你愿意,你可以将它们包装到 类 中,比如 std::mutex
和 std::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++ 接口继承的简单案例,然后将其包装在 类 和模板中,这样用户就不会注意到。