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