1
2
3 // Qt includes
4 #include <QCoreApplication>
5 #include <QThread>
6 #include <QTime>
7 #include <QSharedMemory>
8 #include <QProcess>
9 #include <QMutex>
10 #include <QSemaphore>
11 #include <QAtomicInt>
12 #include <QEventLoop>
13 #include <QTimer>
14
15 // System-specific includes
16 #if defined(_WIN32)
17 #include <windows.h>
18 #elif defined(MACOSX)
19 #include <sys/sysctl.h>
20 #include <unistd.h>
21 #elif defined(LINUX) || defined(FREEBSD)
22 #include <sys/time.h>
23 #include <unistd.h>
24 #endif
25
26 #include "tipc.h"
27
28 /*
29 PLATFORM-SPECIFIC REMINDERS:
30
31 There are few remarks to be aware when maintaining this code.
32 Please, be careful that:
33
34 - It seems that, on Windows, QLocalSocket::waitForBytesWritten does not return
35 success unless the data is actually read on the other end. On Unix this is not
36 the case, presumably because the data is written to a buffer which can be read
37 by
38 the other process at some later point.
39
40 Thus, *BE SURE* that every data written is received on the other side.
41
42 - On MACOSX, the default shared memory settings can be quite restrictive.
43 On a standard machine, the maximum size of a shared segment is 4 MB, exactly
44 the same as the TOTAL size of shared memory available.
45
46 Whereas tipc respects the former parameter, there must be a way to circumvent
47 the
48 latter in order to make the allocation of multiple shared segments of the
49 maximum
50 size.
51 */
52
53 //********************************************************
54 // Diagnostics Stuff
55 //********************************************************
56
57 //#define TIPC_DEBUG
58
59 #ifdef TIPC_DEBUG
60 #define tipc_debug(expr) expr
61 #else
62 #define tipc_debug(expr)
63 #endif
64
65 #ifdef TIPC_DEBUG
66 #include <QTime>
67 #endif
68
69 //********************************************************
70 // Local namespace Stuff
71 //********************************************************
72
73 namespace {
74 int shm_max = -1;
75 int shm_all = -1;
76 int shm_seg = -1;
77 int shm_mni = -1;
78 }
79
80 //********************************************************
81 // tipc Stream Implementation
82 //********************************************************
83
readSize()84 int tipc::Stream::readSize() {
85 if (m_socket->bytesAvailable() < sizeof(TINT32)) return -1;
86
87 TINT32 msgSize = -1;
88 m_socket->peek((char *)&msgSize, sizeof(TINT32));
89
90 return msgSize;
91 }
92
93 //-------------------------------------------------------------
94
messageReady()95 bool tipc::Stream::messageReady() {
96 TINT32 msgSize;
97 return (msgSize = readSize()) >= 0 && m_socket->bytesAvailable() >= msgSize;
98 }
99
100 //-------------------------------------------------------------
101
readData(char * data,qint64 dataSize,int msecs)102 bool tipc::Stream::readData(char *data, qint64 dataSize, int msecs) {
103 tipc_debug(qDebug("tipc::Stream::readData entry"));
104 qint64 r, dataRead = 0;
105 char *currData = data;
106
107 while (dataRead < dataSize) {
108 if ((m_socket->bytesAvailable() == 0) &&
109 !m_socket->waitForReadyRead(msecs)) {
110 tipc_debug(
111 qDebug("tipc::Stream::readData exit (unexpected loss of data)"));
112 return false;
113 }
114
115 // Read the supplied data
116 currData += r = m_socket->read(currData, dataSize - dataRead);
117 dataRead += r;
118 }
119
120 tipc_debug(qDebug("tipc::Stream::readData exit"));
121
122 return true;
123 }
124
125 //-------------------------------------------------------------
126
readDataNB(char * data,qint64 dataSize,int msecs,QEventLoop::ProcessEventsFlag flag)127 bool tipc::Stream::readDataNB(char *data, qint64 dataSize, int msecs,
128 QEventLoop::ProcessEventsFlag flag) {
129 tipc_debug(qDebug("tipc::Stream::readDataNB entry"));
130 qint64 r, dataRead = 0;
131 char *currData = data;
132
133 QEventLoop loop;
134 QObject::connect(m_socket, SIGNAL(readyRead()), &loop, SLOT(quit()));
135 QObject::connect(m_socket, SIGNAL(error(QLocalSocket::LocalSocketError)),
136 &loop, SLOT(quit()));
137
138 if (msecs >= 0) QTimer::singleShot(msecs, &loop, SLOT(quit()));
139
140 while (dataRead < dataSize) {
141 if (m_socket->bytesAvailable() == 0) {
142 loop.exec(flag);
143 if (m_socket->bytesAvailable() == 0) {
144 tipc_debug(
145 qDebug("tipc::Stream::readDataNB exit (unexpected loss of data)"));
146 return false;
147 }
148 }
149
150 // Read the supplied data
151 currData += r = m_socket->read(currData, dataSize - dataRead);
152 dataRead += r;
153 }
154
155 tipc_debug(qDebug("tipc::Stream::readDataNB exit"));
156
157 return true;
158 }
159
160 //-------------------------------------------------------------
161
162 /*!
163 Reads the message and returns its header.
164 This function reads a complete message from the socket, waiting
165 until it is completely available. The function accepts
166 an inactivity timeout which can be supplied to drop the operation
167 after msecs milliseconds no data has been received.
168 */
readMessage(Message & msg,int msecs)169 bool tipc::Stream::readMessage(Message &msg, int msecs) {
170 TINT32 msgSize = 0;
171 if (!readData((char *)&msgSize, sizeof(TINT32), msecs)) return false;
172
173 msg.ba().resize(msgSize);
174 if (!readData(msg.ba().data(), msgSize, msecs)) return false;
175
176 return true;
177 }
178
179 //-------------------------------------------------------------
180
181 /*!
182 The non-blocking equivalent to readMessage(), this function
183 performs event processing in a local event loop until all
184 message data has been received.
185 */
readMessageNB(Message & msg,int msecs,QEventLoop::ProcessEventsFlag flag)186 bool tipc::Stream::readMessageNB(Message &msg, int msecs,
187 QEventLoop::ProcessEventsFlag flag) {
188 TINT32 msgSize = 0;
189 if (!readDataNB((char *)&msgSize, sizeof(TINT32), msecs, flag)) return false;
190
191 msg.ba().resize(msgSize);
192 if (!readDataNB(msg.ba().data(), msgSize, msecs, flag)) return false;
193
194 return true;
195 }
196
197 //-------------------------------------------------------------
198
199 /*!
200 Flushes all data written to the stream.
201 This function waits until all data written on the stream
202 has been successfully delivered in output.
203 Returns true if the operation was successful, false if
204 it timed out or an error occurred.
205 */
flush(int msecs)206 bool tipc::Stream::flush(int msecs) {
207 tipc_debug(qDebug("tipc:flush entry"));
208
209 while (m_socket->bytesToWrite() > 0) {
210 tipc_debug(qDebug() << "bytes to write:" << m_socket->bytesToWrite());
211 bool ok = m_socket->flush();
212 tipc_debug(qDebug() << "flush success:" << ok
213 << "bytes to write:" << m_socket->bytesToWrite());
214 if (m_socket->bytesToWrite() > 0 && !m_socket->waitForBytesWritten(msecs))
215 return false;
216 }
217
218 tipc_debug(qDebug() << "tipc:flush exit - bytes to write:"
219 << m_socket->bytesToWrite());
220 return (m_socket->bytesToWrite() == 0);
221 }
222
223 //********************************************************
224 // tipc Stream Operators
225 //********************************************************
226
227 //! \warning This operation assumes that all the message is available for read.
228 //! Use tipc::stream::readMessage if this cannot be ensured.
operator >>(tipc::Stream & stream,tipc::Message & msg)229 tipc::Stream &operator>>(tipc::Stream &stream, tipc::Message &msg) {
230 QLocalSocket *socket = stream.socket();
231 msg.clear();
232
233 TINT32 msgSize;
234 socket->read((char *)&msgSize, sizeof(TINT32));
235 msg.ba().resize(msgSize);
236 socket->read(msg.ba().data(), msgSize);
237 return stream;
238 }
239
operator <<(tipc::Stream & stream,tipc::Message & msg)240 tipc::Stream &operator<<(tipc::Stream &stream, tipc::Message &msg) {
241 QLocalSocket *socket = stream.socket();
242
243 TINT32 size = msg.ba().size();
244 socket->write((char *)&size, sizeof(TINT32));
245 socket->write(msg.ba().data(), size);
246
247 return stream;
248 }
249
250 //********************************************************
251 // tipc Utilities
252 //********************************************************
253
254 /*!
255 Appends the invoking process' pid to the passed srvName.
256 */
applicationSpecificServerName(QString srvName)257 QString tipc::applicationSpecificServerName(QString srvName) {
258 return srvName + QString::number(QCoreApplication::applicationPid());
259 }
260
261 //-------------------------------------------------------------
262
startBackgroundProcess(QString cmdline)263 bool tipc::startBackgroundProcess(QString cmdline) {
264 #ifdef _WIN32
265 QProcess *proc = new QProcess;
266 proc->start(cmdline);
267 if (proc->state() == QProcess::NotRunning) {
268 delete proc;
269 return false;
270 }
271
272 QObject::connect(proc, SIGNAL(finished(int, QProcess::ExitStatus)), proc,
273 SLOT(deleteLater()));
274 QObject::connect(proc, SIGNAL(error(QProcess::ProcessError)), proc,
275 SLOT(deleteLater()));
276 return true;
277 #else
278 return QProcess::startDetached(cmdline);
279 ;
280 #endif
281 }
282
283 //-------------------------------------------------------------
284
285 /*!
286 Invokes the passed command line to run a slave server.
287 A slave server is hereby intended as a 'child' server process which
288 automatically destroys itself in case the calling application
289 crashes.
290 This process \b MUST support one server, running in the \b MAIN \b THREAD,
291 whose name is <srvName>_main.
292 This function waits until the main server is up and ready to
293 listen for incoming connections - no timeout accepted.
294
295 \warning Please, observe that a correct slave server name should be
296 ensured to be unique to the system.
297 */
startSlaveServer(QString srvName,QString cmdline)298 bool tipc::startSlaveServer(QString srvName, QString cmdline) {
299 if (!tipc::startBackgroundProcess(cmdline)) return false;
300
301 QString mainSrvName(srvName + "_main");
302
303 // Establish a dummy socket connection to provide a mean for the process
304 // to tell whether the calling process exited unexpectedly.
305 QLocalSocket *dummySock = new QLocalSocket;
306 dummySock->connectToServer(mainSrvName);
307
308 // Wait up to msecs until the socket is connecting. Wait a small amount of
309 // time
310 // until the server is up and listening to connection (there is no other way
311 // to tell).
312 while (dummySock->state() == QLocalSocket::UnconnectedState) {
313 #ifdef _WIN32
314 Sleep(10);
315 #else
316 usleep(10 << 10); // 10.24 msecs
317 #endif
318
319 dummySock->connectToServer(mainSrvName);
320 }
321
322 dummySock->waitForConnected(-1);
323
324 tipc::Stream stream(dummySock);
325 tipc::Message msg;
326
327 // Supply the 'quit if this socket connection fails' command
328 // This command ensure termination of the child process in case of some errors
329 // or ending of the program
330 stream << (msg << QString("$quit_on_error"));
331 if (tipc::readMessage(stream, msg, 3000) == QString()) {
332 std::cout << "tipc::startSlaveServer - tipc::readMessage TIMEOUT"
333 << std::endl;
334 return false;
335 }
336
337 // The server should die if dummyDock is destroyed. This should happen when
338 // the *MAIN* thread
339 // in *this process* exits. So, if this is not the main thread, we must move
340 // the socket there.
341 if (QCoreApplication::instance() &&
342 QThread::currentThread() != QCoreApplication::instance()->thread())
343 dummySock->moveToThread(QCoreApplication::instance()->thread());
344
345 // If a connection error takes place, release the dummy socket.
346 // Please, observe that this QObject::connect is invoked *AFTER* the
347 // connection trials above...
348 QObject::connect(dummySock, SIGNAL(error(QLocalSocket::LocalSocketError)),
349 dummySock, SLOT(deleteLater()));
350
351 return true;
352 }
353
354 //-------------------------------------------------------------
355
356 /*!
357 Connects the passed socket to the server with name <srvName> + <threadName>.
358 Awaits for the connection up to msecs milliseconds before returning false.
359 If no server was found, a new slave server is started by invoking
360 the supplied command line and connection is re-attempted.
361 Returns true on success, false otherwise.
362
363 \warning Please, observe that a correct slave server name should be
364 ensured to be unique to the parent process.
365 */
startSlaveConnection(QLocalSocket * socket,QString srvName,int msecs,QString cmdline,QString threadName)366 bool tipc::startSlaveConnection(QLocalSocket *socket, QString srvName,
367 int msecs, QString cmdline,
368 QString threadName) {
369 QTime time;
370 time.start();
371
372 if (msecs == -1) msecs = (std::numeric_limits<int>::max)();
373
374 QString fullSrvName(srvName + threadName);
375 socket->connectToServer(fullSrvName);
376
377 // If the socket is not connecting, the server lookup table returned that the
378 // no server with
379 // the passed name exists. This means that a server must be created.
380 if (socket->state() == QLocalSocket::UnconnectedState && !cmdline.isEmpty()) {
381 // Completely serialize the server start
382 static QMutex mutex;
383 QMutexLocker locker(&mutex);
384
385 // Retry connection - this is required due to the mutex
386 socket->connectToServer(fullSrvName);
387 if (socket->state() != QLocalSocket::UnconnectedState) goto connecting;
388
389 // Invoke the supplied command line to start the server
390 if (!tipc::startSlaveServer(srvName, cmdline)) return false;
391
392 // Reconnect to the server
393 socket->connectToServer(fullSrvName);
394 if (socket->state() == QLocalSocket::UnconnectedState) return false;
395 }
396
397 connecting:
398
399 // Now, the server is connecting or already connected. Wait until the socket
400 // is connected.
401 socket->waitForConnected(msecs - time.elapsed());
402 if (socket->state() != QLocalSocket::ConnectedState) return false;
403
404 return true;
405 }
406
407 //-------------------------------------------------------------
408
409 /*!
410 Waits and reads the next message from stream.
411 This function is mainly a useful macro that encapsulates
412 the following steps in one call:
413
414 \li Flush the write buffer (output messages)
415 \li Wait until an input message is completely readable
416 \li Read the message from stream
417 \li Read the first string from the message and return it
418
419 This function returns an empty QString if the message could not be
420 entirely retrieved from the stream.
421 */
readMessage(Stream & stream,Message & msg,int msecs)422 QString tipc::readMessage(Stream &stream, Message &msg, int msecs) {
423 msg.clear();
424 stream.flush();
425 if (!stream.readMessage(msg, msecs)) return QString();
426
427 QString res;
428 msg >> res;
429 return res;
430 }
431
432 //-------------------------------------------------------------
433
434 /*!
435 The non-blocking equivalent to tipc::readMessage.
436 */
readMessageNB(Stream & stream,Message & msg,int msecs,QEventLoop::ProcessEventsFlag flag)437 QString tipc::readMessageNB(Stream &stream, Message &msg, int msecs,
438 QEventLoop::ProcessEventsFlag flag) {
439 msg.clear();
440 if (!stream.readMessageNB(msg, msecs, flag)) return QString();
441
442 QString res;
443 msg >> res;
444 return res;
445 }
446
447 //-------------------------------------------------------------
448
449 /*!
450 Returns an inter-process unique id string; the returned
451 id should be used to create QSharedMemory objects.
452 */
uniqueId()453 QString tipc::uniqueId() {
454 static QAtomicInt count;
455 count.ref();
456 return QString::number(QCoreApplication::applicationPid()) + "_" +
457 QString::number((int)count);
458 }
459
460 //-------------------------------------------------------------
461
462 //! Returns the maximum size of a shared memory segment allowed by the system
shm_maxSegmentSize()463 int tipc::shm_maxSegmentSize() {
464 if (shm_max < 0) {
465 #ifdef MACOSX
466 // Retrieve it by invoking sysctl
467 size_t valSize = sizeof(TINT64);
468 TINT64 val;
469 sysctlbyname("kern.sysv.shmmax", &val, &valSize, NULL, 0);
470 shm_max = std::min(val, (TINT64)(std::numeric_limits<int>::max)());
471 #else
472 // Windows case: no such limit
473 // Observe that QSharedMemory accepts only an int size - so the num_lim is
474 // against int.
475 shm_max = (std::numeric_limits<int>::max)();
476 #endif
477 }
478
479 return shm_max;
480 }
481
482 //-------------------------------------------------------------
483
484 //! Returns the maximum number of shared segments allowed by the system
shm_maxSegmentCount()485 int tipc::shm_maxSegmentCount() {
486 if (shm_seg < 0) {
487 #ifdef MACOSX
488 size_t valSize = sizeof(TINT64);
489 TINT64 val;
490 sysctlbyname("kern.sysv.shmseg", &val, &valSize, NULL, 0);
491 shm_seg = std::min(val, (TINT64)(std::numeric_limits<int>::max)());
492 #else
493 // Windows case: no such limit - again, using limit against max due to Qt
494 shm_seg = (std::numeric_limits<int>::max)();
495 #endif
496 }
497
498 return shm_seg;
499 }
500
501 //-------------------------------------------------------------
502
shm_maxSharedPages()503 int tipc::shm_maxSharedPages() {
504 if (shm_all < 0) {
505 #ifdef MACOSX
506 size_t valSize = sizeof(TINT64);
507 TINT64 val;
508 sysctlbyname("kern.sysv.shmall", &val, &valSize, NULL, 0);
509 shm_all = std::min(val, (TINT64)(std::numeric_limits<int>::max)());
510 #else
511 shm_all = (std::numeric_limits<int>::max)();
512 #endif
513 }
514
515 return shm_all;
516 }
517
518 //-------------------------------------------------------------
519
shm_maxSharedCount()520 int tipc::shm_maxSharedCount() {
521 if (shm_mni < 0) {
522 #ifdef MACOSX
523 size_t valSize = sizeof(TINT64);
524 TINT64 val;
525 sysctlbyname("kern.sysv.shmmni", &val, &valSize, NULL, 0);
526 shm_mni = std::min(val, (TINT64)(std::numeric_limits<int>::max)());
527 #else
528 shm_mni = (std::numeric_limits<int>::max)();
529 #endif
530 }
531
532 return shm_mni;
533 }
534
535 //-------------------------------------------------------------
536
537 /*!
538 Attempts to set the shared memory parameters to the system.
539 This is only working on MAC's SystemV shm, it's a no-op on Win.
540 This function will fail anyway if the process is not owned by an
541 admin.
542 */
shm_set(int shmmax,int shmseg,int shmall,int shmmni)543 void tipc::shm_set(int shmmax, int shmseg, int shmall, int shmmni) {
544 tipc_debug(qDebug("shmmax: %i, shmseg: %i, shmall: %i, shmmni: %i", shmmax,
545 shmseg, shmall, shmmni));
546 #ifdef MACOSX
547 TINT64 val;
548 int err;
549 if (shmmax > 0) {
550 val = shmmax;
551 err = sysctlbyname("kern.sysv.shmmax", NULL, NULL, &val, sizeof(TINT64));
552 if (!err) shm_max = shmmax;
553 }
554 if (shmseg > 0) {
555 val = shmseg;
556 err = sysctlbyname("kern.sysv.shmseg", NULL, NULL, &val, sizeof(TINT64));
557 if (!err) shm_seg = shmseg;
558 }
559 if (shmall > 0) {
560 val = shmall;
561 err = sysctlbyname("kern.sysv.shmall", NULL, NULL, &val, sizeof(TINT64));
562 if (!err) shm_all = shmall;
563 }
564 if (shmmni > 0) {
565 val = shmmni;
566 err = sysctlbyname("kern.sysv.shmmni", NULL, NULL, &val, sizeof(TINT64));
567 if (!err) shm_mni = shmmni;
568 }
569 #endif
570 }
571
572 //-------------------------------------------------------------
573
574 /*!
575 Creates a shared memory segment for passed QSharedMemory.
576
577 This function attempts creation of a shared memory segment
578 in the form of Qt's QSharedMemory, with the following \b UNIX-specific
579 distinctions:
580
581 <LI> If the segment size is beyond that supported by the system,
582 the function can be set to either fail or return a segment with
583 the maximum supported size. <\LI>
584
585 <LI> Unlike QSharedMemory::create, this function attempts to
586 reclaim an already existing memory id before creating a new one. <\LI>
587 */
create(QSharedMemory & shmem,int size,bool strictSize)588 int tipc::create(QSharedMemory &shmem, int size, bool strictSize) {
589 bool ok, retried = false;
590
591 if (!strictSize) size = std::min(size, (int)shm_maxSegmentSize());
592
593 tipc_debug(qDebug() << "shMem create: size =" << size);
594
595 retry:
596
597 ok = shmem.create(size);
598 if (!ok) {
599 tipc_debug(qDebug() << "Error: Shared Segment could not be created: #"
600 << shmem.errorString());
601
602 // Unix-specific error recovery follows. See Qt's docs about it.
603
604 // Try to recover error #AlreadyExists - supposedly, the server crashed in a
605 // previous instance.
606 // As shared memory segments that happen to go this way are owned by the
607 // server process with 1
608 // reference count, detaching it now may solve the issue.
609 if (shmem.error() == QSharedMemory::AlreadyExists && !retried) {
610 retried = true; // We're trying this only once... for now it works.
611 shmem.attach();
612 shmem.detach();
613 goto retry;
614 }
615
616 return -1;
617 }
618
619 return size;
620 }
621
622 //-------------------------------------------------------------
623
624 /*!
625 Writes data through a shared memory segment medium.
626 */
writeShMemBuffer(Stream & stream,Message & msg,int bufSize,ShMemWriter * dataWriter)627 bool tipc::writeShMemBuffer(Stream &stream, Message &msg, int bufSize,
628 ShMemWriter *dataWriter) {
629 tipc_debug(QTime time; time.start());
630 tipc_debug(qDebug("tipc::writeShMemBuffer entry"));
631
632 static QSemaphore sem(tipc::shm_maxSegmentCount());
633 sem.acquire(1);
634
635 {
636 // Create a shared memory segment, possibly of passed size
637 QSharedMemory shmem(tipc::uniqueId());
638 bool ok = (tipc::create(shmem, bufSize) > 0);
639 if (!ok) goto err;
640
641 // Communicate the shared memory id and bufSize to the reader
642 msg << QString("shm") << shmem.key() << bufSize;
643
644 // Fill in data until all the buffer has been sent
645 int chunkData, remainingData = bufSize;
646 while (remainingData > 0) {
647 // Write to the shared memory segment
648 tipc_debug(QTime xchTime; xchTime.start());
649 shmem.lock();
650 remainingData -= chunkData = dataWriter->write(
651 (char *)shmem.data(), std::min(shmem.size(), remainingData));
652 shmem.unlock();
653 tipc_debug(qDebug() << "exchange time:" << xchTime.elapsed());
654
655 stream << (msg << QString("chk") << chunkData);
656
657 if (tipc::readMessage(stream, msg) != "ok") goto err;
658
659 msg.clear();
660 }
661 }
662
663 sem.release(1);
664 tipc_debug(qDebug("tipc::writeShMemBuffer exit"));
665 tipc_debug(qDebug() << "tipc::writeShMemBuffer time:" << time.elapsed());
666 return true;
667
668 err:
669
670 tipc_debug(qDebug("tipc::writeShMemBuffer exit (error)"));
671
672 msg.clear();
673 sem.release(1);
674 return false;
675 }
676
677 //-------------------------------------------------------------
678
679 /*!
680 Reads data through a shared memory segment medium.
681 */
readShMemBuffer(Stream & stream,Message & msg,ShMemReader * dataReader)682 bool tipc::readShMemBuffer(Stream &stream, Message &msg,
683 ShMemReader *dataReader) {
684 tipc_debug(QTime time; time.start(););
685 tipc_debug(qDebug("tipc::readShMemBuffer entry"));
686
687 // Read the id from stream
688 QString res(tipc::readMessage(stream, msg));
689 if (res != "shm") {
690 tipc_debug(qDebug("tipc::readShMemBuffer exit (res != \"shm\")"));
691 return false;
692 }
693
694 // Read message and reply
695 QString id, chkStr;
696 int bufSize;
697 msg >> id >> bufSize >> chkStr;
698
699 // Data is ready to be read - attach to the shared memory segment.
700 QSharedMemory shmem(id);
701 shmem.attach();
702 if (!shmem.isAttached()) {
703 tipc_debug(qDebug("tipc::readShMemBuffer exit (shmem not attached)"));
704 return false;
705 }
706
707 // Start reading from it
708 int chunkData, remainingData = bufSize;
709 while (true) {
710 msg >> chunkData;
711
712 tipc_debug(QTime xchTime; xchTime.start());
713 shmem.lock();
714 remainingData -= dataReader->read((const char *)shmem.data(), chunkData);
715 shmem.unlock();
716 tipc_debug(qDebug() << "exchange time:" << xchTime.elapsed());
717
718 // Data was read. Inform the writer
719 stream << (msg << clr << QString("ok"));
720 stream.flush();
721
722 if (remainingData <= 0) break;
723
724 // Wait for more chunks
725 if (tipc::readMessage(stream, msg) != "chk") {
726 tipc_debug(
727 qDebug("tipc::readShMemBuffer exit (unexpected chunk absence)"));
728 return false;
729 }
730 }
731
732 shmem.detach();
733 tipc_debug(qDebug("tipc::readShMemBuffer exit"));
734 tipc_debug(qDebug() << "tipc::readShMemBuffer time:" << time.elapsed());
735 return true;
736 }
737