annotate vamp-client/ProcessQtTransport.h @ 118:ff3fd8d1b2dc

Boilerplate comments
author Chris Cannam <c.cannam@qmul.ac.uk>
date Thu, 27 Oct 2016 12:01:37 +0100
parents 5a716f08e4be
children 5876f3e9c677
rev   line source
cannam@111 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
c@118 2 /*
c@118 3 Piper C++
c@118 4
c@118 5 An API for audio analysis and feature extraction plugins.
c@118 6
c@118 7 Centre for Digital Music, Queen Mary, University of London.
c@118 8 Copyright 2006-2016 Chris Cannam and QMUL.
c@118 9
c@118 10 Permission is hereby granted, free of charge, to any person
c@118 11 obtaining a copy of this software and associated documentation
c@118 12 files (the "Software"), to deal in the Software without
c@118 13 restriction, including without limitation the rights to use, copy,
c@118 14 modify, merge, publish, distribute, sublicense, and/or sell copies
c@118 15 of the Software, and to permit persons to whom the Software is
c@118 16 furnished to do so, subject to the following conditions:
c@118 17
c@118 18 The above copyright notice and this permission notice shall be
c@118 19 included in all copies or substantial portions of the Software.
c@118 20
c@118 21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
c@118 22 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
c@118 23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
c@118 24 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
c@118 25 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
c@118 26 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
c@118 27 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
c@118 28
c@118 29 Except as contained in this notice, the names of the Centre for
c@118 30 Digital Music; Queen Mary, University of London; and Chris Cannam
c@118 31 shall not be used in advertising or otherwise to promote the sale,
c@118 32 use or other dealings in this Software without prior written
c@118 33 authorization.
c@118 34 */
cannam@111 35
c@94 36 #ifndef PIPER_PROCESS_QT_TRANSPORT_H
c@94 37 #define PIPER_PROCESS_QT_TRANSPORT_H
c@94 38
c@94 39 #include "SynchronousTransport.h"
c@94 40
c@94 41 #include <QProcess>
c@94 42 #include <QString>
c@100 43 #include <QMutex>
c@94 44
c@94 45 #include <iostream>
c@94 46
c@97 47 namespace piper_vamp {
c@97 48 namespace client {
c@94 49
c@100 50 /**
c@100 51 * A SynchronousTransport implementation that spawns a sub-process
c@100 52 * using Qt's QProcess abstraction and talks to it via stdin/stdout
c@100 53 * channels. Calls are completely serialized; the protocol only
c@100 54 * supports one call in process at a time, and therefore the transport
c@100 55 * only allows one at a time. This class is thread-safe because it
c@100 56 * serializes explicitly using a mutex.
c@100 57 */
c@94 58 class ProcessQtTransport : public SynchronousTransport
c@94 59 {
c@94 60 public:
c@101 61 ProcessQtTransport(std::string processName) :
c@94 62 m_completenessChecker(0) {
c@94 63 m_process = new QProcess();
c@94 64 m_process->setReadChannel(QProcess::StandardOutput);
c@94 65 m_process->setProcessChannelMode(QProcess::ForwardedErrorChannel);
cannam@114 66 QString name(QString::fromStdString(processName));
cannam@114 67
cannam@114 68 // The second argument here is vital, otherwise we get a
cannam@114 69 // different start() overload which parses all command args
cannam@114 70 // out of its first argument only and therefore fails when
cannam@114 71 // name has a space in it. This is such a gotcha that Qt5.6
cannam@114 72 // even introduced a QT_NO_PROCESS_COMBINED_ARGUMENT_START
cannam@114 73 // build flag to disable that overload. Unfortunately I only
cannam@114 74 // discovered that after wasting almost a day on it.
cannam@114 75 m_process->start(name, QStringList());
cannam@114 76
c@94 77 if (!m_process->waitForStarted()) {
cannam@113 78 if (m_process->state() == QProcess::NotRunning) {
cannam@113 79 QProcess::ProcessError err = m_process->error();
cannam@113 80 if (err == QProcess::FailedToStart) {
cannam@113 81 std::cerr << "Unable to start server process "
cannam@113 82 << processName << std::endl;
cannam@113 83 } else if (err == QProcess::Crashed) {
cannam@113 84 std::cerr << "Server process " << processName
cannam@113 85 << " crashed on startup" << std::endl;
cannam@113 86 } else {
cannam@113 87 std::cerr << "Server process " << processName
cannam@113 88 << " failed on startup with error code "
cannam@113 89 << err << std::endl;
cannam@113 90 }
cannam@113 91 delete m_process;
cannam@113 92 m_process = nullptr;
cannam@111 93 }
c@94 94 }
c@94 95 }
c@94 96
c@94 97 ~ProcessQtTransport() {
c@94 98 if (m_process) {
c@94 99 if (m_process->state() != QProcess::NotRunning) {
c@118 100 m_process->closeWriteChannel();
c@94 101 m_process->waitForFinished(200);
c@94 102 m_process->close();
c@94 103 m_process->waitForFinished();
c@94 104 std::cerr << "server exited" << std::endl;
c@94 105 }
c@94 106 delete m_process;
c@94 107 }
c@94 108 }
c@94 109
c@94 110 void
cannam@111 111 setCompletenessChecker(MessageCompletenessChecker *checker) override {
c@94 112 //!!! ownership?
c@94 113 m_completenessChecker = checker;
c@94 114 }
c@94 115
c@94 116 bool
c@94 117 isOK() const override {
c@94 118 return m_process != nullptr;
c@94 119 }
c@94 120
c@94 121 std::vector<char>
c@94 122 call(const char *ptr, size_t size) override {
c@94 123
c@118 124 QMutexLocker locker(&m_mutex);
c@118 125
c@94 126 if (!m_completenessChecker) {
c@94 127 throw std::logic_error("No completeness checker set on transport");
c@94 128 }
c@94 129
c@115 130 std::cerr << "writing " << size << " bytes to server" << std::endl;
c@94 131 m_process->write(ptr, size);
c@94 132
c@94 133 std::vector<char> buffer;
c@94 134 bool complete = false;
c@94 135
c@94 136 while (!complete) {
c@94 137
c@94 138 qint64 byteCount = m_process->bytesAvailable();
c@94 139
c@118 140 if (!byteCount) {
c@118 141 std::cerr << "waiting for data from server..." << std::endl;
c@118 142 m_process->waitForReadyRead(1000);
c@94 143 if (m_process->state() == QProcess::NotRunning) {
c@115 144 QProcess::ProcessError err = m_process->error();
c@115 145 if (err == QProcess::Crashed) {
c@115 146 std::cerr << "Server crashed during request" << std::endl;
c@115 147 } else {
c@115 148 std::cerr << "Server failed during request with error code "
c@115 149 << err << std::endl;
c@115 150 }
c@115 151 //!!! + catch
c@94 152 throw std::runtime_error("Piper server exited unexpectedly");
c@94 153 }
c@94 154 } else {
c@94 155 size_t formerSize = buffer.size();
c@94 156 buffer.resize(formerSize + byteCount);
c@94 157 m_process->read(buffer.data() + formerSize, byteCount);
c@94 158 complete = m_completenessChecker->isComplete(buffer);
c@94 159 }
c@94 160 }
c@94 161
c@94 162 return buffer;
c@94 163 }
c@94 164
c@94 165 private:
c@94 166 MessageCompletenessChecker *m_completenessChecker; //!!! I don't own this (currently)
c@94 167 QProcess *m_process; // I own this
c@100 168 QMutex m_mutex;
c@94 169 };
c@94 170
c@94 171 }
c@94 172 }
c@94 173
c@94 174 #endif