Merge lp:~jamesh/thumbnailer/dbus-handler into lp:thumbnailer/devel

Proposed by James Henstridge
Status: Merged
Approved by: Michi Henning
Approved revision: 139
Merged at revision: 133
Proposed branch: lp:~jamesh/thumbnailer/dbus-handler
Merge into: lp:thumbnailer/devel
Diff against target: 1147 lines (+800/-185)
12 files modified
src/service/CMakeLists.txt (+6/-2)
src/service/albumarthandler.cpp (+80/-0)
src/service/albumarthandler.h (+59/-0)
src/service/artistarthandler.cpp (+80/-0)
src/service/artistarthandler.h (+59/-0)
src/service/dbusinterface.cpp (+45/-177)
src/service/dbusinterface.h (+16/-3)
src/service/handler.cpp (+199/-0)
src/service/handler.h (+89/-0)
src/service/main.cpp (+5/-3)
src/service/thumbnailhandler.cpp (+103/-0)
src/service/thumbnailhandler.h (+59/-0)
To merge this branch: bzr merge lp:~jamesh/thumbnailer/dbus-handler
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Michi Henning (community) Approve
Review via email: mp+256616@code.launchpad.net

Commit message

Split out the D-Bus method request handlers into separate classes, with some processing happening in a thread pool (where blocking is permissible) and some in the event loop thread (where blocking is not allowed, but we don't monopolise a thread).

Description of the change

Split the D-Bus method handlers out from the DBusInterface class, and structure them so they can run in three phases. The intended phases are:

1. check() - called in a thread pool, so can perform blocking calls. This method should determine whether it has a valid image available in the cache, and return it if so.

May raise exceptions on failure. If no image is returned, the request moves on to phase 2.

2. download() - called from the event loop, so can not block. This phase is intended to be used to perform actions that don't require a dedicated thread to complete, such as using QNetwork to download data, or QProcess to monitor a child process.

When complete, the handler should call the downloadFinished() slot on success, or sendError() method on failure.

3. check() - called in a thread pool, so can perform blocking calls. This method should use the information gathered from phase 2 to generate a thumbnail, store it in the cache, and return it to the user.

As with phase 1, exceptions can be used to signal failure.

At the moment, none of the code from the Thumbnailer class is set up to handle this split of responsibility, so the first two phases are stubbed out with all the work being done in the third phase. I could have done the work in the first phase, but this ensures that the other two are getting called correctly.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:136
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~jamesh/thumbnailer/dbus-handler/+merge/256616/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/thumbnailer-devel-ci/33/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/thumbnailer-devel-vivid-amd64-ci/33/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/thumbnailer-devel-vivid-armhf-ci/33/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/thumbnailer-devel-vivid-i386-ci/33/console

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/thumbnailer-devel-ci/33/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michi Henning (michihenning) wrote :

Looks very good, thanks!

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~jamesh/thumbnailer/dbus-handler updated
139. By James Henstridge

Merge from devel, fixing conflicts.

Revision history for this message
Michi Henning (michihenning) wrote :

Thanks!

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/service/CMakeLists.txt'
2--- src/service/CMakeLists.txt 2015-04-13 04:15:17 +0000
3+++ src/service/CMakeLists.txt 2015-04-20 05:02:20 +0000
4@@ -1,15 +1,19 @@
5 add_definitions(${THUMBNAILER_CFLAGS})
6 include_directories(. ${CMAKE_CURRENT_BINARY_DIR})
7
8-qt5_add_dbus_adaptor(adaptor_files dbusinterface.xml dbusinterface.h DBusInterface)
9+qt5_add_dbus_adaptor(adaptor_files dbusinterface.xml dbusinterface.h unity::thumbnailer::service::DBusInterface)
10
11 add_executable(thumbnailer-service
12 main.cpp
13 dbusinterface.cpp
14+ handler.cpp
15+ thumbnailhandler.cpp
16+ albumarthandler.cpp
17+ artistarthandler.cpp
18 ${adaptor_files}
19 )
20
21-qt5_use_modules(thumbnailer-service DBus)
22+qt5_use_modules(thumbnailer-service DBus Concurrent)
23 target_link_libraries(thumbnailer-service thumbnailer ${CMAKE_THREAD_LIBS_INIT})
24 set_target_properties(thumbnailer-service PROPERTIES AUTOMOC TRUE)
25
26
27=== added file 'src/service/albumarthandler.cpp'
28--- src/service/albumarthandler.cpp 1970-01-01 00:00:00 +0000
29+++ src/service/albumarthandler.cpp 2015-04-20 05:02:20 +0000
30@@ -0,0 +1,80 @@
31+/*
32+ * Copyright (C) 2015 Canonical, Ltd.
33+ *
34+ * Authors:
35+ * James Henstridge <james.henstridge@canonical.com>
36+ *
37+ * This library is free software; you can redistribute it and/or modify it under
38+ * the terms of version 3 of the GNU General Public License as published
39+ * by the Free Software Foundation.
40+ *
41+ * This library is distributed in the hope that it will be useful, but WITHOUT
42+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
43+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
44+ * details.
45+ *
46+ * You should have received a copy of the GNU General Public License
47+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
48+ */
49+
50+#include "albumarthandler.h"
51+
52+#include <fcntl.h>
53+#include <sys/stat.h>
54+#include <sys/types.h>
55+#include <unistd.h>
56+
57+#include <unity/util/ResourcePtr.h>
58+
59+namespace unity {
60+namespace thumbnailer {
61+namespace service {
62+
63+struct AlbumArtHandlerPrivate {
64+ const std::shared_ptr<Thumbnailer> thumbnailer;
65+ const QString artist;
66+ const QString album;
67+ const ThumbnailSize size;
68+
69+ AlbumArtHandlerPrivate(const std::shared_ptr<Thumbnailer> &thumbnailer,
70+ const QString &artist,
71+ const QString &album,
72+ ThumbnailSize size)
73+ : thumbnailer(thumbnailer), artist(artist), album(album), size(size) {
74+ }
75+};
76+
77+AlbumArtHandler::AlbumArtHandler(const QDBusConnection &bus,
78+ const QDBusMessage &message,
79+ const std::shared_ptr<Thumbnailer> &thumbnailer,
80+ const QString &artist,
81+ const QString &album,
82+ ThumbnailSize size)
83+ : Handler(bus, message), p(new AlbumArtHandlerPrivate(thumbnailer, artist, album, size)) {
84+}
85+
86+AlbumArtHandler::~AlbumArtHandler() {
87+}
88+
89+QDBusUnixFileDescriptor AlbumArtHandler::check() {
90+ return QDBusUnixFileDescriptor();
91+}
92+
93+void AlbumArtHandler::download() {
94+ downloadFinished();
95+}
96+
97+QDBusUnixFileDescriptor AlbumArtHandler::create() {
98+ std::string art_image = p->thumbnailer->get_album_art(
99+ p->artist.toStdString(), p->album.toStdString(), p->size, TN_REMOTE);
100+
101+ if (art_image.empty()) {
102+ throw std::runtime_error("Could not get thumbnail");
103+ }
104+
105+ return write_to_tmpfile(art_image);
106+}
107+
108+}
109+}
110+}
111
112=== added file 'src/service/albumarthandler.h'
113--- src/service/albumarthandler.h 1970-01-01 00:00:00 +0000
114+++ src/service/albumarthandler.h 2015-04-20 05:02:20 +0000
115@@ -0,0 +1,59 @@
116+/*
117+ * Copyright (C) 2015 Canonical, Ltd.
118+ *
119+ * Authors:
120+ * James Henstridge <james.henstridge@canonical.com>
121+ *
122+ * This library is free software; you can redistribute it and/or modify it under
123+ * the terms of version 3 of the GNU General Public License as published
124+ * by the Free Software Foundation.
125+ *
126+ * This library is distributed in the hope that it will be useful, but WITHOUT
127+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
128+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
129+ * details.
130+ *
131+ * You should have received a copy of the GNU General Public License
132+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
133+ */
134+
135+#pragma once
136+
137+#include "handler.h"
138+#include <thumbnailer.h>
139+
140+#include <memory>
141+
142+#include <QObject>
143+#include <QDBusConnection>
144+#include <QDBusMessage>
145+#include <QDBusUnixFileDescriptor>
146+
147+namespace unity {
148+namespace thumbnailer {
149+namespace service {
150+
151+struct AlbumArtHandlerPrivate;
152+
153+class AlbumArtHandler : public Handler {
154+ Q_OBJECT
155+public:
156+ AlbumArtHandler(const QDBusConnection &bus, const QDBusMessage &message,
157+ const std::shared_ptr<Thumbnailer> &thumbnailer,
158+ const QString &artist,
159+ const QString &album,
160+ ThumbnailSize size);
161+ ~AlbumArtHandler();
162+
163+protected:
164+ virtual QDBusUnixFileDescriptor check() override;
165+ virtual void download() override;
166+ virtual QDBusUnixFileDescriptor create() override;
167+
168+private:
169+ std::unique_ptr<AlbumArtHandlerPrivate> p;
170+};
171+
172+}
173+}
174+}
175
176=== added file 'src/service/artistarthandler.cpp'
177--- src/service/artistarthandler.cpp 1970-01-01 00:00:00 +0000
178+++ src/service/artistarthandler.cpp 2015-04-20 05:02:20 +0000
179@@ -0,0 +1,80 @@
180+/*
181+ * Copyright (C) 2015 Canonical, Ltd.
182+ *
183+ * Authors:
184+ * James Henstridge <james.henstridge@canonical.com>
185+ *
186+ * This library is free software; you can redistribute it and/or modify it under
187+ * the terms of version 3 of the GNU General Public License as published
188+ * by the Free Software Foundation.
189+ *
190+ * This library is distributed in the hope that it will be useful, but WITHOUT
191+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
192+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
193+ * details.
194+ *
195+ * You should have received a copy of the GNU General Public License
196+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
197+ */
198+
199+#include "artistarthandler.h"
200+
201+#include <fcntl.h>
202+#include <sys/stat.h>
203+#include <sys/types.h>
204+#include <unistd.h>
205+
206+#include <unity/util/ResourcePtr.h>
207+
208+namespace unity {
209+namespace thumbnailer {
210+namespace service {
211+
212+struct ArtistArtHandlerPrivate {
213+ const std::shared_ptr<Thumbnailer> thumbnailer;
214+ const QString artist;
215+ const QString album;
216+ const ThumbnailSize size;
217+
218+ ArtistArtHandlerPrivate(const std::shared_ptr<Thumbnailer> &thumbnailer,
219+ const QString &artist,
220+ const QString &album,
221+ ThumbnailSize size)
222+ : thumbnailer(thumbnailer), artist(artist), album(album), size(size) {
223+ }
224+};
225+
226+ArtistArtHandler::ArtistArtHandler(const QDBusConnection &bus,
227+ const QDBusMessage &message,
228+ const std::shared_ptr<Thumbnailer> &thumbnailer,
229+ const QString &artist,
230+ const QString &album,
231+ ThumbnailSize size)
232+ : Handler(bus, message), p(new ArtistArtHandlerPrivate(thumbnailer, artist, album, size)) {
233+}
234+
235+ArtistArtHandler::~ArtistArtHandler() {
236+}
237+
238+QDBusUnixFileDescriptor ArtistArtHandler::check() {
239+ return QDBusUnixFileDescriptor();
240+}
241+
242+void ArtistArtHandler::download() {
243+ downloadFinished();
244+}
245+
246+QDBusUnixFileDescriptor ArtistArtHandler::create() {
247+ std::string art_image = p->thumbnailer->get_artist_art(
248+ p->artist.toStdString(), p->album.toStdString(), p->size, TN_REMOTE);
249+
250+ if (art_image.empty()) {
251+ throw std::runtime_error("Could not get thumbnail");
252+ }
253+
254+ return write_to_tmpfile(art_image);
255+}
256+
257+}
258+}
259+}
260
261=== added file 'src/service/artistarthandler.h'
262--- src/service/artistarthandler.h 1970-01-01 00:00:00 +0000
263+++ src/service/artistarthandler.h 2015-04-20 05:02:20 +0000
264@@ -0,0 +1,59 @@
265+/*
266+ * Copyright (C) 2015 Canonical, Ltd.
267+ *
268+ * Authors:
269+ * James Henstridge <james.henstridge@canonical.com>
270+ *
271+ * This library is free software; you can redistribute it and/or modify it under
272+ * the terms of version 3 of the GNU General Public License as published
273+ * by the Free Software Foundation.
274+ *
275+ * This library is distributed in the hope that it will be useful, but WITHOUT
276+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
277+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
278+ * details.
279+ *
280+ * You should have received a copy of the GNU General Public License
281+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
282+ */
283+
284+#pragma once
285+
286+#include "handler.h"
287+#include <thumbnailer.h>
288+
289+#include <memory>
290+
291+#include <QObject>
292+#include <QDBusConnection>
293+#include <QDBusMessage>
294+#include <QDBusUnixFileDescriptor>
295+
296+namespace unity {
297+namespace thumbnailer {
298+namespace service {
299+
300+struct ArtistArtHandlerPrivate;
301+
302+class ArtistArtHandler : public Handler {
303+ Q_OBJECT
304+public:
305+ ArtistArtHandler(const QDBusConnection &bus, const QDBusMessage &message,
306+ const std::shared_ptr<Thumbnailer> &thumbnailer,
307+ const QString &artist,
308+ const QString &album,
309+ ThumbnailSize size);
310+ ~ArtistArtHandler();
311+
312+protected:
313+ virtual QDBusUnixFileDescriptor check() override;
314+ virtual void download() override;
315+ virtual QDBusUnixFileDescriptor create() override;
316+
317+private:
318+ std::unique_ptr<ArtistArtHandlerPrivate> p;
319+};
320+
321+}
322+}
323+}
324
325=== modified file 'src/service/dbusinterface.cpp'
326--- src/service/dbusinterface.cpp 2015-04-16 12:39:55 +0000
327+++ src/service/dbusinterface.cpp 2015-04-20 05:02:20 +0000
328@@ -18,34 +18,23 @@
329 */
330
331 #include "dbusinterface.h"
332-
333+#include "thumbnailhandler.h"
334+#include "albumarthandler.h"
335+#include "artistarthandler.h"
336 #include <thumbnailer.h>
337
338-#include <internal/safe_strerror.h>
339+#include <map>
340
341 #include <QDBusConnection>
342 #include <QDBusMessage>
343 #include <QDebug>
344-#include <QRunnable>
345-#include <QThreadPool>
346-#include <unity/util/ResourcePtr.h>
347-
348-#include <fcntl.h>
349-#include <sys/stat.h>
350-#include <unistd.h>
351
352 using namespace std;
353-using namespace unity::thumbnailer::internal;
354-
355-static const char ART_ERROR[] = "com.canonical.MediaScanner2.Error.Failed";
356-
357-struct DBusInterfacePrivate {
358- std::shared_ptr<Thumbnailer> thumbnailer = std::make_shared<Thumbnailer>();
359- QThreadPool pool;
360-};
361
362 namespace {
363
364+const char ART_ERROR[] = "com.canonical.Thubnailer.Error.Failed";
365+
366 ThumbnailSize desiredSizeFromString(const QString &size)
367 {
368 if (size == "small") {
369@@ -62,16 +51,16 @@
370 throw std::logic_error(error);
371 }
372
373-class Task final : public QRunnable {
374-public:
375- Task(std::function<void()> task) : task(task) {}
376- virtual void run() override {
377- task();
378- }
379-private:
380- std::function<void()> task;
381+}
382+
383+namespace unity {
384+namespace thumbnailer {
385+namespace service {
386+
387+struct DBusInterfacePrivate {
388+ std::shared_ptr<Thumbnailer> thumbnailer = std::make_shared<Thumbnailer>();
389+ std::map<Handler*,std::unique_ptr<Handler>> requests;
390 };
391-}
392
393 DBusInterface::DBusInterface(QObject *parent)
394 : QObject(parent), p(new DBusInterfacePrivate) {
395@@ -80,48 +69,6 @@
396 DBusInterface::~DBusInterface() {
397 }
398
399-namespace
400-{
401-
402-QDBusUnixFileDescriptor write_to_tmpfile(string const& image)
403-{
404-
405- typedef unity::util::ResourcePtr<int, decltype(&::close)> FdPtr;
406-
407- static auto find_tmpdir = []
408- {
409- char const* dirp = getenv("TMPDIR");
410- string dir = dirp ? dirp : "/tmp";
411- return dir;
412- };
413- static string dir = find_tmpdir();
414-
415- int fd = open(dir.c_str(), O_TMPFILE | O_RDWR);
416- if (fd < 0) {
417- string err = "cannot create tmpfile in " + dir + ": " + safe_strerror(errno);
418- throw runtime_error(err);
419- }
420- FdPtr fd_ptr(fd, ::close);
421- auto rc = write(fd_ptr.get(), &image[0], image.size());
422- if (rc == -1)
423- {
424- string err = "cannot write image data in " + dir + ": " + safe_strerror(errno);
425- throw runtime_error(err);
426- }
427- else if (string::size_type(rc) != image.size())
428- {
429- string err = "short write for image data in " + dir +
430- "(requested = " + to_string(image.size()) + ", actual = " + to_string(rc) + ")";
431- throw runtime_error(err);
432- }
433- lseek(fd, SEEK_SET, 0); // No error check needed, can't fail.
434-
435- return QDBusUnixFileDescriptor(fd_ptr.get());
436-}
437-
438-}
439-
440-
441 QDBusUnixFileDescriptor DBusInterface::GetAlbumArt(const QString &artist, const QString &album, const QString &desiredSize) {
442 qDebug() << "Look up cover art for" << artist << "/" << album << "at size" << desiredSize;
443
444@@ -133,37 +80,8 @@
445 return QDBusUnixFileDescriptor();
446 }
447
448- setDelayedReply(true);
449- auto thumbnailer = p->thumbnailer;
450- auto bus = connection();
451- auto msg = message();
452- p->pool.start(new Task([thumbnailer, artist, album, size, bus, msg]() {
453- std::string art_image;
454- try {
455- art_image = thumbnailer->get_album_art(
456- artist.toStdString(), album.toStdString(), size, TN_REMOTE);
457- } catch (const std::exception &e) {
458- bus.send(msg.createErrorReply(ART_ERROR, e.what()));
459- return;
460- }
461-
462- if (art_image.empty()) {
463- bus.send(msg.createErrorReply(ART_ERROR, "Could not get thumbnail"));
464- return;
465- }
466-
467- try
468- {
469- auto unix_fd = write_to_tmpfile(art_image);
470- bus.send(msg.createReply(QVariant::fromValue(unix_fd)));
471- }
472- catch (runtime_error const& e)
473- {
474- string err = string("GetAlbumArt(): ") + e.what();
475- bus.send(msg.createErrorReply(ART_ERROR, err.c_str()));
476- }
477- }));
478-
479+ queueRequest(new AlbumArtHandler(connection(), message(), p->thumbnailer,
480+ artist, album, size));
481 return QDBusUnixFileDescriptor();
482 }
483
484@@ -178,42 +96,13 @@
485 return QDBusUnixFileDescriptor();
486 }
487
488- setDelayedReply(true);
489- auto thumbnailer = p->thumbnailer;
490- auto bus = connection();
491- auto msg = message();
492- p->pool.start(new Task([thumbnailer, artist, album, size, bus, msg]() {
493- std::string art_image;
494- try {
495- art_image = thumbnailer->get_artist_art(
496- artist.toStdString(), album.toStdString(), size, TN_REMOTE);
497- } catch (const std::exception &e) {
498- bus.send(msg.createErrorReply(ART_ERROR, e.what()));
499- return;
500- }
501-
502- if (art_image.empty()) {
503- bus.send(msg.createErrorReply(ART_ERROR, "Could not get thumbnail"));
504- return;
505- }
506-
507- try
508- {
509- auto unix_fd = write_to_tmpfile(art_image);
510- bus.send(msg.createReply(QVariant::fromValue(unix_fd)));
511- }
512- catch (runtime_error const& e)
513- {
514- string err = string("GetArtistArt(): ") + e.what();
515- bus.send(msg.createErrorReply(ART_ERROR, err.c_str()));
516- }
517- }));
518-
519+ queueRequest(new ArtistArtHandler(connection(), message(), p->thumbnailer,
520+ artist, album, size));
521 return QDBusUnixFileDescriptor();
522 }
523
524 QDBusUnixFileDescriptor DBusInterface::GetThumbnail(const QString &filename, const QDBusUnixFileDescriptor &filename_fd, const QString &desiredSize) {
525- qDebug() << "Look thumbnail for" << filename << "at size" << desiredSize;
526+ qDebug() << "Create thumbnail for" << filename << "at size" << desiredSize;
527
528 ThumbnailSize size;
529 try {
530@@ -223,52 +112,31 @@
531 return QDBusUnixFileDescriptor();
532 }
533
534- struct stat filename_stat, fd_stat;
535- if (stat(filename.toUtf8(), &filename_stat) < 0) {
536- sendErrorReply(ART_ERROR, "Could not stat file");
537- return QDBusUnixFileDescriptor();
538- }
539- if (fstat(filename_fd.fileDescriptor(), &fd_stat) < 0) {
540- sendErrorReply(ART_ERROR, "Could not stat file descriptor");
541- return QDBusUnixFileDescriptor();
542- }
543- if (filename_stat.st_dev != fd_stat.st_dev ||
544- filename_stat.st_ino != fd_stat.st_ino) {
545- sendErrorReply(ART_ERROR, "filename refers to a different file to the file descriptor");
546- return QDBusUnixFileDescriptor();
547- }
548+ queueRequest(new ThumbnailHandler(connection(), message(), p->thumbnailer,
549+ filename, filename_fd, size));
550+ return QDBusUnixFileDescriptor();
551+}
552
553+void DBusInterface::queueRequest(Handler *handler) {
554+ p->requests.emplace(handler, std::unique_ptr<Handler>(handler));
555+ connect(handler, &Handler::finished, this, &DBusInterface::requestFinished);
556 setDelayedReply(true);
557- auto thumbnailer = p->thumbnailer;
558- auto bus = connection();
559- auto msg = message();
560- p->pool.start(new Task([=]() {
561- std::string art;
562- try {
563- art = thumbnailer->get_thumbnail(
564- filename.toStdString(), size, TN_REMOTE);
565- } catch (const std::exception &e) {
566- bus.send(msg.createErrorReply(ART_ERROR, e.what()));
567- return;
568- }
569-
570- if (art.empty()) {
571- bus.send(msg.createErrorReply(ART_ERROR, "Could not get thumbnail"));
572- return;
573- }
574-
575- // FIXME: check that the thumbnail was produced for fd_stat
576- int fd = open(art.c_str(), O_RDONLY);
577- if (fd < 0) {
578- bus.send(msg.createErrorReply(ART_ERROR, safe_strerror(errno).c_str()));
579- return;
580- }
581-
582- QDBusUnixFileDescriptor unix_fd(fd);
583- close(fd);
584-
585- bus.send(msg.createReply(QVariant::fromValue(unix_fd)));
586- }));
587-
588- return QDBusUnixFileDescriptor();
589+ handler->begin();
590+}
591+
592+void DBusInterface::requestFinished() {
593+ Handler *handler = static_cast<Handler*>(sender());
594+ try {
595+ auto &h = p->requests.at(handler);
596+ h.release();
597+ p->requests.erase(handler);
598+ } catch (const std::out_of_range &e) {
599+ qWarning() << "finished() called on unknown handler" << handler;
600+ }
601+ // Queue deletion of handler when we re-enter the event loop.
602+ handler->deleteLater();
603+}
604+
605+}
606+}
607 }
608
609=== modified file 'src/service/dbusinterface.h'
610--- src/service/dbusinterface.h 2015-04-13 01:58:39 +0000
611+++ src/service/dbusinterface.h 2015-04-20 05:02:20 +0000
612@@ -17,8 +17,9 @@
613 * along with this program. If not, see <http://www.gnu.org/licenses/>.
614 */
615
616-#ifndef DBUSINTERFACE_H
617-#define DBUSINTERFACE_H
618+#pragma once
619+
620+#include "handler.h"
621
622 #include <memory>
623
624@@ -27,6 +28,10 @@
625 #include <QObject>
626 #include <QString>
627
628+namespace unity {
629+namespace thumbnailer {
630+namespace service {
631+
632 struct DBusInterfacePrivate;
633
634 class DBusInterface : public QObject, protected QDBusContext {
635@@ -44,7 +49,15 @@
636 QDBusUnixFileDescriptor GetThumbnail(const QString &filename, const QDBusUnixFileDescriptor &filename_fd, const QString &desiredSize);
637
638 private:
639+ void queueRequest(Handler* handler);
640+
641+private Q_SLOTS:
642+ void requestFinished();
643+
644+private:
645 std::unique_ptr<DBusInterfacePrivate> p;
646 };
647
648-#endif
649+}
650+}
651+}
652
653=== added file 'src/service/handler.cpp'
654--- src/service/handler.cpp 1970-01-01 00:00:00 +0000
655+++ src/service/handler.cpp 2015-04-20 05:02:20 +0000
656@@ -0,0 +1,199 @@
657+/*
658+ * Copyright (C) 2015 Canonical, Ltd.
659+ *
660+ * Authors:
661+ * James Henstridge <james.henstridge@canonical.com>
662+ *
663+ * This library is free software; you can redistribute it and/or modify it under
664+ * the terms of version 3 of the GNU General Public License as published
665+ * by the Free Software Foundation.
666+ *
667+ * This library is distributed in the hope that it will be useful, but WITHOUT
668+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
669+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
670+ * details.
671+ *
672+ * You should have received a copy of the GNU General Public License
673+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
674+ */
675+
676+#include "handler.h"
677+#include <internal/safe_strerror.h>
678+
679+#include <fcntl.h>
680+#include <sys/stat.h>
681+#include <sys/types.h>
682+#include <unistd.h>
683+
684+#include <QtConcurrent>
685+#include <QFuture>
686+#include <QFutureWatcher>
687+#include <QDebug>
688+#include <unity/util/ResourcePtr.h>
689+
690+using namespace unity::thumbnailer::internal;
691+
692+namespace {
693+const char ART_ERROR[] = "com.canonical.Thumbnailer.Error.Failed";
694+
695+struct FdOrError {
696+ QDBusUnixFileDescriptor fd;
697+ QString error;
698+};
699+}
700+
701+namespace unity {
702+namespace thumbnailer {
703+namespace service {
704+
705+struct HandlerPrivate {
706+ QDBusConnection bus;
707+ const QDBusMessage message;
708+
709+ bool cancelled = false;
710+ QFutureWatcher<FdOrError> checkWatcher;
711+ QFutureWatcher<FdOrError> createWatcher;
712+
713+ HandlerPrivate(const QDBusConnection &bus, const QDBusMessage &message)
714+ : bus(bus), message(message) {
715+ }
716+};
717+
718+Handler::Handler(const QDBusConnection &bus, const QDBusMessage &message)
719+ : p(new HandlerPrivate(bus, message)) {
720+ connect(&p->checkWatcher, &QFutureWatcher<FdOrError>::finished,
721+ this, &Handler::checkFinished);
722+ connect(&p->createWatcher, &QFutureWatcher<FdOrError>::finished,
723+ this, &Handler::createFinished);
724+}
725+
726+Handler::~Handler() {
727+ p->cancelled = true;
728+ // ensure that jobs occurring in the thread pool complete.
729+ p->checkWatcher.waitForFinished();
730+ p->createWatcher.waitForFinished();
731+ qDebug() << "Handler" << this << "destroyed";
732+}
733+
734+void Handler::begin() {
735+ auto do_check = [this]() -> FdOrError {
736+ try {
737+ return FdOrError{check(), nullptr};
738+ } catch (const std::exception &e) {
739+ return FdOrError{QDBusUnixFileDescriptor(), e.what()};
740+ }
741+ };
742+ p->checkWatcher.setFuture(QtConcurrent::run(do_check));
743+}
744+
745+void Handler::checkFinished() {
746+ if (p->cancelled)
747+ return;
748+
749+ FdOrError fd_error;
750+ try {
751+ fd_error = p->checkWatcher.result();
752+ } catch (const std::exception &e) {
753+ sendError(e.what());
754+ return;
755+ }
756+ if (!fd_error.error.isNull()) {
757+ sendError(fd_error.error);
758+ return;
759+ }
760+ // Did we find a valid thumbnail in the cache?
761+ if (fd_error.fd.isValid()) {
762+ sendThumbnail(fd_error.fd);
763+ } else {
764+ // otherwise move on to the download phase.
765+ download();
766+ }
767+}
768+
769+void Handler::downloadFinished() {
770+ if (p->cancelled)
771+ return;
772+
773+ auto do_create = [this]() -> FdOrError {
774+ try {
775+ return FdOrError{create(), nullptr};
776+ } catch (const std::exception &e) {
777+ return FdOrError{QDBusUnixFileDescriptor(), e.what()};
778+ }
779+ };
780+ p->createWatcher.setFuture(QtConcurrent::run(do_create));
781+}
782+
783+void Handler::createFinished() {
784+ if (p->cancelled)
785+ return;
786+
787+ FdOrError fd_error;
788+ try {
789+ fd_error = p->createWatcher.result();
790+ } catch (const std::exception &e) {
791+ sendError(e.what());
792+ return;
793+ }
794+ if (!fd_error.error.isEmpty()) {
795+ sendError(fd_error.error);
796+ return;
797+ }
798+ // Did we find a valid thumbnail in the cache?
799+ if (fd_error.fd.isValid()) {
800+ sendThumbnail(fd_error.fd);
801+ } else {
802+ // otherwise move on to the download phase.
803+ download();
804+ }
805+}
806+
807+void Handler::sendThumbnail(const QDBusUnixFileDescriptor &unix_fd) {
808+ p->bus.send(p->message.createReply(QVariant::fromValue(unix_fd)));
809+ Q_EMIT finished();
810+}
811+
812+void Handler::sendError(const QString &error) {
813+ p->bus.send(p->message.createErrorReply(ART_ERROR, error));
814+ Q_EMIT finished();
815+}
816+
817+QDBusUnixFileDescriptor write_to_tmpfile(std::string const& image)
818+{
819+
820+ typedef unity::util::ResourcePtr<int, decltype(&::close)> FdPtr;
821+
822+ static auto find_tmpdir = []
823+ {
824+ char const* dirp = getenv("TMPDIR");
825+ std::string dir = dirp ? dirp : "/tmp";
826+ return dir;
827+ };
828+ static std::string dir = find_tmpdir();
829+
830+ int fd = open(dir.c_str(), O_TMPFILE | O_RDWR);
831+ if (fd < 0) {
832+ std::string err = "cannot create tmpfile in " + dir + ": " + safe_strerror(errno);
833+ throw std::runtime_error(err);
834+ }
835+ FdPtr fd_ptr(fd, ::close);
836+ auto rc = write(fd_ptr.get(), &image[0], image.size());
837+ if (rc == -1)
838+ {
839+ std::string err = "cannot write image data in " + dir + ": " + safe_strerror(errno);
840+ throw std::runtime_error(err);
841+ }
842+ else if (std::string::size_type(rc) != image.size())
843+ {
844+ std::string err = "short write for image data in " + dir +
845+ "(requested = " + std::to_string(image.size()) + ", actual = " + std::to_string(rc) + ")";
846+ throw std::runtime_error(err);
847+ }
848+ lseek(fd, SEEK_SET, 0); // No error check needed, can't fail.
849+
850+ return QDBusUnixFileDescriptor(fd_ptr.get());
851+}
852+
853+}
854+}
855+}
856
857=== added file 'src/service/handler.h'
858--- src/service/handler.h 1970-01-01 00:00:00 +0000
859+++ src/service/handler.h 2015-04-20 05:02:20 +0000
860@@ -0,0 +1,89 @@
861+/*
862+ * Copyright (C) 2015 Canonical, Ltd.
863+ *
864+ * Authors:
865+ * James Henstridge <james.henstridge@canonical.com>
866+ *
867+ * This library is free software; you can redistribute it and/or modify it under
868+ * the terms of version 3 of the GNU General Public License as published
869+ * by the Free Software Foundation.
870+ *
871+ * This library is distributed in the hope that it will be useful, but WITHOUT
872+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
873+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
874+ * details.
875+ *
876+ * You should have received a copy of the GNU General Public License
877+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
878+ */
879+
880+#pragma once
881+
882+#include <memory>
883+#include <string>
884+
885+#include <QObject>
886+#include <QDBusConnection>
887+#include <QDBusMessage>
888+#include <QDBusUnixFileDescriptor>
889+
890+namespace unity {
891+namespace thumbnailer {
892+namespace service {
893+
894+struct HandlerPrivate;
895+
896+class Handler : public QObject {
897+ Q_OBJECT
898+public:
899+ Handler(const QDBusConnection &bus, const QDBusMessage &message);
900+ ~Handler();
901+
902+ Handler(const Handler&) = delete;
903+ Handler& operator=(Handler&) = delete;
904+
905+ void begin();
906+
907+protected:
908+ void sendThumbnail(const QDBusUnixFileDescriptor &unix_fd);
909+ void sendError(const QString &error);
910+
911+ // Methods to be overridden by handlers
912+
913+ // check() determines whether the requested thumbnail exists in
914+ // the cache. It is called synchronously in the thread pool.
915+ //
916+ // If it is available, it should be returned as a file descriptor,
917+ // which will be returned to the user.
918+ //
919+ // If not, processing will continue.
920+ virtual QDBusUnixFileDescriptor check() = 0;
921+
922+ // download() is expected to perform any asynchronous actions and
923+ // end the request by either calling the downloadFinished() slot
924+ // or sendError().
925+ virtual void download() = 0;
926+
927+ // create() should create the thumbnail and store it in the cache,
928+ // and return it as a file descriptor. It is called synchronously
929+ // in the thread pool.
930+ virtual QDBusUnixFileDescriptor create() = 0;
931+
932+protected Q_SLOTS:
933+ void checkFinished();
934+ void downloadFinished();
935+ void createFinished();
936+
937+Q_SIGNALS:
938+ void finished();
939+
940+private:
941+ std::unique_ptr<HandlerPrivate> p;
942+};
943+
944+// Helper routine for creating an unnamed tmpfile from image data
945+QDBusUnixFileDescriptor write_to_tmpfile(std::string const& image);
946+
947+}
948+}
949+}
950
951=== modified file 'src/service/main.cpp'
952--- src/service/main.cpp 2015-04-01 08:53:21 +0000
953+++ src/service/main.cpp 2015-04-20 05:02:20 +0000
954@@ -5,6 +5,8 @@
955 #include "dbusinterface.h"
956 #include "dbusinterfaceadaptor.h"
957
958+using namespace unity::thumbnailer::service;
959+
960 static const char BUS_NAME[] = "com.canonical.Thumbnailer";
961 static const char BUS_PATH[] = "/com/canonical/Thumbnailer";
962
963@@ -13,9 +15,9 @@
964
965 auto bus = QDBusConnection::sessionBus();
966
967- auto server = new DBusInterface();
968- new ThumbnailerAdaptor(server);
969- bus.registerObject(BUS_PATH, server);
970+ unity::thumbnailer::service::DBusInterface server;
971+ new ThumbnailerAdaptor(&server);
972+ bus.registerObject(BUS_PATH, &server);
973
974 if (!bus.registerService(BUS_NAME)) {
975 fprintf(stderr, "Could no acquire D-Bus name %s.\n", BUS_NAME);
976
977=== added file 'src/service/thumbnailhandler.cpp'
978--- src/service/thumbnailhandler.cpp 1970-01-01 00:00:00 +0000
979+++ src/service/thumbnailhandler.cpp 2015-04-20 05:02:20 +0000
980@@ -0,0 +1,103 @@
981+/*
982+ * Copyright (C) 2015 Canonical, Ltd.
983+ *
984+ * Authors:
985+ * James Henstridge <james.henstridge@canonical.com>
986+ *
987+ * This library is free software; you can redistribute it and/or modify it under
988+ * the terms of version 3 of the GNU General Public License as published
989+ * by the Free Software Foundation.
990+ *
991+ * This library is distributed in the hope that it will be useful, but WITHOUT
992+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
993+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
994+ * details.
995+ *
996+ * You should have received a copy of the GNU General Public License
997+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
998+ */
999+
1000+#include "thumbnailhandler.h"
1001+#include <internal/safe_strerror.h>
1002+
1003+#include <fcntl.h>
1004+#include <sys/stat.h>
1005+#include <sys/types.h>
1006+#include <unistd.h>
1007+
1008+#include <unity/util/ResourcePtr.h>
1009+
1010+using namespace unity::thumbnailer::internal;
1011+
1012+namespace unity {
1013+namespace thumbnailer {
1014+namespace service {
1015+
1016+struct ThumbnailHandlerPrivate {
1017+ const std::shared_ptr<Thumbnailer> thumbnailer;
1018+ const QString filename;
1019+ const QDBusUnixFileDescriptor filename_fd;
1020+ const ThumbnailSize size;
1021+
1022+ ThumbnailHandlerPrivate(const std::shared_ptr<Thumbnailer> &thumbnailer,
1023+ const QString &filename,
1024+ const QDBusUnixFileDescriptor &filename_fd,
1025+ ThumbnailSize size)
1026+ : thumbnailer(thumbnailer), filename(filename),
1027+ filename_fd(filename_fd), size(size) {
1028+ }
1029+};
1030+
1031+ThumbnailHandler::ThumbnailHandler(const QDBusConnection &bus,
1032+ const QDBusMessage &message,
1033+ const std::shared_ptr<Thumbnailer> &thumbnailer,
1034+ const QString &filename,
1035+ const QDBusUnixFileDescriptor &filename_fd,
1036+ ThumbnailSize size)
1037+ : Handler(bus, message), p(new ThumbnailHandlerPrivate(thumbnailer, filename, filename_fd, size)) {
1038+}
1039+
1040+ThumbnailHandler::~ThumbnailHandler() {
1041+}
1042+
1043+QDBusUnixFileDescriptor ThumbnailHandler::check() {
1044+ return QDBusUnixFileDescriptor();
1045+}
1046+
1047+void ThumbnailHandler::download() {
1048+ downloadFinished();
1049+}
1050+
1051+QDBusUnixFileDescriptor ThumbnailHandler::create() {
1052+ typedef unity::util::ResourcePtr<int, decltype(&::close)> FdPtr;
1053+ struct stat filename_stat, fd_stat;
1054+
1055+ if (stat(p->filename.toUtf8(), &filename_stat) < 0) {
1056+ throw std::runtime_error("Could not stat file");
1057+ }
1058+ if (fstat(p->filename_fd.fileDescriptor(), &fd_stat) < 0) {
1059+ throw std::runtime_error("Could not stat file descriptor");
1060+ }
1061+ if (filename_stat.st_dev != fd_stat.st_dev ||
1062+ filename_stat.st_ino != fd_stat.st_ino) {
1063+ throw std::runtime_error("filename refers to a different file to the file descriptor");
1064+ }
1065+
1066+ std::string art = p->thumbnailer->get_thumbnail(
1067+ p->filename.toStdString(), p->size, TN_REMOTE);
1068+
1069+ if (art.empty()) {
1070+ throw std::runtime_error("Could not get thumbnail");
1071+ }
1072+
1073+ // FIXME: check that the thumbnail was produced for fd_stat
1074+ FdPtr fd(open(art.c_str(), O_RDONLY), ::close);
1075+ if (fd.get() < 0) {
1076+ throw std::runtime_error(safe_strerror(errno));
1077+ }
1078+ return QDBusUnixFileDescriptor(fd.get());
1079+}
1080+
1081+}
1082+}
1083+}
1084
1085=== added file 'src/service/thumbnailhandler.h'
1086--- src/service/thumbnailhandler.h 1970-01-01 00:00:00 +0000
1087+++ src/service/thumbnailhandler.h 2015-04-20 05:02:20 +0000
1088@@ -0,0 +1,59 @@
1089+/*
1090+ * Copyright (C) 2015 Canonical, Ltd.
1091+ *
1092+ * Authors:
1093+ * James Henstridge <james.henstridge@canonical.com>
1094+ *
1095+ * This library is free software; you can redistribute it and/or modify it under
1096+ * the terms of version 3 of the GNU General Public License as published
1097+ * by the Free Software Foundation.
1098+ *
1099+ * This library is distributed in the hope that it will be useful, but WITHOUT
1100+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1101+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1102+ * details.
1103+ *
1104+ * You should have received a copy of the GNU General Public License
1105+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1106+ */
1107+
1108+#pragma once
1109+
1110+#include "handler.h"
1111+#include <thumbnailer.h>
1112+
1113+#include <memory>
1114+
1115+#include <QObject>
1116+#include <QDBusConnection>
1117+#include <QDBusMessage>
1118+#include <QDBusUnixFileDescriptor>
1119+
1120+namespace unity {
1121+namespace thumbnailer {
1122+namespace service {
1123+
1124+struct ThumbnailHandlerPrivate;
1125+
1126+class ThumbnailHandler : public Handler {
1127+ Q_OBJECT
1128+public:
1129+ ThumbnailHandler(const QDBusConnection &bus, const QDBusMessage &message,
1130+ const std::shared_ptr<Thumbnailer> &thumbnailer,
1131+ const QString &filename,
1132+ const QDBusUnixFileDescriptor &filename_fd,
1133+ ThumbnailSize size);
1134+ ~ThumbnailHandler();
1135+
1136+protected:
1137+ virtual QDBusUnixFileDescriptor check() override;
1138+ virtual void download() override;
1139+ virtual QDBusUnixFileDescriptor create() override;
1140+
1141+private:
1142+ std::unique_ptr<ThumbnailHandlerPrivate> p;
1143+};
1144+
1145+}
1146+}
1147+}

Subscribers

People subscribed via source and target branches

to all changes: