summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/clientapi.cpp1
-rw-r--r--common/clientapi.h196
-rw-r--r--common/resultprovider.h221
3 files changed, 223 insertions, 195 deletions
diff --git a/common/clientapi.cpp b/common/clientapi.cpp
index 5cb3b69..3350927 100644
--- a/common/clientapi.cpp
+++ b/common/clientapi.cpp
@@ -3,6 +3,7 @@
3#include "resourceaccess.h" 3#include "resourceaccess.h"
4#include "commands.h" 4#include "commands.h"
5#include "log.h" 5#include "log.h"
6#include <QtConcurrent/QtConcurrentRun>
6 7
7namespace async 8namespace async
8{ 9{
diff --git a/common/clientapi.h b/common/clientapi.h
index 48177df..56247a7 100644
--- a/common/clientapi.h
+++ b/common/clientapi.h
@@ -24,212 +24,18 @@
24#include <QSet> 24#include <QSet>
25#include <QSharedPointer> 25#include <QSharedPointer>
26#include <QStandardPaths> 26#include <QStandardPaths>
27#include <QTimer>
28#include <QDebug> 27#include <QDebug>
29#include <QEventLoop> 28#include <QEventLoop>
30#include <QtConcurrent/QtConcurrentRun>
31#include <functional> 29#include <functional>
32#include <memory> 30#include <memory>
33#include "threadboundary.h" 31#include "threadboundary.h"
34#include "async/src/async.h" 32#include "async/src/async.h"
33#include "resultprovider.h"
35 34
36namespace async { 35namespace async {
37 //This should abstract if we execute from eventloop or in thread. 36 //This should abstract if we execute from eventloop or in thread.
38 //It supposed to allow the caller to finish the current method before executing the runner. 37 //It supposed to allow the caller to finish the current method before executing the runner.
39 void run(const std::function<void()> &runner); 38 void run(const std::function<void()> &runner);
40
41 /**
42 * Query result set
43 */
44
45 template<class T>
46 class ResultEmitter;
47
48 /*
49 * The promise side for the result emitter
50 */
51 template<class T>
52 class ResultProvider {
53 private:
54 void callInMainThreadOnEmitter(void (ResultEmitter<T>::*f)())
55 {
56 //We use the eventloop to call the addHandler directly from the main eventloop.
57 //That way the result emitter implementation doesn't have to care about threadsafety at all.
58 //The alternative would be to make all handlers of the emitter threadsafe.
59 if (auto emitter = mResultEmitter.toStrongRef()) {
60 auto weakEmitter = mResultEmitter;
61 //We don't want to keep the emitter alive here, so we only capture a weak reference
62 emitter->mThreadBoundary.callInMainThread([weakEmitter, f]() {
63 if (auto strongRef = weakEmitter.toStrongRef()) {
64 (strongRef.data()->*f)();
65 }
66 });
67 }
68 }
69
70 void callInMainThreadOnEmitter(const std::function<void()> &f)
71 {
72 //We use the eventloop to call the addHandler directly from the main eventloop.
73 //That way the result emitter implementation doesn't have to care about threadsafety at all.
74 //The alternative would be to make all handlers of the emitter threadsafe.
75 if (auto emitter = mResultEmitter.toStrongRef()) {
76 emitter->mThreadBoundary.callInMainThread([f]() {
77 f();
78 });
79 }
80 }
81
82 public:
83 //Called from worker thread
84 void add(const T &value)
85 {
86 //Because I don't know how to use bind
87 auto weakEmitter = mResultEmitter;
88 callInMainThreadOnEmitter([weakEmitter, value](){
89 if (auto strongRef = weakEmitter.toStrongRef()) {
90 strongRef->addHandler(value);
91 }
92 });
93 }
94
95 void initialResultSetComplete()
96 {
97 callInMainThreadOnEmitter(&ResultEmitter<T>::initialResultSetComplete);
98 }
99
100 //Called from worker thread
101 void complete()
102 {
103 callInMainThreadOnEmitter(&ResultEmitter<T>::complete);
104 }
105
106 void clear()
107 {
108 callInMainThreadOnEmitter(&ResultEmitter<T>::clear);
109 }
110
111
112 QSharedPointer<ResultEmitter<T> > emitter()
113 {
114 if (!mResultEmitter) {
115 //We have to go over a separate var and return that, otherwise we'd delete the emitter immediately again
116 auto sharedPtr = QSharedPointer<ResultEmitter<T> >(new ResultEmitter<T>, [this](ResultEmitter<T> *emitter){ done(); delete emitter; });
117 mResultEmitter = sharedPtr;
118 return sharedPtr;
119 }
120
121 return mResultEmitter.toStrongRef();
122 }
123
124 /**
125 * For lifetimemanagement only.
126 * We keep the runner alive as long as the result provider exists.
127 */
128 void setQueryRunner(const QSharedPointer<QObject> &runner)
129 {
130 mQueryRunner = runner;
131 }
132
133 /**
134 * For lifetimemanagement only.
135 * We keep the runner alive as long as the result provider exists.
136 */
137 void setFacade(const std::shared_ptr<void> &facade)
138 {
139 mFacade = facade;
140 }
141
142 void onDone(const std::function<void()> &callback)
143 {
144 mOnDoneCallback = callback;
145 }
146
147 bool isDone() const
148 {
149 //The existance of the emitter currently defines wether we're done or not.
150 return mResultEmitter.toStrongRef().isNull();
151 }
152
153 private:
154 void done()
155 {
156 qWarning() << "done";
157 if (mOnDoneCallback) {
158 mOnDoneCallback();
159 mOnDoneCallback = std::function<void()>();
160 }
161 }
162
163 QWeakPointer<ResultEmitter<T> > mResultEmitter;
164 QSharedPointer<QObject> mQueryRunner;
165 std::shared_ptr<void> mFacade;
166 std::function<void()> mOnDoneCallback;
167 };
168
169 /*
170 * The future side for the client.
171 *
172 * It does not directly hold the state.
173 *
174 * The advantage of this is that we can specialize it to:
175 * * do inline transformations to the data
176 * * directly store the state in a suitable datastructure: QList, QSet, std::list, QVector, ...
177 * * build async interfaces with signals
178 * * build sync interfaces that block when accessing the value
179 *
180 */
181 template<class DomainType>
182 class ResultEmitter {
183 public:
184 void onAdded(const std::function<void(const DomainType&)> &handler)
185 {
186 addHandler = handler;
187 }
188 // void onRemoved(const std::function<void(const T&)> &handler);
189 void onInitialResultSetComplete(const std::function<void(void)> &handler)
190 {
191 initialResultSetCompleteHandler = handler;
192 }
193 void onComplete(const std::function<void(void)> &handler)
194 {
195 completeHandler = handler;
196 }
197 void onClear(const std::function<void(void)> &handler)
198 {
199 clearHandler = handler;
200 }
201
202 void add(const DomainType &value)
203 {
204 addHandler(value);
205 }
206
207 void initialResultSetComplete()
208 {
209 initialResultSetCompleteHandler();
210 }
211
212 void complete()
213 {
214 completeHandler();
215 }
216
217 void clear()
218 {
219 clearHandler();
220 }
221
222 private:
223 friend class ResultProvider<DomainType>;
224
225 std::function<void(const DomainType&)> addHandler;
226 // std::function<void(const T&)> removeHandler;
227 std::function<void(void)> initialResultSetCompleteHandler;
228 std::function<void(void)> completeHandler;
229 std::function<void(void)> clearHandler;
230 ThreadBoundary mThreadBoundary;
231 };
232
233} 39}
234 40
235namespace Akonadi2 { 41namespace Akonadi2 {
diff --git a/common/resultprovider.h b/common/resultprovider.h
new file mode 100644
index 0000000..4d985d9
--- /dev/null
+++ b/common/resultprovider.h
@@ -0,0 +1,221 @@
1/*
2 * Copyright (C) 2014 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) version 3, or any
8 * later version accepted by the membership of KDE e.V. (or its
9 * successor approved by the membership of KDE e.V.), which shall
10 * act as a proxy defined in Section 6 of version 3 of the license.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#pragma once
22
23#include <functional>
24#include <memory>
25#include "threadboundary.h"
26
27namespace async {
28
29/**
30* Query result set
31*/
32template<class T>
33class ResultEmitter;
34
35/*
36* The promise side for the result emitter
37*/
38template<class T>
39class ResultProvider {
40private:
41 void callInMainThreadOnEmitter(void (ResultEmitter<T>::*f)())
42 {
43 //We use the eventloop to call the addHandler directly from the main eventloop.
44 //That way the result emitter implementation doesn't have to care about threadsafety at all.
45 //The alternative would be to make all handlers of the emitter threadsafe.
46 if (auto emitter = mResultEmitter.toStrongRef()) {
47 auto weakEmitter = mResultEmitter;
48 //We don't want to keep the emitter alive here, so we only capture a weak reference
49 emitter->mThreadBoundary.callInMainThread([weakEmitter, f]() {
50 if (auto strongRef = weakEmitter.toStrongRef()) {
51 (strongRef.data()->*f)();
52 }
53 });
54 }
55 }
56
57 void callInMainThreadOnEmitter(const std::function<void()> &f)
58 {
59 //We use the eventloop to call the addHandler directly from the main eventloop.
60 //That way the result emitter implementation doesn't have to care about threadsafety at all.
61 //The alternative would be to make all handlers of the emitter threadsafe.
62 if (auto emitter = mResultEmitter.toStrongRef()) {
63 emitter->mThreadBoundary.callInMainThread([f]() {
64 f();
65 });
66 }
67 }
68
69public:
70 //Called from worker thread
71 void add(const T &value)
72 {
73 //Because I don't know how to use bind
74 auto weakEmitter = mResultEmitter;
75 callInMainThreadOnEmitter([weakEmitter, value](){
76 if (auto strongRef = weakEmitter.toStrongRef()) {
77 strongRef->addHandler(value);
78 }
79 });
80 }
81
82 void initialResultSetComplete()
83 {
84 callInMainThreadOnEmitter(&ResultEmitter<T>::initialResultSetComplete);
85 }
86
87 //Called from worker thread
88 void complete()
89 {
90 callInMainThreadOnEmitter(&ResultEmitter<T>::complete);
91 }
92
93 void clear()
94 {
95 callInMainThreadOnEmitter(&ResultEmitter<T>::clear);
96 }
97
98
99 QSharedPointer<ResultEmitter<T> > emitter()
100 {
101 if (!mResultEmitter) {
102 //We have to go over a separate var and return that, otherwise we'd delete the emitter immediately again
103 auto sharedPtr = QSharedPointer<ResultEmitter<T> >(new ResultEmitter<T>, [this](ResultEmitter<T> *emitter){ done(); delete emitter; });
104 mResultEmitter = sharedPtr;
105 return sharedPtr;
106 }
107
108 return mResultEmitter.toStrongRef();
109 }
110
111 /**
112 * For lifetimemanagement only.
113 * We keep the runner alive as long as the result provider exists.
114 */
115 void setQueryRunner(const QSharedPointer<QObject> &runner)
116 {
117 mQueryRunner = runner;
118 }
119
120 /**
121 * For lifetimemanagement only.
122 * We keep the runner alive as long as the result provider exists.
123 */
124 void setFacade(const std::shared_ptr<void> &facade)
125 {
126 mFacade = facade;
127 }
128
129 void onDone(const std::function<void()> &callback)
130 {
131 mOnDoneCallback = callback;
132 }
133
134 bool isDone() const
135 {
136 //The existance of the emitter currently defines wether we're done or not.
137 return mResultEmitter.toStrongRef().isNull();
138 }
139
140private:
141 void done()
142 {
143 qWarning() << "done";
144 if (mOnDoneCallback) {
145 mOnDoneCallback();
146 mOnDoneCallback = std::function<void()>();
147 }
148 }
149
150 QWeakPointer<ResultEmitter<T> > mResultEmitter;
151 QSharedPointer<QObject> mQueryRunner;
152 std::shared_ptr<void> mFacade;
153 std::function<void()> mOnDoneCallback;
154};
155
156/*
157* The future side for the client.
158*
159* It does not directly hold the state.
160*
161* The advantage of this is that we can specialize it to:
162* * do inline transformations to the data
163* * directly store the state in a suitable datastructure: QList, QSet, std::list, QVector, ...
164* * build async interfaces with signals
165* * build sync interfaces that block when accessing the value
166*
167*/
168template<class DomainType>
169class ResultEmitter {
170public:
171 void onAdded(const std::function<void(const DomainType&)> &handler)
172 {
173 addHandler = handler;
174 }
175 // void onRemoved(const std::function<void(const T&)> &handler);
176 void onInitialResultSetComplete(const std::function<void(void)> &handler)
177 {
178 initialResultSetCompleteHandler = handler;
179 }
180 void onComplete(const std::function<void(void)> &handler)
181 {
182 completeHandler = handler;
183 }
184 void onClear(const std::function<void(void)> &handler)
185 {
186 clearHandler = handler;
187 }
188
189 void add(const DomainType &value)
190 {
191 addHandler(value);
192 }
193
194 void initialResultSetComplete()
195 {
196 initialResultSetCompleteHandler();
197 }
198
199 void complete()
200 {
201 completeHandler();
202 }
203
204 void clear()
205 {
206 clearHandler();
207 }
208
209private:
210 friend class ResultProvider<DomainType>;
211
212 std::function<void(const DomainType&)> addHandler;
213 // std::function<void(const T&)> removeHandler;
214 std::function<void(void)> initialResultSetCompleteHandler;
215 std::function<void(void)> completeHandler;
216 std::function<void(void)> clearHandler;
217 ThreadBoundary mThreadBoundary;
218};
219
220}
221