annotate vamp-client/ProcessQtTransport.h @ 141:c3ddc4a72561

Fix compiler warning
author Chris Cannam <c.cannam@qmul.ac.uk>
date Mon, 16 Jan 2017 14:31:23 +0000
parents 3dcf0394971d
children 228a66adfb30
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@124 47 //#define DEBUG_TRANSPORT 1
c@124 48
c@97 49 namespace piper_vamp {
c@97 50 namespace client {
c@94 51
c@100 52 /**
c@100 53 * A SynchronousTransport implementation that spawns a sub-process
c@100 54 * using Qt's QProcess abstraction and talks to it via stdin/stdout
c@100 55 * channels. Calls are completely serialized; the protocol only
c@100 56 * supports one call in process at a time, and therefore the transport
c@125 57 * only allows one at a time.
c@125 58 *
c@125 59 * This class is thread-safe, but in practice you can only use it from
c@125 60 * within a single thread, because the underlying QProcess does not
c@125 61 * support switching threads.
c@100 62 */
c@94 63 class ProcessQtTransport : public SynchronousTransport
c@94 64 {
c@94 65 public:
c@134 66 ProcessQtTransport(std::string processName,
c@134 67 std::string formatArg,
c@134 68 LogCallback *logger) : // logger may be nullptr for cerr
c@134 69 m_logger(logger),
c@126 70 m_completenessChecker(0),
c@126 71 m_crashed(false) {
c@119 72
c@94 73 m_process = new QProcess();
c@94 74 m_process->setReadChannel(QProcess::StandardOutput);
c@94 75 m_process->setProcessChannelMode(QProcess::ForwardedErrorChannel);
cannam@114 76
c@119 77 m_process->start(QString::fromStdString(processName),
c@124 78 { QString::fromStdString(formatArg) });
cannam@114 79
c@94 80 if (!m_process->waitForStarted()) {
cannam@113 81 if (m_process->state() == QProcess::NotRunning) {
cannam@113 82 QProcess::ProcessError err = m_process->error();
cannam@113 83 if (err == QProcess::FailedToStart) {
c@134 84 log("Unable to start server process " + processName);
cannam@113 85 } else if (err == QProcess::Crashed) {
c@134 86 log("Server process " + processName + " crashed on startup");
cannam@113 87 } else {
c@134 88 QString e = QString("%1").arg(err);
c@134 89 log("Server process " + processName +
c@134 90 " failed on startup with error code " + e.toStdString());
cannam@113 91 }
cannam@113 92 delete m_process;
cannam@113 93 m_process = nullptr;
cannam@111 94 }
c@94 95 }
c@134 96
c@134 97 if (m_process) {
c@134 98 log("Server process " + processName + " started OK");
c@134 99 }
c@94 100 }
c@94 101
c@94 102 ~ProcessQtTransport() {
c@94 103 if (m_process) {
c@94 104 if (m_process->state() != QProcess::NotRunning) {
c@118 105 m_process->closeWriteChannel();
c@94 106 m_process->waitForFinished(200);
c@94 107 m_process->close();
c@94 108 m_process->waitForFinished();
c@134 109 log("Server process exited normally");
c@94 110 }
c@94 111 delete m_process;
c@94 112 }
c@94 113 }
c@94 114
c@94 115 void
cannam@111 116 setCompletenessChecker(MessageCompletenessChecker *checker) override {
c@94 117 m_completenessChecker = checker;
c@94 118 }
c@94 119
c@94 120 bool
c@94 121 isOK() const override {
c@126 122 return (m_process != nullptr) && !m_crashed;
c@94 123 }
c@94 124
c@94 125 std::vector<char>
c@134 126 call(const char *ptr, size_t size, std::string type, bool slow) override {
c@94 127
c@118 128 QMutexLocker locker(&m_mutex);
c@118 129
c@94 130 if (!m_completenessChecker) {
c@134 131 log("call: No completeness checker set on transport");
c@94 132 throw std::logic_error("No completeness checker set on transport");
c@94 133 }
c@126 134 if (!isOK()) {
c@134 135 log("call: Transport is not OK");
c@126 136 throw std::logic_error("Transport is not OK");
c@126 137 }
c@94 138
c@124 139 #ifdef DEBUG_TRANSPORT
c@115 140 std::cerr << "writing " << size << " bytes to server" << std::endl;
c@124 141 #endif
c@94 142 m_process->write(ptr, size);
c@126 143 m_process->waitForBytesWritten();
c@94 144
c@94 145 std::vector<char> buffer;
c@94 146 bool complete = false;
c@94 147
c@94 148 while (!complete) {
c@94 149
c@94 150 qint64 byteCount = m_process->bytesAvailable();
c@94 151
c@118 152 if (!byteCount) {
c@124 153 #ifdef DEBUG_TRANSPORT
c@118 154 std::cerr << "waiting for data from server..." << std::endl;
c@124 155 #endif
c@126 156 if (slow) {
c@126 157 m_process->waitForReadyRead(1000);
c@126 158 } else {
c@126 159 #ifdef _WIN32
c@126 160 // This is most unsatisfactory -- if we give a non-zero
c@126 161 // arg here, then we end up sleeping way beyond the arrival
c@126 162 // of the data to read -- can end up using less than 10%
c@126 163 // CPU during processing which is crazy. So for Windows
c@126 164 // only, we busy-wait during "fast" calls. It works out
c@126 165 // much faster in the end. Could do with a simpler native
c@126 166 // blocking API really.
c@126 167 m_process->waitForReadyRead(0);
c@126 168 #else
c@126 169 m_process->waitForReadyRead(100);
c@126 170 #endif
c@126 171 }
c@126 172 if (m_process->state() == QProcess::NotRunning &&
c@126 173 // don't give up until we've read all that's been buffered!
c@126 174 !m_process->bytesAvailable()) {
c@115 175 QProcess::ProcessError err = m_process->error();
c@115 176 if (err == QProcess::Crashed) {
c@134 177 log("Server crashed during " + type + " request");
c@115 178 } else {
c@134 179 QString e = QString("%1").arg(err);
c@134 180 log("Server failed during " + type
c@134 181 + " request with error code " + e.toStdString());
c@115 182 }
c@126 183 m_crashed = true;
c@121 184 throw ServerCrashed();
c@94 185 }
c@94 186 } else {
c@94 187 size_t formerSize = buffer.size();
c@94 188 buffer.resize(formerSize + byteCount);
c@94 189 m_process->read(buffer.data() + formerSize, byteCount);
c@94 190 complete = m_completenessChecker->isComplete(buffer);
c@94 191 }
c@94 192 }
c@94 193
c@94 194 return buffer;
c@94 195 }
c@94 196
c@94 197 private:
c@134 198 LogCallback *m_logger;
c@94 199 MessageCompletenessChecker *m_completenessChecker; //!!! I don't own this (currently)
c@94 200 QProcess *m_process; // I own this
c@100 201 QMutex m_mutex;
c@126 202 bool m_crashed;
c@134 203
c@134 204 void log(std::string message) const {
c@134 205 if (m_logger) m_logger->log(message);
c@134 206 else std::cerr << message << std::endl;
c@134 207 }
c@94 208 };
c@94 209
c@94 210 }
c@94 211 }
c@94 212
c@94 213 #endif