CustusX  2023.01.05-dev+develop.0da12
An IGT application
cxSharedMemory.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 #include "cxSharedMemory.h"
12 
13 namespace cx
14 {
15 
16 // Shared header kept first in shared memory area
17 struct shm_header
18 {
19  qint32 lastDone; // index to last buffer that was written
20  qint32 writeBuffer; // index to the currently held write buffer (-1 if no buffer is held)
21  qint32 numBuffers; // number of buffers
22  qint32 bufferSize; // size of each buffer
23  qint32 headerSize; // size of this header
24  qint64 timestamp; // timestamp of last buffer that was written
25  qint32 buffer[0]; // number of readers currently operating on each buffer
26 };
27 
28 SharedMemoryServer::SharedMemoryServer(QString key, int buffers, int sizeEach, QObject *parent) : mBuffer(key, parent)
29 {
30  int headerSize = sizeof(struct shm_header) + buffers * sizeof(qint32);
31  mSize = sizeEach;
32  mBuffers = buffers;
33  int size = buffers * sizeEach + headerSize;
34  if (!mBuffer.create(size))
35  {
36  if (mBuffer.error() == QSharedMemory::AlreadyExists)
37  {
38  qWarning("Reusing existing buffer -- this should generally not happen");
39  // reuse and overwrite; hopefully it was made by previous run of same program that crashed
40  mBuffer.attach();
41  }
42  else
43  {
44  qWarning("Failed to create shared memory buffer of size %d: %s",
45  size, mBuffer.errorString().toLatin1().constData());
46  return;
47  }
48  }
49  struct shm_header *header = (struct shm_header *)mBuffer.data();
50  header->bufferSize = mSize;
51  header->numBuffers = mBuffers;
52  header->headerSize = headerSize;
53  header->lastDone = -1;
54  header->writeBuffer = -1;
55  header->timestamp = 0;
56  memset(header->buffer, 0, sizeof(qint32) * buffers);
57  mCurrentBuffer = -1;
58 }
59 
61 {
62 }
63 
64 // Find and return an available write buffer from the circle
66 {
67  struct shm_header *header = (struct shm_header *)mBuffer.data();
68  bool found = false;
69  if (header)
70  {
71  mBuffer.lock();
72  // Find first next buffer that is not being read; searching from left to right
73  for (int i = mCurrentBuffer + 1; i < header->numBuffers && !found; i++)
74  {
75  if (header->buffer[i] == 0) // no read locks
76  {
77  found = true;
78  internalRelease(false);
79  mCurrentBuffer = i;
80  }
81  }
82  for (int i = 0; i < header->numBuffers && !found; i++)
83  {
84  if (header->buffer[i] == 0) // no read locks
85  {
86  found = true;
87  internalRelease(false);
88  mCurrentBuffer = i;
89  }
90  }
91  if (!found)
92  {
93  qWarning("Could not find an available write buffer");
94  mBuffer.unlock();
95  return NULL;
96  }
97  void *ptr = ((char *)header) + header->headerSize + header->bufferSize * mCurrentBuffer;
98  header->writeBuffer = mCurrentBuffer;
99  mBuffer.unlock();
100  return ptr;
101  }
102  return NULL;
103 }
104 
105 // Set last finished buffer to current write buffer, then unset current write buffer index.
106 // Note that timestamp is only set here, since this is the only place where it can be set
107 // precisely.
108 void SharedMemoryServer::internalRelease(bool lock)
109 {
110  struct shm_header *header = (struct shm_header *)mBuffer.data();
111  if (header && mCurrentBuffer >= 0)
112  {
113  if (lock) mBuffer.lock();
114  header->lastDone = mCurrentBuffer;
115  header->writeBuffer = -1;
116  mLastTimestamp = QDateTime::currentDateTime();
117  header->timestamp = mLastTimestamp.toMSecsSinceEpoch();
118  if (lock) mBuffer.unlock();
119  mCurrentBuffer = -1;
120  }
121 }
122 
124 {
125  internalRelease(true);
126 }
127 
128 SharedMemoryClient::SharedMemoryClient(QObject *parent) : mBuffer(parent)
129 {
130  mSize = 0;
131  mBuffers = 0;
132  mCurrentBuffer = -1;
133 }
134 
135 bool SharedMemoryClient::attach(const QString &key)
136 {
137  mBuffer.setKey(key);
138  bool success = mBuffer.attach(QSharedMemory::ReadWrite);
139  if (success)
140  {
141  const struct shm_header *header = (const struct shm_header *)mBuffer.data();
142  mSize = header->bufferSize;
143  mBuffers = header->numBuffers;
144  }
145  return success;
146 }
147 
149 {
150  return mBuffer.detach();
151 }
152 
153 const void *SharedMemoryClient::buffer(bool onlyNew)
154 {
155  struct shm_header *header = (struct shm_header *)mBuffer.data();
156  if (header)
157  {
158  mBuffer.lock();
159  if (header->lastDone == -1 || header->lastDone == header->writeBuffer ||
160  ( onlyNew && header->lastDone == mCurrentBuffer) )
161  {
162  mBuffer.unlock();
163  return NULL; // Nothing
164  }
165  if (mCurrentBuffer >= 0 && header->buffer[mCurrentBuffer] > 0)
166  {
167  header->buffer[mCurrentBuffer]--; // Release previous read lock
168  }
169  header->buffer[header->lastDone]++; // Lock new page against writing
170  mCurrentBuffer = header->lastDone;
171  const void *ptr = ((const char *)header) + header->headerSize + header->bufferSize * header->lastDone;
172  mTimestamp.setMSecsSinceEpoch(header->timestamp);
173  mBuffer.unlock();
174  return ptr;
175  }
176  return NULL;
177 }
178 
180 {
181  return buffer(true);
182 }
183 
185 {
186  struct shm_header *header = (struct shm_header *)mBuffer.data();
187  if (header && mCurrentBuffer >= 0)
188  {
189  mBuffer.lock();
190  if (header->buffer[mCurrentBuffer] > 0)
191  {
192  header->buffer[mCurrentBuffer]--;
193  }
194  mBuffer.unlock();
195  mCurrentBuffer = -1;
196  }
197 }
198 
200 {
201  release();
202 }
203 
204 }
void release()
Release our read buffer.
SharedMemoryClient(QObject *parent=0)
const void * buffer(bool onlyNew=false)
Grab and lock a read buffer.
const void * isNew()
Return new buffer only if new is available, otherwise return NULL.
SharedMemoryServer(QString key, int buffers, int sizeEach, QObject *parent=0)
bool attach(const QString &key)
void release()
Release our write buffer. Buffer will not be used before it is released.
void * buffer()
Grab and lock a write buffer.
Namespace for all CustusX production code.