Mercurial > hg > piper-cpp
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 |