summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt5
-rw-r--r--async/CMakeLists.txt2
-rw-r--r--async/autotests/CMakeLists.txt5
-rw-r--r--async/autotests/asynctest.cpp140
-rw-r--r--async/src/CMakeLists.txt6
-rw-r--r--async/src/async.cpp98
-rw-r--r--async/src/async.h337
-rw-r--r--async/src/async_impl.h51
-rw-r--r--async/src/future.h92
-rw-r--r--client/clientapi.h45
-rw-r--r--client/test/clientapitest.cpp12
-rw-r--r--common/storage.h1
-rw-r--r--tests/hawd/module.h2
13 files changed, 786 insertions, 10 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c5b2eb4..3f86163 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -36,7 +36,7 @@ endfunction(generate_flatbuffers)
36 36
37set(CMAKE_AUTOMOC ON) 37set(CMAKE_AUTOMOC ON)
38add_definitions("-Wall -std=c++0x") 38add_definitions("-Wall -std=c++0x")
39include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}) 39include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${FLATBUFFERS_INCLUDE_DIR})
40 40
41configure_file(hawd.conf hawd.conf) 41configure_file(hawd.conf hawd.conf)
42 42
@@ -55,4 +55,7 @@ add_subdirectory(dummyresource)
55# some tests 55# some tests
56add_subdirectory(tests) 56add_subdirectory(tests)
57 57
58# async library prototype
59add_subdirectory(async)
60
58feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) 61feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/async/CMakeLists.txt b/async/CMakeLists.txt
new file mode 100644
index 0000000..19ac407
--- /dev/null
+++ b/async/CMakeLists.txt
@@ -0,0 +1,2 @@
1add_subdirectory(src)
2add_subdirectory(autotests) \ No newline at end of file
diff --git a/async/autotests/CMakeLists.txt b/async/autotests/CMakeLists.txt
new file mode 100644
index 0000000..a2bedc8
--- /dev/null
+++ b/async/autotests/CMakeLists.txt
@@ -0,0 +1,5 @@
1include_directories(../src ${CMAKE_CURRENT_BINARY_DIR})
2
3add_executable(asynctest asynctest.cpp)
4qt5_use_modules(asynctest Test)
5target_link_libraries(asynctest akonadi2async Qt5::Core Qt5::Test) \ No newline at end of file
diff --git a/async/autotests/asynctest.cpp b/async/autotests/asynctest.cpp
new file mode 100644
index 0000000..7aedfc4
--- /dev/null
+++ b/async/autotests/asynctest.cpp
@@ -0,0 +1,140 @@
1/*
2 * Copyright 2014 Daniel Vrátil <dvratil@redhat.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License or (at your option) version 3 or any later version
8 * accepted by the membership of KDE e.V. (or its successor approved
9 * by the membership of KDE e.V.), which shall act as a proxy
10 * defined in Section 14 of version 3 of the license.
11 *
12 * This program 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
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#include "../src/async.h"
23
24#include <QObject>
25#include <QString>
26#include <QTimer>
27#include <QtTest/QTest>
28
29class AsyncTest : public QObject
30{
31 Q_OBJECT
32
33public:
34 AsyncTest()
35 {}
36
37 ~AsyncTest()
38 {}
39
40private Q_SLOTS:
41 void testSyncPromises();
42 void testAsyncPromises();
43 void testSyncEach();
44 void testSyncReduce();
45};
46
47void AsyncTest::testSyncPromises()
48{
49 auto baseJob = Async::start<int>(
50 [](Async::Future<int> &f) {
51 f.setValue(42);
52 f.setFinished();
53 })
54 .then<QString, int>(
55 [](int v, Async::Future<QString> &f) {
56 f.setValue("Result is " + QString::number(v));
57 f.setFinished();
58 });
59
60 auto job = baseJob.then<QString, QString>(
61 [](const QString &v, Async::Future<QString> &f) {
62 f.setValue(v.toUpper());
63 f.setFinished();
64 });
65
66 job.exec();
67 Async::Future<QString> future = job.result();
68
69 QVERIFY(future.isFinished());
70 QCOMPARE(future.value(), QString::fromLatin1("RESULT IS 42"));
71}
72
73void AsyncTest::testAsyncPromises()
74{
75 auto job = Async::start<int>(
76 [](Async::Future<int> &future) {
77 QTimer *timer = new QTimer();
78 QObject::connect(timer, &QTimer::timeout,
79 [&]() {
80 future.setValue(42);
81 future.setFinished();
82 });
83 QObject::connect(timer, &QTimer::timeout,
84 timer, &QObject::deleteLater);
85 timer->setSingleShot(true);
86 timer->start(200);
87 });
88
89 job.exec();
90 Async::Future<int> future = job.result();
91 QVERIFY(future.isFinished());
92 QCOMPARE(future.value(), 42);
93}
94
95void AsyncTest::testSyncEach()
96{
97 auto job = Async::start<QList<int>>(
98 [](Async::Future<QList<int>> &future) {
99 future.setValue(QList<int>{ 1, 2, 3, 4 });
100 future.setFinished();
101 })
102 .each<QList<int>, int>(
103 [](const int &v, Async::Future<QList<int>> &future) {
104 future.setValue(QList<int>{ v + 1 });
105 future.setFinished();
106 });
107
108 job.exec();
109 Async::Future<QList<int>> future = job.result();
110 const QList<int> expected({ 2, 3, 4, 5 });
111 QVERIFY(future.isFinished());
112 QCOMPARE(future.value(), expected);
113}
114
115void AsyncTest::testSyncReduce()
116{
117 auto job = Async::start<QList<int>>(
118 [](Async::Future<QList<int>> &future) {
119 future.setValue(QList<int>{ 1, 2, 3, 4 });
120 future.setFinished();
121 })
122 .reduce<int, QList<int>>(
123 [](const QList<int> &list, Async::Future<int> &future) {
124 int sum = 0;
125 for (int i : list) sum += i;
126 future.setValue(sum);
127 future.setFinished();
128 });
129
130 job.exec();
131 Async::Future<int> future = job.result();
132 QVERIFY(future.isFinished());
133 QCOMPARE(future.value(), 10);
134}
135
136
137
138QTEST_MAIN(AsyncTest);
139
140#include "asynctest.moc"
diff --git a/async/src/CMakeLists.txt b/async/src/CMakeLists.txt
new file mode 100644
index 0000000..a98d8ce
--- /dev/null
+++ b/async/src/CMakeLists.txt
@@ -0,0 +1,6 @@
1set(async_SRCS
2 async.cpp
3)
4
5add_library(akonadi2async SHARED ${async_SRCS})
6target_link_libraries(akonadi2async Qt5::Core)
diff --git a/async/src/async.cpp b/async/src/async.cpp
new file mode 100644
index 0000000..0b8d7f3
--- /dev/null
+++ b/async/src/async.cpp
@@ -0,0 +1,98 @@
1/*
2 * Copyright 2014 Daniel Vrátil <dvratil@redhat.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License or (at your option) version 3 or any later version
8 * accepted by the membership of KDE e.V. (or its successor approved
9 * by the membership of KDE e.V.), which shall act as a proxy
10 * defined in Section 14 of version 3 of the license.
11 *
12 * This program 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
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#include "async.h"
23
24#include <QCoreApplication>
25#include <QDebug>
26#include <QEventLoop>
27
28
29using namespace Async;
30
31Private::ExecutorBase::ExecutorBase(ExecutorBase* parent)
32 : mPrev(parent)
33 , mResult(0)
34{
35}
36
37Private::ExecutorBase::~ExecutorBase()
38{
39 delete mResult;
40}
41
42
43JobBase::JobBase(Private::ExecutorBase *executor)
44 : mExecutor(executor)
45{
46}
47
48JobBase::~JobBase()
49{
50}
51
52void JobBase::exec()
53{
54 mExecutor->exec();
55}
56
57
58FutureBase::FutureBase()
59 : mFinished(false)
60 , mWaitLoop(nullptr)
61{
62}
63
64FutureBase::FutureBase(const Async::FutureBase &other)
65 : mFinished(other.mFinished)
66 , mWaitLoop(other.mWaitLoop)
67{
68}
69
70FutureBase::~FutureBase()
71{
72}
73
74void FutureBase::setFinished()
75{
76 mFinished = true;
77 if (mWaitLoop && mWaitLoop->isRunning()) {
78 mWaitLoop->quit();
79 }
80}
81
82bool FutureBase::isFinished() const
83{
84 return mFinished;
85}
86
87void FutureBase::waitForFinished()
88{
89 if (mFinished) {
90 return;
91 }
92
93 mWaitLoop = new QEventLoop;
94 mWaitLoop->exec(QEventLoop::ExcludeUserInputEvents);
95 delete mWaitLoop;
96 mWaitLoop = 0;
97}
98
diff --git a/async/src/async.h b/async/src/async.h
new file mode 100644
index 0000000..0e4f246
--- /dev/null
+++ b/async/src/async.h
@@ -0,0 +1,337 @@
1/*
2 * Copyright 2014 Daniel Vrátil <dvratil@redhat.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License or (at your option) version 3 or any later version
8 * accepted by the membership of KDE e.V. (or its successor approved
9 * by the membership of KDE e.V.), which shall act as a proxy
10 * defined in Section 14 of version 3 of the license.
11 *
12 * This program 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
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#ifndef ASYNC_H
23#define ASYNC_H
24
25#include <functional>
26#include <list>
27#include <type_traits>
28#include <iostream>
29#include <cassert>
30#include <iterator>
31#include <boost/graph/graph_concepts.hpp>
32
33#include "future.h"
34#include "async_impl.h"
35
36
37namespace Async {
38
39template<typename PrevOut, typename Out, typename ... In>
40class Executor;
41
42class JobBase;
43
44template<typename Out, typename ... In>
45class Job;
46
47template<typename Out, typename ... In>
48using ThenTask = typename detail::identity<std::function<void(In ..., Async::Future<Out>&)>>::type;
49template<typename Out, typename In>
50using EachTask = typename detail::identity<std::function<void(In, Async::Future<Out>&)>>::type;
51template<typename Out, typename In>
52using ReduceTask = typename detail::identity<std::function<void(In, Async::Future<Out>&)>>::type;
53
54namespace Private
55{
56
57template<typename ... T>
58struct PreviousOut {
59 using type = typename std::tuple_element<0, std::tuple<T ..., void>>::type;
60};
61
62class ExecutorBase
63{
64 template<typename PrevOut, typename Out, typename ... In>
65 friend class Executor;
66
67public:
68 virtual ~ExecutorBase();
69 virtual void exec() = 0;
70
71 inline FutureBase* result() const
72 {
73 return mResult;
74 }
75
76protected:
77 ExecutorBase(ExecutorBase *parent);
78
79 ExecutorBase *mPrev;
80 FutureBase *mResult;
81};
82
83template<typename PrevOut, typename Out, typename ... In>
84class Executor : public ExecutorBase
85{
86protected:
87 Executor(ExecutorBase *parent)
88 : ExecutorBase(parent)
89 {}
90 virtual ~Executor() {}
91 inline Async::Future<PrevOut>* chainup();
92
93 std::function<void(const In& ..., Async::Future<Out> &)> mFunc;
94};
95
96template<typename Out, typename ... In>
97class ThenExecutor: public Executor<typename PreviousOut<In ...>::type, Out, In ...>
98{
99public:
100 ThenExecutor(ThenTask<Out, In ...> then, ExecutorBase *parent = nullptr);
101 void exec();
102};
103
104template<typename PrevOut, typename Out, typename In>
105class EachExecutor : public Executor<PrevOut, Out, In>
106{
107public:
108 EachExecutor(EachTask<Out, In> each, ExecutorBase *parent);
109 void exec();
110};
111
112template<typename Out, typename In>
113class ReduceExecutor : public Executor<In, Out, In>
114{
115public:
116 ReduceExecutor(ReduceTask<Out, In> reduce, ExecutorBase *parent);
117 void exec();
118};
119
120} // namespace Private
121
122/**
123 * Start an asynchronous job sequence.
124 *
125 * Async::start() is your starting point to build a chain of jobs to be executed
126 * asynchronously.
127 *
128 * @param func An asynchronous function to be executed. The function must have
129 * void return type, and accept exactly one argument of type @p Async::Future<In>,
130 * where @p In is type of the result.
131 */
132template<typename Out>
133Job<Out> start(ThenTask<Out> func);
134
135class JobBase
136{
137 template<typename Out, typename ... In>
138 friend class Job;
139
140public:
141 JobBase(Private::ExecutorBase *executor);
142 ~JobBase();
143
144 void exec();
145
146protected:
147 Private::ExecutorBase *mExecutor;
148};
149
150/**
151 * An Asynchronous job
152 *
153 * A single instance of Job represents a single method that will be executed
154 * asynchrously. The Job is started by @p Job::exec(), which returns @p Async::Future
155 * immediatelly. The Future will be set to finished state once the asynchronous
156 * task has finished. You can use @p Async::Future::waitForFinished() to wait for
157 * for the Future in blocking manner.
158 *
159 * It is possible to chain multiple Jobs one after another in different fashion
160 * (sequential, parallel, etc.). Calling Job::exec() will then return a pending
161 * @p Async::Future, and will execute the entire chain of jobs.
162 *
163 * @code
164 * auto job = Job::start<QList<int>>(
165 * [](Async::Future<QList<int>> &future) {
166 * MyREST::PendingUsers *pu = MyREST::requestListOfUsers();
167 * QObject::connect(pu, &PendingOperation::finished,
168 * [&](PendingOperation *pu) {
169 * future->setValue(dynamic_cast<MyREST::PendingUsers*>(pu)->userIds());
170 * future->setFinished();
171 * });
172 * })
173 * .each<QList<MyREST::User>, int>(
174 * [](const int &userId, Async::Future<QList<MyREST::User>> &future) {
175 * MyREST::PendingUser *pu = MyREST::requestUserDetails(userId);
176 * QObject::connect(pu, &PendingOperation::finished,
177 * [&](PendingOperation *pu) {
178 * future->setValue(Qlist<MyREST::User>() << dynamic_cast<MyREST::PendingUser*>(pu)->user());
179 * future->setFinished();
180 * });
181 * });
182 *
183 * Async::Future<QList<MyREST::User>> usersFuture = job.exec();
184 * usersFuture.waitForFinished();
185 * QList<MyRest::User> users = usersFuture.value();
186 * @endcode
187 *
188 * In the example above, calling @p job.exec() will first invoke the first job,
189 * which will retrieve a list of IDs, and then will invoke the second function
190 * for each single entry in the list returned by the first function.
191 */
192template<typename Out, typename ... In>
193class Job : public JobBase
194{
195 template<typename OutOther, typename ... InOther>
196 friend class Job;
197
198 template<typename OutOther>
199 friend Job<OutOther> start(Async::ThenTask<OutOther> func);
200
201public:
202 template<typename OutOther, typename ... InOther>
203 Job<OutOther, InOther ...> then(ThenTask<OutOther, InOther ...> func)
204 {
205 return Job<OutOther, InOther ...>(new Private::ThenExecutor<OutOther, InOther ...>(func, mExecutor));
206 }
207
208 template<typename OutOther, typename InOther>
209 Job<OutOther, InOther> each(EachTask<OutOther, InOther> func)
210 {
211 static_assert(detail::isIterable<Out>::value,
212 "The 'Each' task can only be connected to a job that returns a list or an array.");
213 static_assert(detail::isIterable<OutOther>::value,
214 "The result type of 'Each' task must be a list or an array.");
215 return Job<OutOther, InOther>(new Private::EachExecutor<Out, OutOther, InOther>(func, mExecutor));
216 }
217
218 template<typename OutOther, typename InOther>
219 Job<OutOther, InOther> reduce(ReduceTask<OutOther, InOther> func)
220 {
221 static_assert(Async::detail::isIterable<Out>::value,
222 "The 'Result' task can only be connected to a job that returns a list or an array");
223 static_assert(std::is_same<typename Out::value_type, typename InOther::value_type>::value,
224 "The return type of previous task must be compatible with input type of this task");
225 return Job<OutOther, InOther>(new Private::ReduceExecutor<OutOther, InOther>(func, mExecutor));
226 }
227
228 Async::Future<Out> result() const
229 {
230 return *static_cast<Async::Future<Out>*>(mExecutor->result());
231 }
232
233private:
234 Job(Private::ExecutorBase *executor)
235 : JobBase(executor)
236 {
237 }
238};
239
240} // namespace Async
241
242
243// ********** Out of line definitions ****************
244
245namespace Async {
246
247template<typename Out>
248Job<Out> start(ThenTask<Out> func)
249{
250 return Job<Out>(new Private::ThenExecutor<Out>(func));
251}
252
253namespace Private {
254
255template<typename PrevOut, typename Out, typename ... In>
256Future<PrevOut>* Executor<PrevOut, Out, In ...>::chainup()
257{
258 if (mPrev) {
259 mPrev->exec();
260 auto future = static_cast<Async::Future<PrevOut>*>(mPrev->result());
261 assert(future->isFinished());
262 return future;
263 } else {
264 return 0;
265 }
266}
267
268template<typename Out, typename ... In>
269ThenExecutor<Out, In ...>::ThenExecutor(ThenTask<Out, In ...> then, ExecutorBase* parent)
270 : Executor<typename PreviousOut<In ...>::type, Out, In ...>(parent)
271{
272 this->mFunc = then;
273}
274
275template<typename Out, typename ... In>
276void ThenExecutor<Out, In ...>::exec()
277{
278 auto in = this->chainup();
279 (void)in; // supress 'unused variable' warning when In is void
280
281 auto out = new Async::Future<Out>();
282 this->mFunc(in ? in->value() : In() ..., *out);
283 out->waitForFinished();
284 this->mResult = out;
285}
286
287template<typename PrevOut, typename Out, typename In>
288EachExecutor<PrevOut, Out, In>::EachExecutor(EachTask<Out, In> each, ExecutorBase* parent)
289 : Executor<PrevOut, Out, In>(parent)
290{
291 this->mFunc = each;
292}
293
294template<typename PrevOut, typename Out, typename In>
295void EachExecutor<PrevOut, Out, In>::exec()
296{
297 auto in = this->chainup();
298
299 auto *out = new Async::Future<Out>();
300 for (auto arg : in->value()) {
301 Async::Future<Out> future;
302 this->mFunc(arg, future);
303 future.waitForFinished();
304 out->setValue(out->value() + future.value());
305 }
306 out->setFinished();
307
308 this->mResult = out;
309}
310
311template<typename Out, typename In>
312ReduceExecutor<Out, In>::ReduceExecutor(ReduceTask<Out, In> reduce, ExecutorBase* parent)
313 : Executor<In, Out, In>(parent)
314{
315 this->mFunc = reduce;
316}
317
318template<typename Out, typename In>
319void ReduceExecutor<Out, In>::exec()
320{
321 auto in = this->chainup();
322
323 auto out = new Async::Future<Out>();
324 this->mFunc(in->value(), *out);
325 out->waitForFinished();
326 this->mResult = out;
327}
328
329} // namespace Private
330
331} // namespace Async
332
333
334
335#endif // ASYNC_H
336
337
diff --git a/async/src/async_impl.h b/async/src/async_impl.h
new file mode 100644
index 0000000..7b5c140
--- /dev/null
+++ b/async/src/async_impl.h
@@ -0,0 +1,51 @@
1/*
2 * Copyright 2014 Daniel Vrátil <dvratil@redhat.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License or (at your option) version 3 or any later version
8 * accepted by the membership of KDE e.V. (or its successor approved
9 * by the membership of KDE e.V.), which shall act as a proxy
10 * defined in Section 14 of version 3 of the license.
11 *
12 * This program 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
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#ifndef ASYNC_IMPL_H
23#define ASYNC_IMPL_H
24
25#include "async.h"
26
27namespace Async {
28
29namespace detail {
30
31template<typename T>
32struct identity
33{
34 typedef T type;
35};
36
37template<typename T, typename Enable = void>
38struct isIterable {
39 enum { value = 0 };
40};
41
42template<typename T>
43struct isIterable<T, typename std::conditional<false, typename T::iterator, void>::type> {
44 enum { value = 1 };
45};
46
47} // namespace Detail
48
49} // namespace Async
50
51#endif // ASYNC_IMPL_H
diff --git a/async/src/future.h b/async/src/future.h
new file mode 100644
index 0000000..eb3de1e
--- /dev/null
+++ b/async/src/future.h
@@ -0,0 +1,92 @@
1/*
2 * Copyright 2014 Daniel Vrátil <dvratil@redhat.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License or (at your option) version 3 or any later version
8 * accepted by the membership of KDE e.V. (or its successor approved
9 * by the membership of KDE e.V.), which shall act as a proxy
10 * defined in Section 14 of version 3 of the license.
11 *
12 * This program 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
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#ifndef FUTURE_H
23#define FUTURE_H
24
25class QEventLoop;
26
27namespace Async {
28
29class FutureBase
30{
31public:
32 FutureBase();
33 FutureBase(const FutureBase &other);
34 virtual ~FutureBase();
35
36 void setFinished();
37 bool isFinished() const;
38 void waitForFinished();
39
40protected:
41 bool mFinished;
42 QEventLoop *mWaitLoop;
43};
44
45template<typename T>
46class Future : public FutureBase
47{
48public:
49 Future()
50 : FutureBase()
51 {}
52
53 Future(const Future<T> &other)
54 : FutureBase(other)
55 , mValue(other.mValue)
56 {}
57
58 Future(const T &val)
59 : FutureBase()
60 , mValue(val)
61 {}
62
63 void setValue(const T &val)
64 {
65 mValue = val;
66 }
67
68 T value() const
69 {
70 return mValue;
71 }
72
73private:
74 T mValue;
75};
76
77template<>
78class Future<void> : public FutureBase
79{
80public:
81 Future()
82 : FutureBase()
83 {}
84
85 Future(const Future<void> &other)
86 : FutureBase(other)
87 {}
88};
89
90} // namespace Async
91
92#endif // FUTURE_H
diff --git a/client/clientapi.h b/client/clientapi.h
index 592fa48..662f96c 100644
--- a/client/clientapi.h
+++ b/client/clientapi.h
@@ -6,6 +6,7 @@
6#include <QStandardPaths> 6#include <QStandardPaths>
7#include <QTimer> 7#include <QTimer>
8#include <QDebug> 8#include <QDebug>
9#include <QEventLoop>
9#include <functional> 10#include <functional>
10 11
11namespace async { 12namespace async {
@@ -64,6 +65,14 @@ namespace async {
64 * The future side for the client. 65 * The future side for the client.
65 * 66 *
66 * It does not directly hold the state. 67 * It does not directly hold the state.
68 *
69 * The advantage of this is that we can specialize it to:
70 * * do inline transformations to the data
71 * * directly store the state in a suitable datastructure: QList, QSet, std::list, QVector, ...
72 * * build async interfaces with signals
73 * * build sync interfaces that block when accessing the value
74 *
75 * TODO: This should probably be merged with daniels futurebase used in Async
67 */ 76 */
68 template<class DomainType> 77 template<class DomainType>
69 class ResultEmitter { 78 class ResultEmitter {
@@ -85,6 +94,42 @@ namespace async {
85 std::function<void(void)> completeHandler; 94 std::function<void(void)> completeHandler;
86 }; 95 };
87 96
97
98 /*
99 * A result set specialization that provides a syncronous list
100 */
101 template<class T>
102 class SyncListResult : public QList<T> {
103 public:
104 SyncListResult(const QSharedPointer<ResultEmitter<T> > &emitter)
105 :QList<T>(),
106 mComplete(false),
107 mEmitter(emitter)
108 {
109 emitter->onAdded([this](const T &value) {
110 this->append(value);
111 });
112 emitter->onComplete([this]() {
113 mComplete = true;
114 auto loop = mWaitLoop.toStrongRef();
115 if (loop) {
116 loop->quit();
117 }
118 });
119 }
120
121 void exec()
122 {
123 auto loop = QSharedPointer<QEventLoop>::create();
124 mWaitLoop = loop;
125 loop->exec(QEventLoop::ExcludeUserInputEvents);
126 }
127
128 private:
129 bool mComplete;
130 QWeakPointer<QEventLoop> mWaitLoop;
131 QSharedPointer<ResultEmitter<T> > mEmitter;
132 };
88} 133}
89 134
90namespace Akonadi2 { 135namespace Akonadi2 {
diff --git a/client/test/clientapitest.cpp b/client/test/clientapitest.cpp
index bff910b..2d1c238 100644
--- a/client/test/clientapitest.cpp
+++ b/client/test/clientapitest.cpp
@@ -37,15 +37,9 @@ private Q_SLOTS:
37 Akonadi2::Query query; 37 Akonadi2::Query query;
38 query.resources << "dummyresource"; 38 query.resources << "dummyresource";
39 39
40 auto result = Akonadi2::Store::load<Akonadi2::Domain::Event>(query); 40 async::SyncListResult<Akonadi2::Domain::Event::Ptr> result(Akonadi2::Store::load<Akonadi2::Domain::Event>(query));
41 41 result.exec();
42 QList<Akonadi2::Domain::Event::Ptr> resultSet; 42 QCOMPARE(result.size(), 1);
43 result->onAdded([&resultSet](const Akonadi2::Domain::Event::Ptr &event){ resultSet << event; qDebug() << "result added";});
44
45 bool complete;
46 result->onComplete([&complete]{ complete = true; qDebug() << "complete";});
47
48 QTRY_VERIFY(complete);
49 } 43 }
50 44
51}; 45};
diff --git a/common/storage.h b/common/storage.h
index a051043..3f14e83 100644
--- a/common/storage.h
+++ b/common/storage.h
@@ -1,6 +1,7 @@
1#pragma once 1#pragma once
2 2
3#include <string> 3#include <string>
4#include <functional>
4#include <QString> 5#include <QString>
5 6
6class Storage { 7class Storage {
diff --git a/tests/hawd/module.h b/tests/hawd/module.h
index 856c09c..4242cfe 100644
--- a/tests/hawd/module.h
+++ b/tests/hawd/module.h
@@ -21,6 +21,8 @@
21 21
22#include "state.h" 22#include "state.h"
23 23
24#include <functional>
25
24#include <QStringList> 26#include <QStringList>
25#include <QVector> 27#include <QVector>
26 28