Skip to content
This repository has been archived by the owner on Sep 4, 2021. It is now read-only.

Commit

Permalink
[mt] Generic actor implementation around QThread
Browse files Browse the repository at this point in the history
Actor creates QObject inside QThread and allows to post/send events to
it, protecting it from incorrect direct access.


Signed-off-by: Denis Zalevskiy <denis.zalevskiy@jolla.com>
  • Loading branch information
Denis Zalevskiy committed Nov 6, 2014
1 parent 29a659f commit 28e8e37
Show file tree
Hide file tree
Showing 8 changed files with 355 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CMakeLists.txt
Expand Up @@ -58,6 +58,10 @@ set(QTAROUND_DBUS_MOC_HEADERS
${CMAKE_SOURCE_DIR}/include/qtaround/dbus.hpp
)

set(QTAROUND_MOC_HEADERS
${CMAKE_SOURCE_DIR}/include/qtaround/mt.hpp
)

add_subdirectory(src)
add_subdirectory(tests)

Expand Down
85 changes: 85 additions & 0 deletions include/qtaround/mt.hpp
@@ -0,0 +1,85 @@
#ifndef _QTAROUND_MT_HPP_
#define _QTAROUND_MT_HPP_
/**
* @file mt.hpp
* @brief Concurrency support
* @copyright (C) 2014 Jolla Ltd.
* @par License: LGPL 2.1 http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
*/

#include <qtaround/util.hpp>
#include <QThread>
#include <QCoreApplication>

namespace qtaround { namespace mt {

class AnyActor;
typedef std::shared_ptr<AnyActor> AnyActorHandle;

class ActorContext : public QObject
{
Q_OBJECT
public:
typedef std::function<UNIQUE_PTR(QObject) ()> ctor_type;
typedef std::function<void (AnyActorHandle)>callback_type;
ActorContext(AnyActorHandle actor, ctor_type ctor, callback_type cb)
: actor_(actor), ctor_(ctor), notify_(cb)
{}

AnyActorHandle actor_;
ctor_type ctor_;
callback_type notify_;
};

class AnyActor : public QThread
{
Q_OBJECT
protected:
virtual ~AnyActor();

void run() Q_DECL_OVERRIDE;

public:
AnyActor(QObject *parent) : QThread(parent) {}

AnyActor(AnyActor const&) = delete;
AnyActor& operator = (AnyActor const&) = delete;

static void create(ActorContext::ctor_type
, ActorContext::callback_type
, QObject *parent = nullptr);

static AnyActorHandle createSync
(ActorContext::ctor_type, QObject *parent = nullptr);

bool postEvent(QEvent *);
bool sendEvent(QEvent *);

private:
std::shared_ptr<QObject> obj_;
};

template <typename T> void startActor
(std::function<UNIQUE_PTR(T) ()> ctor
, ActorContext::callback_type cb, QObject *parent = nullptr
, typename std::enable_if<std::is_convertible<T*, QObject*>::value>::type* = 0)
{
auto qobj_ctor = [ctor]() {
return static_cast_qobject_unique<QObject>(ctor());
};
AnyActor::create(qobj_ctor, cb, parent);
}

template <typename T> AnyActorHandle startActorSync
(std::function<UNIQUE_PTR(T) ()> ctor, QObject *parent = nullptr
, typename std::enable_if<std::is_convertible<T*, QObject*>::value>::type* = 0)
{
auto qobj_ctor = [ctor]() {
return static_cast_qobject_unique<QObject>(ctor());
};
return AnyActor::createSync(qobj_ctor, parent);
}

}}

#endif // _QTAROUND_MT_HPP_
33 changes: 33 additions & 0 deletions include/qtaround/util.hpp
Expand Up @@ -333,6 +333,24 @@ UNIQUE_PTR(T) make_qobject_unique(Args &&...args)
, get_qobject_deleter<T>());
}

template <typename T>
UNIQUE_PTR(T) acquire_qobject_unique(T *p)
{
return UNIQUE_PTR(T)(p, get_qobject_deleter<T>());
}

template <typename T>
UNIQUE_PTR(T) null_qobject_unique()
{
return acquire_qobject_unique<T>(nullptr);
}

template <typename To, typename From>
UNIQUE_PTR(To) static_cast_qobject_unique(UNIQUE_PTR(From) from)
{
return acquire_qobject_unique<To>(static_cast<To*>(from.release()));
}

template <typename T, typename ... Args>
std::shared_ptr<T> make_qobject_shared(Args &&...args)
{
Expand All @@ -353,6 +371,21 @@ std::shared_ptr<T> qobject_shared(UNIQUE_PTR(T) p)
return std::shared_ptr<T>(p.release(), get_qobject_deleter<T>());
}

template <typename T>
std::shared_ptr<QObject> qobject_shared_cast
(UNIQUE_PTR(T) p, typename std::enable_if
<std::is_convertible<T*, QObject*>::value>::type* = 0)
{
return qobject_shared(static_cast<QObject*>(p.release()));
}

template <typename To, typename From>
std::shared_ptr<To> static_cast_qobject_shared(UNIQUE_PTR(From) from)
{
auto p = static_cast_qobject_unique<To>(from);
return qobject_shared(std::move(p));
}

template <typename T>
UNIQUE_PTR(T) qobject_box(T &&v)
{
Expand Down
1 change: 1 addition & 0 deletions rpm/qtaround.spec
Expand Up @@ -81,6 +81,7 @@ rm -rf $RPM_BUILD_ROOT
%{_includedir}/qtaround/subprocess.hpp
%{_includedir}/qtaround/sys.hpp
%{_includedir}/qtaround/util.hpp
%{_includedir}/qtaround/mt.hpp

%files dbus
%defattr(-,root,root,-)
Expand Down
4 changes: 4 additions & 0 deletions src/CMakeLists.txt
@@ -1,7 +1,11 @@
set(CMAKE_AUTOMOC TRUE)

qt5_wrap_cpp(QTAROUND_MOC_SRC ${QTAROUND_MOC_HEADERS})

add_library(qtaround SHARED
${QTAROUND_MOC_SRC}
debug.cpp os.cpp json.cpp sys.cpp subprocess.cpp util.cpp
mt.cpp
)
qt5_use_modules(qtaround Core)
target_link_libraries(qtaround ${COR_LIBRARIES})
Expand Down
90 changes: 90 additions & 0 deletions src/mt.cpp
@@ -0,0 +1,90 @@
/**
* @file mt.cpp
* @brief Concurrency support
* @copyright (C) 2014 Jolla Ltd.
* @par License: LGPL 2.1 http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
*/

#include <qtaround/debug.hpp>
#include <qtaround/mt.hpp>
#include <mutex>
#include <condition_variable>

namespace qtaround { namespace mt {

AnyActor::~AnyActor()
{
auto app = QCoreApplication::instance();
if (app) {
if (isRunning())
quit();
if (QThread::currentThread() != this)
if (!wait(10000))
debug::warning("Timeout: no quit from thread!");
}
if (obj_ && this != QThread::currentThread())
debug::warning("Managed object is not deleted in a right thread Current:"
, QThread::currentThread(), ", Need:", this);
}

void AnyActor::run()
{
auto ctx = std::static_pointer_cast<ActorContext>(std::move(obj_));
obj_ = ctx->ctor_();
ctx->notify_(std::move(ctx->actor_));
exec();
obj_.reset();
}

bool AnyActor::postEvent(QEvent *e)
{
auto obj = obj_;

if (obj) {
QCoreApplication::postEvent(obj.get(), e);
return true;
} else {
if (e) delete e;
return false;
}
}

bool AnyActor::sendEvent(QEvent *e)
{
auto obj = obj_;
if (obj) {
return QCoreApplication::sendEvent(obj.get(), e);
} else {
if (e) delete e;
return false;
}
}

void AnyActor::create
(ActorContext::ctor_type ctor, ActorContext::callback_type cb
, QObject *parent)
{
auto self = make_qobject_shared<AnyActor>(parent);
auto ctx = make_qobject_unique<ActorContext>
(self, std::move(ctor), std::move(cb));
self->obj_ = qobject_shared_cast(std::move(ctx));
self->start();
}

AnyActorHandle AnyActor::createSync
(ActorContext::ctor_type ctor, QObject *parent)
{
std::mutex mutex;
std::condition_variable cond;
AnyActorHandle result;
std::unique_lock<std::mutex> l(mutex);
create(ctor, [&](mt::AnyActorHandle p) {
std::unique_lock<std::mutex> l(mutex);
result = p;
cond.notify_all();
}, parent);
cond.wait(l, [&result]() { return !!result; });
return result;
}

}}
2 changes: 1 addition & 1 deletion tests/CMakeLists.txt
Expand Up @@ -4,7 +4,7 @@ include_directories(${TUT_INCLUDES})
find_package(Qt5Core REQUIRED)
set(CMAKE_AUTOMOC TRUE)

set(UNIT_TESTS os dbus misc)
set(UNIT_TESTS os dbus misc mt)
set(TESTS_DIR /opt/tests/qtaround)

MACRO(UNIT_TEST _name)
Expand Down

0 comments on commit 28e8e37

Please sign in to comment.