mirror of https://github.com/KLayout/klayout.git
Bugfix: multithread-safety for gsi::Proxy (required for the tiling processor use case).
This commit is contained in:
parent
803a29037a
commit
2b64c4bf82
|
|
@ -31,6 +31,8 @@
|
||||||
#include "dbSaveLayoutOptions.h"
|
#include "dbSaveLayoutOptions.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QMutexLocker>
|
||||||
|
|
||||||
unsigned int get_rand()
|
unsigned int get_rand()
|
||||||
{
|
{
|
||||||
|
|
@ -400,6 +402,8 @@ public:
|
||||||
|
|
||||||
void add (double x) const
|
void add (double x) const
|
||||||
{
|
{
|
||||||
|
static QMutex lock;
|
||||||
|
QMutexLocker locker (&lock);
|
||||||
*mp_sum += x;
|
*mp_sum += x;
|
||||||
*mp_n += 1;
|
*mp_n += 1;
|
||||||
}
|
}
|
||||||
|
|
@ -419,6 +423,8 @@ gsi::Class<MyTilingOutputReceiver> decl_MyTilingOutputReceiver (gsi::dbdecl_Tile
|
||||||
);
|
);
|
||||||
|
|
||||||
// Multithreaded, access to _rec()
|
// Multithreaded, access to _rec()
|
||||||
|
// This will mainly test the ability of gsi::Proxy to manage references
|
||||||
|
// in a multithreaded case.
|
||||||
TEST(5)
|
TEST(5)
|
||||||
{
|
{
|
||||||
db::Layout ly1;
|
db::Layout ly1;
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,13 @@
|
||||||
|
|
||||||
#include "tlLog.h"
|
#include "tlLog.h"
|
||||||
|
|
||||||
|
#include <QMutexLocker>
|
||||||
|
|
||||||
namespace gsi
|
namespace gsi
|
||||||
{
|
{
|
||||||
|
|
||||||
|
QMutex Proxy::m_lock;
|
||||||
|
|
||||||
Proxy::Proxy (const gsi::ClassBase *_cls_decl)
|
Proxy::Proxy (const gsi::ClassBase *_cls_decl)
|
||||||
: m_cls_decl (_cls_decl),
|
: m_cls_decl (_cls_decl),
|
||||||
m_obj (0),
|
m_obj (0),
|
||||||
|
|
@ -43,8 +47,10 @@ Proxy::Proxy (const gsi::ClassBase *_cls_decl)
|
||||||
|
|
||||||
Proxy::~Proxy ()
|
Proxy::~Proxy ()
|
||||||
{
|
{
|
||||||
|
QMutexLocker locker (&m_lock);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
set (0, false, false, false);
|
set_internal (0, false, false, false);
|
||||||
} catch (std::exception &ex) {
|
} catch (std::exception &ex) {
|
||||||
tl::warn << "Caught exception in object destructor: " << ex.what ();
|
tl::warn << "Caught exception in object destructor: " << ex.what ();
|
||||||
} catch (tl::Exception &ex) {
|
} catch (tl::Exception &ex) {
|
||||||
|
|
@ -58,6 +64,8 @@ Proxy::~Proxy ()
|
||||||
void
|
void
|
||||||
Proxy::destroy ()
|
Proxy::destroy ()
|
||||||
{
|
{
|
||||||
|
QMutexLocker locker (&m_lock);
|
||||||
|
|
||||||
if (! m_cls_decl) {
|
if (! m_cls_decl) {
|
||||||
m_obj = 0;
|
m_obj = 0;
|
||||||
return;
|
return;
|
||||||
|
|
@ -82,7 +90,7 @@ Proxy::destroy ()
|
||||||
if (m_owned || m_can_destroy) {
|
if (m_owned || m_can_destroy) {
|
||||||
o = m_obj;
|
o = m_obj;
|
||||||
}
|
}
|
||||||
detach ();
|
detach_internal ();
|
||||||
if (o) {
|
if (o) {
|
||||||
m_cls_decl->destroy (o);
|
m_cls_decl->destroy (o);
|
||||||
}
|
}
|
||||||
|
|
@ -91,23 +99,15 @@ Proxy::destroy ()
|
||||||
void
|
void
|
||||||
Proxy::detach ()
|
Proxy::detach ()
|
||||||
{
|
{
|
||||||
if (! m_destroyed && m_cls_decl && m_cls_decl->is_managed ()) {
|
QMutexLocker locker (&m_lock);
|
||||||
gsi::ObjectBase *gsi_object = m_cls_decl->gsi_object (m_obj, false);
|
detach_internal ();
|
||||||
if (gsi_object) {
|
|
||||||
gsi_object->status_changed_event ().remove (this, &Proxy::object_status_changed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_obj = 0;
|
|
||||||
m_destroyed = true;
|
|
||||||
m_const_ref = false;
|
|
||||||
m_owned = false;
|
|
||||||
m_can_destroy = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Proxy::release ()
|
Proxy::release ()
|
||||||
{
|
{
|
||||||
|
QMutexLocker locker (&m_lock);
|
||||||
|
|
||||||
// If the object is managed we first reset the ownership of all other clients
|
// If the object is managed we first reset the ownership of all other clients
|
||||||
// and then make us the owner
|
// and then make us the owner
|
||||||
const gsi::ClassBase *cls = m_cls_decl;
|
const gsi::ClassBase *cls = m_cls_decl;
|
||||||
|
|
@ -125,6 +125,8 @@ Proxy::release ()
|
||||||
void
|
void
|
||||||
Proxy::keep ()
|
Proxy::keep ()
|
||||||
{
|
{
|
||||||
|
QMutexLocker locker (&m_lock);
|
||||||
|
|
||||||
const gsi::ClassBase *cls = m_cls_decl;
|
const gsi::ClassBase *cls = m_cls_decl;
|
||||||
if (cls) {
|
if (cls) {
|
||||||
void *o = obj ();
|
void *o = obj ();
|
||||||
|
|
@ -142,6 +144,45 @@ Proxy::keep ()
|
||||||
|
|
||||||
void
|
void
|
||||||
Proxy::set (void *obj, bool owned, bool const_ref, bool can_destroy)
|
Proxy::set (void *obj, bool owned, bool const_ref, bool can_destroy)
|
||||||
|
{
|
||||||
|
QMutexLocker locker (&m_lock);
|
||||||
|
set_internal (obj, owned, const_ref, can_destroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
Proxy::obj ()
|
||||||
|
{
|
||||||
|
QMutexLocker locker (&m_lock);
|
||||||
|
|
||||||
|
if (! m_obj) {
|
||||||
|
if (m_destroyed) {
|
||||||
|
throw tl::Exception (tl::to_string (QObject::tr ("Object has been destroyed already")));
|
||||||
|
} else {
|
||||||
|
// delayed creation of a detached C++ object ..
|
||||||
|
set_internal (m_cls_decl->create (), true, false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Proxy::object_status_changed (gsi::ObjectBase::StatusEventType type)
|
||||||
|
{
|
||||||
|
QMutexLocker locker (&m_lock);
|
||||||
|
|
||||||
|
if (type == gsi::ObjectBase::ObjectDestroyed) {
|
||||||
|
m_destroyed = true; // NOTE: must be set before detach and indicates that the object was destroyed externally.
|
||||||
|
detach_internal ();
|
||||||
|
} else if (type == gsi::ObjectBase::ObjectKeep) {
|
||||||
|
m_owned = false;
|
||||||
|
} else if (type == gsi::ObjectBase::ObjectRelease) {
|
||||||
|
m_owned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Proxy::set_internal (void *obj, bool owned, bool const_ref, bool can_destroy)
|
||||||
{
|
{
|
||||||
bool prev_owned = m_owned;
|
bool prev_owned = m_owned;
|
||||||
|
|
||||||
|
|
@ -198,32 +239,21 @@ Proxy::set (void *obj, bool owned, bool const_ref, bool can_destroy)
|
||||||
m_destroyed = false;
|
m_destroyed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *
|
void
|
||||||
Proxy::obj ()
|
Proxy::detach_internal()
|
||||||
{
|
{
|
||||||
if (! m_obj) {
|
if (! m_destroyed && m_cls_decl && m_cls_decl->is_managed ()) {
|
||||||
if (m_destroyed) {
|
gsi::ObjectBase *gsi_object = m_cls_decl->gsi_object (m_obj, false);
|
||||||
throw tl::Exception (tl::to_string (QObject::tr ("Object has been destroyed already")));
|
if (gsi_object) {
|
||||||
} else {
|
gsi_object->status_changed_event ().remove (this, &Proxy::object_status_changed);
|
||||||
// delayed creation of a detached C++ object ..
|
|
||||||
set(m_cls_decl->create (), true, false, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_obj;
|
m_obj = 0;
|
||||||
}
|
m_destroyed = true;
|
||||||
|
m_const_ref = false;
|
||||||
void
|
m_owned = false;
|
||||||
Proxy::object_status_changed (gsi::ObjectBase::StatusEventType type)
|
m_can_destroy = false;
|
||||||
{
|
|
||||||
if (type == gsi::ObjectBase::ObjectDestroyed) {
|
|
||||||
m_destroyed = true; // NOTE: must be set before detach and indicates that the object was destroyed externally.
|
|
||||||
detach ();
|
|
||||||
} else if (type == gsi::ObjectBase::ObjectKeep) {
|
|
||||||
m_owned = false;
|
|
||||||
} else if (type == gsi::ObjectBase::ObjectRelease) {
|
|
||||||
m_owned = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
#include "gsiCommon.h"
|
#include "gsiCommon.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <QMutex>
|
||||||
|
|
||||||
// For a comprehensive documentation see gsi.h
|
// For a comprehensive documentation see gsi.h
|
||||||
|
|
||||||
|
|
@ -191,6 +192,17 @@ private:
|
||||||
*
|
*
|
||||||
* Using a proxy object allows having a gsi::ObjectBase that does not derive
|
* Using a proxy object allows having a gsi::ObjectBase that does not derive
|
||||||
* from tl::Object and can derive from any base class (specifically QObject).
|
* from tl::Object and can derive from any base class (specifically QObject).
|
||||||
|
*
|
||||||
|
* NOTE about MT safety: the model is:
|
||||||
|
*
|
||||||
|
* - the Proxy belongs to a thread
|
||||||
|
* - the original object (the proxy target) belongs to a different thread
|
||||||
|
* - there can be multiple Proxy objects for one target
|
||||||
|
* - the target object itself and the methods by which the proxy acts on
|
||||||
|
* the target object are thread safe
|
||||||
|
*
|
||||||
|
* This implies that all operations related to the manipulation of proxy
|
||||||
|
* to target relation need to be guarded by a lock.
|
||||||
*/
|
*/
|
||||||
class GSI_PUBLIC Proxy
|
class GSI_PUBLIC Proxy
|
||||||
: public tl::Object
|
: public tl::Object
|
||||||
|
|
@ -228,8 +240,11 @@ private:
|
||||||
bool m_const_ref : 1;
|
bool m_const_ref : 1;
|
||||||
bool m_destroyed : 1;
|
bool m_destroyed : 1;
|
||||||
bool m_can_destroy : 1;
|
bool m_can_destroy : 1;
|
||||||
|
static QMutex m_lock;
|
||||||
|
|
||||||
|
void set_internal (void *obj, bool owned, bool const_ref, bool can_destroy);
|
||||||
void object_status_changed (gsi::ObjectBase::StatusEventType type);
|
void object_status_changed (gsi::ObjectBase::StatusEventType type);
|
||||||
|
void detach_internal ();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue