diff options
Diffstat (limited to 'client/clientapi.h')
-rw-r--r-- | client/clientapi.h | 46 |
1 files changed, 30 insertions, 16 deletions
diff --git a/client/clientapi.h b/client/clientapi.h index 662f96c..a0020af 100644 --- a/client/clientapi.h +++ b/client/clientapi.h | |||
@@ -7,48 +7,60 @@ | |||
7 | #include <QTimer> | 7 | #include <QTimer> |
8 | #include <QDebug> | 8 | #include <QDebug> |
9 | #include <QEventLoop> | 9 | #include <QEventLoop> |
10 | #include <QtConcurrent/QtConcurrentRun> | ||
10 | #include <functional> | 11 | #include <functional> |
12 | #include "threadboundary.h" | ||
11 | 13 | ||
12 | namespace async { | 14 | namespace async { |
13 | //This should abstract if we execute from eventloop or in thread. | 15 | //This should abstract if we execute from eventloop or in thread. |
14 | //It supposed to allow the caller to finish the current method before executing the runner. | 16 | //It supposed to allow the caller to finish the current method before executing the runner. |
15 | void run(const std::function<void()> &runner) { | 17 | void run(const std::function<void()> &runner) { |
16 | //FIXME we should be using a Job instead of a timer | 18 | QtConcurrent::run(runner); |
17 | auto timer = new QTimer; | 19 | |
18 | timer->setSingleShot(true); | 20 | // //FIXME we should be using a Job instead of a timer |
19 | QObject::connect(timer, &QTimer::timeout, runner); | 21 | // auto timer = new QTimer; |
20 | QObject::connect(timer, &QTimer::timeout, timer, &QObject::deleteLater); | 22 | // timer->setSingleShot(true); |
21 | timer->start(0); | 23 | // QObject::connect(timer, &QTimer::timeout, runner); |
24 | // QObject::connect(timer, &QTimer::timeout, timer, &QObject::deleteLater); | ||
25 | // timer->start(0); | ||
22 | }; | 26 | }; |
23 | 27 | ||
24 | /** | 28 | /** |
25 | * Query result set | 29 | * Query result set |
26 | * | ||
27 | * This should probably become part of a generic kasync library. | ||
28 | * | ||
29 | * Functional is nice because we don't have to store data in the emitter | ||
30 | * Non functional and storing may be the right thing because we want an in-memory representation of the set | ||
31 | * non-functional also allows us to batch move data across thread boundaries. | ||
32 | */ | 30 | */ |
33 | 31 | ||
34 | template<class T> | 32 | template<class T> |
35 | class ResultEmitter; | 33 | class ResultEmitter; |
36 | 34 | ||
37 | /* | 35 | /* |
38 | * The promise side for the result provider | 36 | * The promise side for the result emitter |
39 | */ | 37 | */ |
40 | template<class T> | 38 | template<class T> |
41 | class ResultProvider { | 39 | class ResultProvider { |
42 | public: | 40 | public: |
41 | //Called from worker thread | ||
43 | void add(const T &value) | 42 | void add(const T &value) |
44 | { | 43 | { |
45 | //the handler will be called in the other thread, protect | 44 | //We use the eventloop to call the addHandler directly from the main eventloop. |
46 | mResultEmitter->addHandler(value); | 45 | //That way the result emitter implementation doesn't have to care about threadsafety at all. |
46 | //The alternative would be to make all handlers of the emitter threadsafe. | ||
47 | auto emitter = mResultEmitter; | ||
48 | mResultEmitter->mThreadBoundary.callInMainThread([emitter, value]() { | ||
49 | if (emitter) { | ||
50 | emitter->addHandler(value); | ||
51 | } | ||
52 | }); | ||
47 | } | 53 | } |
48 | 54 | ||
55 | //Called from worker thread | ||
49 | void complete() | 56 | void complete() |
50 | { | 57 | { |
51 | mResultEmitter->completeHandler(); | 58 | auto emitter = mResultEmitter; |
59 | mResultEmitter->mThreadBoundary.callInMainThread([emitter]() { | ||
60 | if (emitter) { | ||
61 | emitter->completeHandler(); | ||
62 | } | ||
63 | }); | ||
52 | } | 64 | } |
53 | 65 | ||
54 | QSharedPointer<ResultEmitter<T> > emitter() | 66 | QSharedPointer<ResultEmitter<T> > emitter() |
@@ -92,6 +104,7 @@ namespace async { | |||
92 | std::function<void(const DomainType&)> addHandler; | 104 | std::function<void(const DomainType&)> addHandler; |
93 | // std::function<void(const T&)> removeHandler; | 105 | // std::function<void(const T&)> removeHandler; |
94 | std::function<void(void)> completeHandler; | 106 | std::function<void(void)> completeHandler; |
107 | ThreadBoundary mThreadBoundary; | ||
95 | }; | 108 | }; |
96 | 109 | ||
97 | 110 | ||
@@ -348,6 +361,7 @@ public: | |||
348 | async::run([resultSet, query](){ | 361 | async::run([resultSet, query](){ |
349 | // Query all resources and aggregate results | 362 | // Query all resources and aggregate results |
350 | // query tells us in which resources we're interested | 363 | // query tells us in which resources we're interested |
364 | // TODO: queries to individual resources could be parallelized | ||
351 | for(const QString &resource : query.resources) { | 365 | for(const QString &resource : query.resources) { |
352 | auto facade = FacadeFactory::instance().getFacade<DomainType>(resource); | 366 | auto facade = FacadeFactory::instance().getFacade<DomainType>(resource); |
353 | //We have to bind an instance to the function callback. Since we use a shared pointer this keeps the result provider instance (and thus also the emitter) alive. | 367 | //We have to bind an instance to the function callback. Since we use a shared pointer this keeps the result provider instance (and thus also the emitter) alive. |