comparison vamp-client/ProcessQtTransport.h @ 126:2004ec2b653e

Ensure we read right up to end of buffered data after server exits; adjust waiting schedule on Windows (where waitForReadyRead is far too wasteful of sleep time)
author Chris Cannam <c.cannam@qmul.ac.uk>
date Fri, 28 Oct 2016 14:31:58 +0100
parents ea06fae1567c
children 3dcf0394971d
comparison
equal deleted inserted replaced
125:ea06fae1567c 126:2004ec2b653e
62 */ 62 */
63 class ProcessQtTransport : public SynchronousTransport 63 class ProcessQtTransport : public SynchronousTransport
64 { 64 {
65 public: 65 public:
66 ProcessQtTransport(std::string processName, std::string formatArg) : 66 ProcessQtTransport(std::string processName, std::string formatArg) :
67 m_completenessChecker(0) { 67 m_completenessChecker(0),
68 m_crashed(false) {
68 69
69 m_process = new QProcess(); 70 m_process = new QProcess();
70 m_process->setReadChannel(QProcess::StandardOutput); 71 m_process->setReadChannel(QProcess::StandardOutput);
71 m_process->setProcessChannelMode(QProcess::ForwardedErrorChannel); 72 m_process->setProcessChannelMode(QProcess::ForwardedErrorChannel);
72 73
108 } 109 }
109 } 110 }
110 111
111 void 112 void
112 setCompletenessChecker(MessageCompletenessChecker *checker) override { 113 setCompletenessChecker(MessageCompletenessChecker *checker) override {
113 //!!! ownership?
114 m_completenessChecker = checker; 114 m_completenessChecker = checker;
115 } 115 }
116 116
117 bool 117 bool
118 isOK() const override { 118 isOK() const override {
119 return m_process != nullptr; 119 return (m_process != nullptr) && !m_crashed;
120 } 120 }
121 121
122 std::vector<char> 122 std::vector<char>
123 call(const char *ptr, size_t size) override { 123 call(const char *ptr, size_t size, bool slow) override {
124 124
125 QMutexLocker locker(&m_mutex); 125 QMutexLocker locker(&m_mutex);
126 126
127 if (!m_completenessChecker) { 127 if (!m_completenessChecker) {
128 throw std::logic_error("No completeness checker set on transport"); 128 throw std::logic_error("No completeness checker set on transport");
129 } 129 }
130 if (!isOK()) {
131 throw std::logic_error("Transport is not OK");
132 }
130 133
131 #ifdef DEBUG_TRANSPORT 134 #ifdef DEBUG_TRANSPORT
132 std::cerr << "writing " << size << " bytes to server" << std::endl; 135 std::cerr << "writing " << size << " bytes to server" << std::endl;
133 #endif 136 #endif
134 m_process->write(ptr, size); 137 m_process->write(ptr, size);
135 m_process->waitForBytesWritten(1000); 138 m_process->waitForBytesWritten();
136 139
137 std::vector<char> buffer; 140 std::vector<char> buffer;
138 bool complete = false; 141 bool complete = false;
139 142
140 while (!complete) { 143 while (!complete) {
143 146
144 if (!byteCount) { 147 if (!byteCount) {
145 #ifdef DEBUG_TRANSPORT 148 #ifdef DEBUG_TRANSPORT
146 std::cerr << "waiting for data from server..." << std::endl; 149 std::cerr << "waiting for data from server..." << std::endl;
147 #endif 150 #endif
148 m_process->waitForReadyRead(1000); 151 if (slow) {
149 152 m_process->waitForReadyRead(1000);
150 if (m_process->state() == QProcess::NotRunning) { 153 } else {
154 #ifdef _WIN32
155 // This is most unsatisfactory -- if we give a non-zero
156 // arg here, then we end up sleeping way beyond the arrival
157 // of the data to read -- can end up using less than 10%
158 // CPU during processing which is crazy. So for Windows
159 // only, we busy-wait during "fast" calls. It works out
160 // much faster in the end. Could do with a simpler native
161 // blocking API really.
162 m_process->waitForReadyRead(0);
163 #else
164 m_process->waitForReadyRead(100);
165 #endif
166 }
167 if (m_process->state() == QProcess::NotRunning &&
168 // don't give up until we've read all that's been buffered!
169 !m_process->bytesAvailable()) {
151 QProcess::ProcessError err = m_process->error(); 170 QProcess::ProcessError err = m_process->error();
152 if (err == QProcess::Crashed) { 171 if (err == QProcess::Crashed) {
153 std::cerr << "Server crashed during request" << std::endl; 172 std::cerr << "Server crashed during request" << std::endl;
154 } else { 173 } else {
155 std::cerr << "Server failed during request with error code " 174 std::cerr << "Server failed during request with error code "
156 << err << std::endl; 175 << err << std::endl;
157 } 176 }
177 m_crashed = true;
158 throw ServerCrashed(); 178 throw ServerCrashed();
159 } 179 }
160 } else { 180 } else {
161 size_t formerSize = buffer.size(); 181 size_t formerSize = buffer.size();
162 buffer.resize(formerSize + byteCount); 182 buffer.resize(formerSize + byteCount);
170 190
171 private: 191 private:
172 MessageCompletenessChecker *m_completenessChecker; //!!! I don't own this (currently) 192 MessageCompletenessChecker *m_completenessChecker; //!!! I don't own this (currently)
173 QProcess *m_process; // I own this 193 QProcess *m_process; // I own this
174 QMutex m_mutex; 194 QMutex m_mutex;
195 bool m_crashed;
175 }; 196 };
176 197
177 } 198 }
178 } 199 }
179 200