author | Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com> |
Fri, 16 Apr 2010 15:50:13 +0300 | |
changeset 18 | 2f34d5167611 |
parent 0 | 1918ee327afb |
permissions | -rw-r--r-- |
0 | 1 |
/**************************************************************************** |
2 |
** |
|
18
2f34d5167611
Revision: 201011
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
3 |
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
0 | 4 |
** All rights reserved. |
5 |
** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 |
** |
|
7 |
** This file is part of the QtCore module of the Qt Toolkit. |
|
8 |
** |
|
9 |
** $QT_BEGIN_LICENSE:LGPL$ |
|
10 |
** No Commercial Usage |
|
11 |
** This file contains pre-release code and may not be distributed. |
|
12 |
** You may use this file in accordance with the terms and conditions |
|
13 |
** contained in the Technology Preview License Agreement accompanying |
|
14 |
** this package. |
|
15 |
** |
|
16 |
** GNU Lesser General Public License Usage |
|
17 |
** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 |
** General Public License version 2.1 as published by the Free Software |
|
19 |
** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 |
** packaging of this file. Please review the following information to |
|
21 |
** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 |
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 |
** |
|
24 |
** In addition, as a special exception, Nokia gives you certain additional |
|
25 |
** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 |
** |
|
28 |
** If you have questions regarding the use of this file, please contact |
|
29 |
** Nokia at qt-info@nokia.com. |
|
30 |
** |
|
31 |
** |
|
32 |
** |
|
33 |
** |
|
34 |
** |
|
35 |
** |
|
36 |
** |
|
37 |
** |
|
38 |
** $QT_END_LICENSE$ |
|
39 |
** |
|
40 |
****************************************************************************/ |
|
41 |
||
42 |
#include "qsharedmemory.h" |
|
43 |
#include "qsharedmemory_p.h" |
|
44 |
#include "qsystemsemaphore.h" |
|
45 |
#include <qdir.h> |
|
46 |
#include <qcryptographichash.h> |
|
47 |
#ifdef Q_OS_SYMBIAN |
|
48 |
#include <e32const.h> |
|
49 |
#endif |
|
50 |
#include <qdebug.h> |
|
51 |
||
52 |
QT_BEGIN_NAMESPACE |
|
53 |
||
54 |
#if !(defined(QT_NO_SHAREDMEMORY) && defined(QT_NO_SYSTEMSEMAPHORE)) |
|
55 |
/*! |
|
56 |
\internal |
|
57 |
||
58 |
Generate a string from the key which can be any unicode string into |
|
59 |
the subset that the win/unix kernel allows. |
|
60 |
||
61 |
On Unix this will be a file name |
|
62 |
On Symbian key will be truncated to 80 characters |
|
63 |
*/ |
|
64 |
QString |
|
65 |
QSharedMemoryPrivate::makePlatformSafeKey(const QString &key, |
|
66 |
const QString &prefix) |
|
67 |
{ |
|
68 |
if (key.isEmpty()) |
|
69 |
return QString(); |
|
70 |
||
71 |
QString result = prefix; |
|
72 |
||
73 |
QString part1 = key; |
|
74 |
part1.replace(QRegExp(QLatin1String("[^A-Za-z]")), QString()); |
|
75 |
result.append(part1); |
|
76 |
#ifdef Q_OS_SYMBIAN |
|
77 |
return result.left(KMaxKernelName); |
|
78 |
#endif |
|
79 |
||
80 |
QByteArray hex = QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Sha1).toHex(); |
|
81 |
result.append(QLatin1String(hex)); |
|
82 |
#ifdef Q_OS_WIN |
|
83 |
return result; |
|
84 |
#else |
|
85 |
return QDir::tempPath() + QLatin1Char('/') + result; |
|
86 |
#endif |
|
87 |
} |
|
88 |
#endif // QT_NO_SHAREDMEMORY && QT_NO_SHAREDMEMORY |
|
89 |
||
90 |
#ifndef QT_NO_SHAREDMEMORY |
|
91 |
||
92 |
/*! |
|
93 |
\class QSharedMemory |
|
94 |
\since 4.4 |
|
95 |
||
96 |
\brief The QSharedMemory class provides access to a shared memory segment. |
|
97 |
||
98 |
QSharedMemory provides access to a shared memory segment by multiple |
|
99 |
threads and processes. It also provides a way for a single thread or |
|
100 |
process to lock the memory for exclusive access. |
|
101 |
||
102 |
When using this class, be aware of the following platform |
|
103 |
differences: |
|
104 |
||
105 |
\list |
|
106 |
||
107 |
\o Windows: QSharedMemory does not "own" the shared memory segment. |
|
108 |
When all threads or processes that have an instance of QSharedMemory |
|
109 |
attached to a particular shared memory segment have either destroyed |
|
110 |
their instance of QSharedMemory or exited, the Windows kernel |
|
111 |
releases the shared memory segment automatically. |
|
112 |
||
113 |
\o Unix: QSharedMemory "owns" the shared memory segment. When the |
|
114 |
last thread or process that has an instance of QSharedMemory |
|
115 |
attached to a particular shared memory segment detaches from the |
|
116 |
segment by destroying its instance of QSharedMemory, the Unix kernel |
|
117 |
release the shared memory segment. But if that last thread or |
|
118 |
process crashes without running the QSharedMemory destructor, the |
|
119 |
shared memory segment survives the crash. |
|
120 |
||
121 |
\o HP-UX: Only one attach to a shared memory segment is allowed per |
|
122 |
process. This means that QSharedMemory should not be used across |
|
123 |
multiple threads in the same process in HP-UX. |
|
124 |
||
125 |
\o Symbian: QSharedMemory does not "own" the shared memory segment. |
|
126 |
When all threads or processes that have an instance of QSharedMemory |
|
127 |
attached to a particular shared memory segment have either destroyed |
|
128 |
their instance of QSharedMemory or exited, the Symbian kernel |
|
129 |
releases the shared memory segment automatically. |
|
130 |
Also, access to a shared memory segment cannot be limited to read-only |
|
131 |
in Symbian. |
|
132 |
||
133 |
\endlist |
|
134 |
||
135 |
Remember to lock the shared memory with lock() before reading from |
|
136 |
or writing to the shared memory, and remember to release the lock |
|
137 |
with unlock() after you are done. |
|
138 |
||
139 |
Unlike QtSharedMemory, QSharedMemory automatically destroys the |
|
140 |
shared memory segment when the last instance of QSharedMemory is |
|
141 |
detached from the segment, and no references to the segment |
|
142 |
remain. Do not mix using QtSharedMemory and QSharedMemory. Port |
|
143 |
everything to QSharedMemory. |
|
144 |
||
145 |
\warning QSharedMemory changes the key in a Qt-specific way. |
|
146 |
It is therefore currently not possible to use the shared memory of |
|
147 |
non-Qt applications with QSharedMemory. |
|
148 |
*/ |
|
149 |
||
150 |
/*! |
|
151 |
\overload QSharedMemory() |
|
152 |
||
153 |
Constructs a shared memory object with the given \a parent. The |
|
154 |
shared memory object's key is not set by the constructor, so the |
|
155 |
shared memory object does not have an underlying shared memory |
|
156 |
segment attached. The key must be set with setKey() before create() |
|
157 |
or attach() can be used. |
|
158 |
||
159 |
\sa setKey() |
|
160 |
*/ |
|
161 |
QSharedMemory::QSharedMemory(QObject *parent) |
|
162 |
: QObject(*new QSharedMemoryPrivate, parent) |
|
163 |
{ |
|
164 |
} |
|
165 |
||
166 |
/*! |
|
167 |
Constructs a shared memory object with the given \a parent and with |
|
168 |
its key set to \a key. Because its key is set, its create() and |
|
169 |
attach() functions can be called. |
|
170 |
||
171 |
\sa setKey(), create(), attach() |
|
172 |
*/ |
|
173 |
QSharedMemory::QSharedMemory(const QString &key, QObject *parent) |
|
174 |
: QObject(*new QSharedMemoryPrivate, parent) |
|
175 |
{ |
|
176 |
setKey(key); |
|
177 |
} |
|
178 |
||
179 |
/*! |
|
180 |
The destructor clears the key, which forces the shared memory object |
|
181 |
to \l {detach()} {detach} from its underlying shared memory |
|
182 |
segment. If this shared memory object is the last one connected to |
|
183 |
the shared memory segment, the detach() operation destroys the |
|
184 |
shared memory segment. |
|
185 |
||
186 |
\sa detach() isAttached() |
|
187 |
*/ |
|
188 |
QSharedMemory::~QSharedMemory() |
|
189 |
{ |
|
190 |
setKey(QString()); |
|
191 |
} |
|
192 |
||
193 |
/*! |
|
194 |
Sets a new \a key for this shared memory object. If \a key and the |
|
195 |
current key are the same, the function returns without doing |
|
196 |
anything. If the shared memory object is attached to an underlying |
|
197 |
shared memory segment, it will \l {detach()} {detach} from it before |
|
198 |
setting the new key. This function does not do an attach(). |
|
199 |
||
200 |
\sa key() isAttached() |
|
201 |
*/ |
|
202 |
void QSharedMemory::setKey(const QString &key) |
|
203 |
{ |
|
204 |
Q_D(QSharedMemory); |
|
205 |
if (key == d->key) |
|
206 |
return; |
|
207 |
||
208 |
if (isAttached()) |
|
209 |
detach(); |
|
210 |
d->cleanHandle(); |
|
211 |
d->key = key; |
|
212 |
} |
|
213 |
||
214 |
bool QSharedMemoryPrivate::initKey() |
|
215 |
{ |
|
216 |
if (!cleanHandle()) |
|
217 |
return false; |
|
218 |
#ifndef QT_NO_SYSTEMSEMAPHORE |
|
219 |
systemSemaphore.setKey(QString(), 1); |
|
220 |
systemSemaphore.setKey(key, 1); |
|
221 |
if (systemSemaphore.error() != QSystemSemaphore::NoError) { |
|
222 |
QString function = QLatin1String("QSharedMemoryPrivate::initKey"); |
|
223 |
errorString = QSharedMemory::tr("%1: unable to set key on lock").arg(function); |
|
224 |
switch(systemSemaphore.error()) { |
|
225 |
case QSystemSemaphore::PermissionDenied: |
|
226 |
error = QSharedMemory::PermissionDenied; |
|
227 |
break; |
|
228 |
case QSystemSemaphore::KeyError: |
|
229 |
error = QSharedMemory::KeyError; |
|
230 |
break; |
|
231 |
case QSystemSemaphore::AlreadyExists: |
|
232 |
error = QSharedMemory::AlreadyExists; |
|
233 |
break; |
|
234 |
case QSystemSemaphore::NotFound: |
|
235 |
error = QSharedMemory::NotFound; |
|
236 |
break; |
|
237 |
case QSystemSemaphore::OutOfResources: |
|
238 |
error = QSharedMemory::OutOfResources; |
|
239 |
break; |
|
240 |
case QSystemSemaphore::UnknownError: |
|
241 |
default: |
|
242 |
error = QSharedMemory::UnknownError; |
|
243 |
break; |
|
244 |
} |
|
245 |
return false; |
|
246 |
} |
|
247 |
#endif |
|
248 |
errorString = QString(); |
|
249 |
error = QSharedMemory::NoError; |
|
250 |
return true; |
|
251 |
} |
|
252 |
||
253 |
/*! |
|
254 |
Returns the key assigned to this shared memory. The key is the |
|
255 |
identifier used by the operating system to identify the shared |
|
256 |
memory segment. When QSharedMemory is used for interprocess |
|
257 |
communication, the key is how each process attaches to the shared |
|
258 |
memory segment through which the IPC occurs. |
|
259 |
||
260 |
\sa setKey() |
|
261 |
*/ |
|
262 |
QString QSharedMemory::key() const |
|
263 |
{ |
|
264 |
Q_D(const QSharedMemory); |
|
265 |
return d->key; |
|
266 |
} |
|
267 |
||
268 |
/*! |
|
269 |
Creates a shared memory segment of \a size bytes with the key passed |
|
270 |
to the constructor or set with setKey(), attaches to the new shared |
|
271 |
memory segment with the given access \a mode, and returns \tt true. |
|
272 |
If a shared memory segment identified by the key already exists, the |
|
273 |
attach operation is not performed, and \tt false is returned. When |
|
274 |
the return value is \tt false, call error() to determine which error |
|
275 |
occurred. |
|
276 |
||
277 |
\sa error() |
|
278 |
*/ |
|
279 |
bool QSharedMemory::create(int size, AccessMode mode) |
|
280 |
{ |
|
281 |
Q_D(QSharedMemory); |
|
282 |
||
283 |
if (!d->initKey()) |
|
284 |
return false; |
|
285 |
||
286 |
#ifndef QT_NO_SYSTEMSEMAPHORE |
|
287 |
#ifndef Q_OS_WIN |
|
288 |
// Take ownership and force set initialValue because the semaphore |
|
289 |
// might have already existed from a previous crash. |
|
290 |
d->systemSemaphore.setKey(d->key, 1, QSystemSemaphore::Create); |
|
291 |
#endif |
|
292 |
#endif |
|
293 |
||
294 |
QString function = QLatin1String("QSharedMemory::create"); |
|
295 |
#ifndef QT_NO_SYSTEMSEMAPHORE |
|
296 |
QSharedMemoryLocker lock(this); |
|
297 |
if (!d->tryLocker(&lock, function)) |
|
298 |
return false; |
|
299 |
#endif |
|
300 |
||
301 |
if (size <= 0) { |
|
302 |
d->error = QSharedMemory::InvalidSize; |
|
303 |
d->errorString = |
|
304 |
QSharedMemory::tr("%1: create size is less then 0").arg(function); |
|
305 |
return false; |
|
306 |
} |
|
307 |
||
308 |
if (!d->create(size)) |
|
309 |
return false; |
|
310 |
||
311 |
return d->attach(mode); |
|
312 |
} |
|
313 |
||
314 |
/*! |
|
315 |
Returns the size of the attached shared memory segment. If no shared |
|
316 |
memory segment is attached, 0 is returned. |
|
317 |
||
318 |
\sa create() attach() |
|
319 |
*/ |
|
320 |
int QSharedMemory::size() const |
|
321 |
{ |
|
322 |
Q_D(const QSharedMemory); |
|
323 |
return d->size; |
|
324 |
} |
|
325 |
||
326 |
/*! |
|
327 |
\enum QSharedMemory::AccessMode |
|
328 |
||
329 |
\value ReadOnly The shared memory segment is read-only. Writing to |
|
330 |
the shared memory segment is not allowed. An attempt to write to a |
|
331 |
shared memory segment created with ReadOnly causes the program to |
|
332 |
abort. |
|
333 |
||
334 |
\value ReadWrite Reading and writing the shared memory segment are |
|
335 |
both allowed. |
|
336 |
*/ |
|
337 |
||
338 |
/*! |
|
339 |
Attempts to attach the process to the shared memory segment |
|
340 |
identified by the key that was passed to the constructor or to a |
|
341 |
call to setKey(). The access \a mode is \l {QSharedMemory::} |
|
342 |
{ReadWrite} by default. It can also be \l {QSharedMemory::} |
|
343 |
{ReadOnly}. Returns true if the attach operation is successful. If |
|
344 |
false is returned, call error() to determine which error occurred. |
|
345 |
After attaching the shared memory segment, a pointer to the shared |
|
346 |
memory can be obtained by calling data(). |
|
347 |
||
348 |
\sa isAttached(), detach(), create() |
|
349 |
*/ |
|
350 |
bool QSharedMemory::attach(AccessMode mode) |
|
351 |
{ |
|
352 |
Q_D(QSharedMemory); |
|
353 |
||
354 |
if (isAttached() || !d->initKey()) |
|
355 |
return false; |
|
356 |
#ifndef QT_NO_SYSTEMSEMAPHORE |
|
357 |
QSharedMemoryLocker lock(this); |
|
358 |
if (!d->tryLocker(&lock, QLatin1String("QSharedMemory::attach"))) |
|
359 |
return false; |
|
360 |
#endif |
|
361 |
||
362 |
if (isAttached() || !d->handle()) |
|
363 |
return false; |
|
364 |
||
365 |
return d->attach(mode); |
|
366 |
} |
|
367 |
||
368 |
/*! |
|
369 |
Returns true if this process is attached to the shared memory |
|
370 |
segment. |
|
371 |
||
372 |
\sa attach(), detach() |
|
373 |
*/ |
|
374 |
bool QSharedMemory::isAttached() const |
|
375 |
{ |
|
376 |
Q_D(const QSharedMemory); |
|
377 |
return (0 != d->memory); |
|
378 |
} |
|
379 |
||
380 |
/*! |
|
381 |
Detaches the process from the shared memory segment. If this was the |
|
382 |
last process attached to the shared memory segment, then the shared |
|
383 |
memory segment is released by the system, i.e., the contents are |
|
384 |
destroyed. The function returns true if it detaches the shared |
|
385 |
memory segment. If it returns false, it usually means the segment |
|
386 |
either isn't attached, or it is locked by another process. |
|
387 |
||
388 |
\sa attach(), isAttached() |
|
389 |
*/ |
|
390 |
bool QSharedMemory::detach() |
|
391 |
{ |
|
392 |
Q_D(QSharedMemory); |
|
393 |
if (!isAttached()) |
|
394 |
return false; |
|
395 |
||
396 |
#ifndef QT_NO_SYSTEMSEMAPHORE |
|
397 |
QSharedMemoryLocker lock(this); |
|
398 |
if (!d->tryLocker(&lock, QLatin1String("QSharedMemory::detach"))) |
|
399 |
return false; |
|
400 |
#endif |
|
401 |
||
402 |
if (d->detach()) { |
|
403 |
d->size = 0; |
|
404 |
return true; |
|
405 |
} |
|
406 |
return false; |
|
407 |
} |
|
408 |
||
409 |
/*! |
|
410 |
Returns a pointer to the contents of the shared memory segment, if |
|
411 |
one is attached. Otherwise it returns null. Remember to lock the |
|
412 |
shared memory with lock() before reading from or writing to the |
|
413 |
shared memory, and remember to release the lock with unlock() after |
|
414 |
you are done. |
|
415 |
||
416 |
\sa attach() |
|
417 |
*/ |
|
418 |
void *QSharedMemory::data() |
|
419 |
{ |
|
420 |
Q_D(QSharedMemory); |
|
421 |
return d->memory; |
|
422 |
} |
|
423 |
||
424 |
/*! |
|
425 |
Returns a const pointer to the contents of the shared memory |
|
426 |
segment, if one is attached. Otherwise it returns null. Remember to |
|
427 |
lock the shared memory with lock() before reading from or writing to |
|
428 |
the shared memory, and remember to release the lock with unlock() |
|
429 |
after you are done. |
|
430 |
||
431 |
\sa attach() create() |
|
432 |
*/ |
|
433 |
const void* QSharedMemory::constData() const |
|
434 |
{ |
|
435 |
Q_D(const QSharedMemory); |
|
436 |
return d->memory; |
|
437 |
} |
|
438 |
||
439 |
/*! |
|
440 |
\overload data() |
|
441 |
*/ |
|
442 |
const void *QSharedMemory::data() const |
|
443 |
{ |
|
444 |
Q_D(const QSharedMemory); |
|
445 |
return d->memory; |
|
446 |
} |
|
447 |
||
448 |
#ifndef QT_NO_SYSTEMSEMAPHORE |
|
449 |
/*! |
|
450 |
This is a semaphore that locks the shared memory segment for access |
|
451 |
by this process and returns true. If another process has locked the |
|
452 |
segment, this function blocks until the lock is released. Then it |
|
453 |
acquires the lock and returns true. If this function returns false, |
|
454 |
it means either that you have ignored a false return from create() |
|
455 |
or attach(), or that QSystemSemaphore::acquire() failed due to an |
|
456 |
unknown system error. |
|
457 |
||
458 |
\sa unlock(), data(), QSystemSemaphore::acquire() |
|
459 |
*/ |
|
460 |
bool QSharedMemory::lock() |
|
461 |
{ |
|
462 |
Q_D(QSharedMemory); |
|
463 |
if (d->lockedByMe) { |
|
464 |
qWarning("QSharedMemory::lock: already locked"); |
|
465 |
return true; |
|
466 |
} |
|
467 |
if (d->systemSemaphore.acquire()) { |
|
468 |
d->lockedByMe = true; |
|
469 |
return true; |
|
470 |
} |
|
471 |
QString function = QLatin1String("QSharedMemory::lock"); |
|
472 |
d->errorString = QSharedMemory::tr("%1: unable to lock").arg(function); |
|
473 |
d->error = QSharedMemory::LockError; |
|
474 |
return false; |
|
475 |
} |
|
476 |
||
477 |
/*! |
|
478 |
Releases the lock on the shared memory segment and returns true, if |
|
479 |
the lock is currently held by this process. If the segment is not |
|
480 |
locked, or if the lock is held by another process, nothing happens |
|
481 |
and false is returned. |
|
482 |
||
483 |
\sa lock() |
|
484 |
*/ |
|
485 |
bool QSharedMemory::unlock() |
|
486 |
{ |
|
487 |
Q_D(QSharedMemory); |
|
488 |
if (!d->lockedByMe) |
|
489 |
return false; |
|
490 |
d->lockedByMe = false; |
|
491 |
if (d->systemSemaphore.release()) |
|
492 |
return true; |
|
493 |
QString function = QLatin1String("QSharedMemory::unlock"); |
|
494 |
d->errorString = QSharedMemory::tr("%1: unable to unlock").arg(function); |
|
495 |
d->error = QSharedMemory::LockError; |
|
496 |
return false; |
|
497 |
} |
|
498 |
#endif // QT_NO_SYSTEMSEMAPHORE |
|
499 |
||
500 |
/*! |
|
501 |
\enum QSharedMemory::SharedMemoryError |
|
502 |
||
503 |
\value NoError No error occurred. |
|
504 |
||
505 |
\value PermissionDenied The operation failed because the caller |
|
506 |
didn't have the required permissions. |
|
507 |
||
508 |
\value InvalidSize A create operation failed because the requested |
|
509 |
size was invalid. |
|
510 |
||
511 |
\value KeyError The operation failed because of an invalid key. |
|
512 |
||
513 |
\value AlreadyExists A create() operation failed because a shared |
|
514 |
memory segment with the specified key already existed. |
|
515 |
||
516 |
\value NotFound An attach() failed because a shared memory segment |
|
517 |
with the specified key could not be found. |
|
518 |
||
519 |
\value LockError The attempt to lock() the shared memory segment |
|
520 |
failed because create() or attach() failed and returned false, or |
|
521 |
because a system error occurred in QSystemSemaphore::acquire(). |
|
522 |
||
523 |
\value OutOfResources A create() operation failed because there was |
|
524 |
not enough memory available to fill the request. |
|
525 |
||
526 |
\value UnknownError Something else happened and it was bad. |
|
527 |
*/ |
|
528 |
||
529 |
/*! |
|
530 |
Returns a value indicating whether an error occurred, and, if so, |
|
531 |
which error it was. |
|
532 |
||
533 |
\sa errorString() |
|
534 |
*/ |
|
535 |
QSharedMemory::SharedMemoryError QSharedMemory::error() const |
|
536 |
{ |
|
537 |
Q_D(const QSharedMemory); |
|
538 |
return d->error; |
|
539 |
} |
|
540 |
||
541 |
/*! |
|
542 |
Returns a text description of the last error that occurred. If |
|
543 |
error() returns an \l {QSharedMemory::SharedMemoryError} {error |
|
544 |
value}, call this function to get a text string that describes the |
|
545 |
error. |
|
546 |
||
547 |
\sa error() |
|
548 |
*/ |
|
549 |
QString QSharedMemory::errorString() const |
|
550 |
{ |
|
551 |
Q_D(const QSharedMemory); |
|
552 |
return d->errorString; |
|
553 |
} |
|
554 |
||
555 |
#endif // QT_NO_SHAREDMEMORY |
|
556 |
||
557 |
QT_END_NAMESPACE |