CustusX  16.5.0-rc9
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 #include "cxSharedMemory.h"
33 
34 namespace cx
35 {
36 
37 // Shared header kept first in shared memory area
38 struct shm_header
39 {
40  qint32 lastDone; // index to last buffer that was written
41  qint32 writeBuffer; // index to the currently held write buffer (-1 if no buffer is held)
42  qint32 numBuffers; // number of buffers
43  qint32 bufferSize; // size of each buffer
44  qint32 headerSize; // size of this header
45  qint64 timestamp; // timestamp of last buffer that was written
46  qint32 buffer[0]; // number of readers currently operating on each buffer
47 };
48 
49 SharedMemoryServer::SharedMemoryServer(QString key, int buffers, int sizeEach, QObject *parent) : mBuffer(key, parent)
50 {
51  int headerSize = sizeof(struct shm_header) + buffers * sizeof(qint32);
52  mSize = sizeEach;
53  mBuffers = buffers;
54  int size = buffers * sizeEach + headerSize;
55  if (!mBuffer.create(size))
56  {
57  if (mBuffer.error() == QSharedMemory::AlreadyExists)
58  {
59  qWarning("Reusing existing buffer -- this should generally not happen");
60  // reuse and overwrite; hopefully it was made by previous run of same program that crashed
61  mBuffer.attach();
62  }
63  else
64  {
65  qWarning("Failed to create shared memory buffer of size %d: %s",
66  size, mBuffer.errorString().toLatin1().constData());
67  return;
68  }
69  }
70  struct shm_header *header = (struct shm_header *)mBuffer.data();
71  header->bufferSize = mSize;
72  header->numBuffers = mBuffers;
73  header->headerSize = headerSize;
74  header->lastDone = -1;
75  header->writeBuffer = -1;
76  header->timestamp = 0;
77  memset(header->buffer, 0, sizeof(qint32) * buffers);
78  mCurrentBuffer = -1;
79 }
80 
82 {
83 }
84 
85 // Find and return an available write buffer from the circle
87 {
88  struct shm_header *header = (struct shm_header *)mBuffer.data();
89  bool found = false;
90  if (header)
91  {
92  mBuffer.lock();
93  // Find first next buffer that is not being read; searching from left to right
94  for (int i = mCurrentBuffer + 1; i < header->numBuffers && !found; i++)
95  {
96  if (header->buffer[i] == 0) // no read locks
97  {
98  found = true;
99  internalRelease(false);
100  mCurrentBuffer = i;
101  }
102  }
103  for (int i = 0; i < header->numBuffers && !found; i++)
104  {
105  if (header->buffer[i] == 0) // no read locks
106  {
107  found = true;
108  internalRelease(false);
109  mCurrentBuffer = i;
110  }
111  }
112  if (!found)
113  {
114  qWarning("Could not find an available write buffer");
115  mBuffer.unlock();
116  return NULL;
117  }
118  void *ptr = ((char *)header) + header->headerSize + header->bufferSize * mCurrentBuffer;
119  header->writeBuffer = mCurrentBuffer;
120  mBuffer.unlock();
121  return ptr;
122  }
123  return NULL;
124 }
125 
126 // Set last finished buffer to current write buffer, then unset current write buffer index.
127 // Note that timestamp is only set here, since this is the only place where it can be set
128 // precisely.
129 void SharedMemoryServer::internalRelease(bool lock)
130 {
131  struct shm_header *header = (struct shm_header *)mBuffer.data();
132  if (header && mCurrentBuffer >= 0)
133  {
134  if (lock) mBuffer.lock();
135  header->lastDone = mCurrentBuffer;
136  header->writeBuffer = -1;
137  mLastTimestamp = QDateTime::currentDateTime();
138  header->timestamp = mLastTimestamp.toMSecsSinceEpoch();
139  if (lock) mBuffer.unlock();
140  mCurrentBuffer = -1;
141  }
142 }
143 
145 {
146  internalRelease(true);
147 }
148 
149 SharedMemoryClient::SharedMemoryClient(QObject *parent) : mBuffer(parent)
150 {
151  mSize = 0;
152  mBuffers = 0;
153  mCurrentBuffer = -1;
154 }
155 
156 bool SharedMemoryClient::attach(const QString &key)
157 {
158  mBuffer.setKey(key);
159  bool success = mBuffer.attach(QSharedMemory::ReadWrite);
160  if (success)
161  {
162  const struct shm_header *header = (const struct shm_header *)mBuffer.data();
163  mSize = header->bufferSize;
164  mBuffers = header->numBuffers;
165  }
166  return success;
167 }
168 
170 {
171  return mBuffer.detach();
172 }
173 
174 const void *SharedMemoryClient::buffer(bool onlyNew)
175 {
176  struct shm_header *header = (struct shm_header *)mBuffer.data();
177  if (header)
178  {
179  mBuffer.lock();
180  if (header->lastDone == -1 || header->lastDone == header->writeBuffer ||
181  ( onlyNew && header->lastDone == mCurrentBuffer) )
182  {
183  mBuffer.unlock();
184  return NULL; // Nothing
185  }
186  if (mCurrentBuffer >= 0 && header->buffer[mCurrentBuffer] > 0)
187  {
188  header->buffer[mCurrentBuffer]--; // Release previous read lock
189  }
190  header->buffer[header->lastDone]++; // Lock new page against writing
191  mCurrentBuffer = header->lastDone;
192  const void *ptr = ((const char *)header) + header->headerSize + header->bufferSize * header->lastDone;
193  mTimestamp.setMSecsSinceEpoch(header->timestamp);
194  mBuffer.unlock();
195  return ptr;
196  }
197  return NULL;
198 }
199 
201 {
202  return buffer(true);
203 }
204 
206 {
207  struct shm_header *header = (struct shm_header *)mBuffer.data();
208  if (header && mCurrentBuffer >= 0)
209  {
210  mBuffer.lock();
211  if (header->buffer[mCurrentBuffer] > 0)
212  {
213  header->buffer[mCurrentBuffer]--;
214  }
215  mBuffer.unlock();
216  mCurrentBuffer = -1;
217  }
218 }
219 
221 {
222  release();
223 }
224 
225 }
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.