annotate vamp-client/ProcessQtTransport.h @ 117:5dffc5147176

Small simplification
author Chris Cannam <c.cannam@qmul.ac.uk>
date Thu, 27 Oct 2016 11:40:57 +0100
parents 5a716f08e4be
children ff3fd8d1b2dc
rev   line source
cannam@111 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
cannam@111 2
c@94 3 #ifndef PIPER_PROCESS_QT_TRANSPORT_H
c@94 4 #define PIPER_PROCESS_QT_TRANSPORT_H
c@94 5
c@94 6 #include "SynchronousTransport.h"
c@94 7
c@94 8 #include <QProcess>
c@94 9 #include <QString>
c@100 10 #include <QMutex>
c@94 11
c@94 12 #include <iostream>
c@94 13
c@97 14 namespace piper_vamp {
c@97 15 namespace client {
c@94 16
c@100 17 /**
c@100 18 * A SynchronousTransport implementation that spawns a sub-process
c@100 19 * using Qt's QProcess abstraction and talks to it via stdin/stdout
c@100 20 * channels. Calls are completely serialized; the protocol only
c@100 21 * supports one call in process at a time, and therefore the transport
c@100 22 * only allows one at a time. This class is thread-safe because it
c@100 23 * serializes explicitly using a mutex.
c@100 24 */
c@94 25 class ProcessQtTransport : public SynchronousTransport
c@94 26 {
c@94 27 public:
c@101 28 ProcessQtTransport(std::string processName) :
c@94 29 m_completenessChecker(0) {
c@94 30 m_process = new QProcess();
c@94 31 m_process->setReadChannel(QProcess::StandardOutput);
c@94 32 m_process->setProcessChannelMode(QProcess::ForwardedErrorChannel);
cannam@114 33 QString name(QString::fromStdString(processName));
cannam@114 34
cannam@114 35 // The second argument here is vital, otherwise we get a
cannam@114 36 // different start() overload which parses all command args
cannam@114 37 // out of its first argument only and therefore fails when
cannam@114 38 // name has a space in it. This is such a gotcha that Qt5.6
cannam@114 39 // even introduced a QT_NO_PROCESS_COMBINED_ARGUMENT_START
cannam@114 40 // build flag to disable that overload. Unfortunately I only
cannam@114 41 // discovered that after wasting almost a day on it.
cannam@114 42 m_process->start(name, QStringList());
cannam@114 43
c@94 44 if (!m_process->waitForStarted()) {
cannam@113 45 if (m_process->state() == QProcess::NotRunning) {
cannam@113 46 QProcess::ProcessError err = m_process->error();
cannam@113 47 if (err == QProcess::FailedToStart) {
cannam@113 48 std::cerr << "Unable to start server process "
cannam@113 49 << processName << std::endl;
cannam@113 50 } else if (err == QProcess::Crashed) {
cannam@113 51 std::cerr << "Server process " << processName
cannam@113 52 << " crashed on startup" << std::endl;
cannam@113 53 } else {
cannam@113 54 std::cerr << "Server process " << processName
cannam@113 55 << " failed on startup with error code "
cannam@113 56 << err << std::endl;
cannam@113 57 }
cannam@113 58 delete m_process;
cannam@113 59 m_process = nullptr;
cannam@111 60 }
c@94 61 }
c@94 62 }
c@94 63
c@94 64 ~ProcessQtTransport() {
c@94 65 if (m_process) {
c@94 66 if (m_process->state() != QProcess::NotRunning) {
c@94 67 m_process->closeWriteChannel();
c@94 68 m_process->waitForFinished(200);
c@94 69 m_process->close();
c@94 70 m_process->waitForFinished();
c@94 71 std::cerr << "server exited" << std::endl;
c@94 72 }
c@94 73 delete m_process;
c@94 74 }
c@94 75 }
c@94 76
c@94 77 void
cannam@111 78 setCompletenessChecker(MessageCompletenessChecker *checker) override {
c@94 79 //!!! ownership?
c@94 80 m_completenessChecker = checker;
c@94 81 }
c@94 82
c@94 83 bool
c@94 84 isOK() const override {
c@94 85 return m_process != nullptr;
c@94 86 }
c@94 87
c@94 88 std::vector<char>
c@94 89 call(const char *ptr, size_t size) override {
c@94 90
c@100 91 QMutexLocker locker(&m_mutex);
c@100 92
c@94 93 if (!m_completenessChecker) {
c@94 94 throw std::logic_error("No completeness checker set on transport");
c@94 95 }
c@94 96
c@115 97 std::cerr << "writing " << size << " bytes to server" << std::endl;
c@94 98 m_process->write(ptr, size);
c@94 99
c@94 100 std::vector<char> buffer;
c@94 101 bool complete = false;
c@94 102
c@94 103 while (!complete) {
c@94 104
c@94 105 qint64 byteCount = m_process->bytesAvailable();
c@94 106
c@101 107 if (!byteCount) {
c@108 108 std::cerr << "waiting for data from server..." << std::endl;
c@101 109 m_process->waitForReadyRead(1000);
c@94 110 if (m_process->state() == QProcess::NotRunning) {
c@115 111 QProcess::ProcessError err = m_process->error();
c@115 112 if (err == QProcess::Crashed) {
c@115 113 std::cerr << "Server crashed during request" << std::endl;
c@115 114 } else {
c@115 115 std::cerr << "Server failed during request with error code "
c@115 116 << err << std::endl;
c@115 117 }
c@115 118 //!!! + catch
c@94 119 throw std::runtime_error("Piper server exited unexpectedly");
c@94 120 }
c@94 121 } else {
c@94 122 size_t formerSize = buffer.size();
c@94 123 buffer.resize(formerSize + byteCount);
c@94 124 m_process->read(buffer.data() + formerSize, byteCount);
c@94 125 complete = m_completenessChecker->isComplete(buffer);
c@94 126 }
c@94 127 }
c@94 128
c@94 129 return buffer;
c@94 130 }
c@94 131
c@94 132 private:
c@94 133 MessageCompletenessChecker *m_completenessChecker; //!!! I don't own this (currently)
c@94 134 QProcess *m_process; // I own this
c@100 135 QMutex m_mutex;
c@94 136 };
c@94 137
c@94 138 }
c@94 139 }
c@94 140
c@94 141 #endif