• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.14.38 API Reference
  • KDE Home
  • Contact Us
 

KDECore

  • kdecore
  • util
kshareddatacache_p.h
Go to the documentation of this file.
1/*
2 * This file is part of the KDE project.
3 * Copyright © 2010 Michael Pyne <mpyne@kde.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License version 2 as published by the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#ifndef KSHAREDDATACACHE_P_H
21#define KSHAREDDATACACHE_P_H
22
23#include <config-util.h> // HAVE_SYS_MMAN_H
24
25#include <QtCore/QSharedPointer>
26#include <QtCore/QBasicAtomicInt>
27
28#include <unistd.h> // Check for sched_yield
29#include <sched.h> // sched_yield
30#include <errno.h>
31#include <fcntl.h>
32#include <time.h>
33
34#include <kdebug.h>
35
36#ifdef __APPLE__
37#include "posix_fallocate_mac.h"
38#endif
39
40// Our debug area, disabled by default
41int ksdcArea();
42
43// Mac OS X, for all its POSIX compliance, does not support timeouts on its
44// mutexes, which is kind of a disaster for cross-process support. However
45// synchronization primitives still work, they just might hang if the cache is
46// corrupted, so keep going.
47#if defined(_POSIX_TIMEOUTS) && ((_POSIX_TIMEOUTS == 0) || (_POSIX_TIMEOUTS >= 200112L))
48#define KSDC_TIMEOUTS_SUPPORTED 1
49#endif
50
51#if defined(__GNUC__) && !defined(KSDC_TIMEOUTS_SUPPORTED)
52#warning "No support for POSIX timeouts -- application hangs are possible if the cache is corrupt"
53#endif
54
55#if defined(_POSIX_THREAD_PROCESS_SHARED) && ((_POSIX_THREAD_PROCESS_SHARED == 0) || (_POSIX_THREAD_PROCESS_SHARED >= 200112L))
56#include <pthread.h>
57#define KSDC_THREAD_PROCESS_SHARED_SUPPORTED 1
58#endif
59
60#if defined(_POSIX_SEMAPHORES) && ((_POSIX_SEMAPHORES == 0) || (_POSIX_SEMAPHORES >= 200112L))
61#include <semaphore.h>
62#define KSDC_SEMAPHORES_SUPPORTED 1
63#endif
64
65#if defined(__GNUC__) && !defined(KSDC_SEMAPHORES_SUPPORTED) && !defined(KSDC_THREAD_PROCESS_SHARED_SUPPORTED)
66#warning "No system support claimed for process-shared synchronization, KSharedDataCache will be mostly useless."
67#endif
68
69#if defined(_POSIX_MAPPED_FILES) && ((_POSIX_MAPPED_FILES == 0) || (_POSIX_MAPPED_FILES >= 200112L))
70#define KSDC_MAPPED_FILES_SUPPORTED 1
71#endif
72
73#if defined(_POSIX_SYNCHRONIZED_IO) && ((_POSIX_SYNCHRONIZED_IO == 0) || (_POSIX_SYNCHRONIZED_IO >= 200112L))
74#define KSDC_SYNCHRONIZED_IO_SUPPORTED 1
75#endif
76
77// msync(2) requires both MAPPED_FILES and SYNCHRONIZED_IO POSIX options
78#if defined(KSDC_MAPPED_FILES_SUPPORTED) && defined(KSDC_SYNCHRONIZED_IO_SUPPORTED)
79#define KSDC_MSYNC_SUPPORTED
80#endif
81
82// posix_fallocate is used to ensure that the file used for the cache is
83// actually fully committed to disk before attempting to use the file.
84#if (defined(_POSIX_ADVISORY_INFO) && ((_POSIX_ADVISORY_INFO == 0) || (_POSIX_ADVISORY_INFO >= 200112L))) || defined(__APPLE__)
85#define KSDC_POSIX_FALLOCATE_SUPPORTED 1
86#endif
87
88// BSD/Mac OS X compat
89#if HAVE_SYS_MMAN_H
90#include <sys/mman.h>
91#endif
92#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
93#define MAP_ANONYMOUS MAP_ANON
94#endif
95
101class KSDCLock {
102public:
103 virtual ~KSDCLock()
104 {
105 }
106
107 // Return value indicates if the mutex was properly initialized (including
108 // threads-only as a fallback).
109 virtual bool initialize(bool &processSharingSupported)
110 {
111 processSharingSupported = false;
112 return false;
113 }
114
115 virtual bool lock()
116 {
117 return false;
118 }
119
120 virtual void unlock()
121 {
122 }
123};
124
130class simpleSpinLock : public KSDCLock
131{
132public:
133 simpleSpinLock(QBasicAtomicInt &spinlock)
134 : m_spinlock(spinlock)
135 {
136 }
137
138 virtual bool initialize(bool &processSharingSupported)
139 {
140 // Clear the spinlock
141 m_spinlock = 0;
142 processSharingSupported = true;
143 return true;
144 }
145
146 virtual bool lock()
147 {
148 // Spin a few times attempting to gain the lock, as upper-level code won't
149 // attempt again without assuming the cache is corrupt.
150 for (unsigned i = 50; i > 0; --i) {
151 if (m_spinlock.testAndSetAcquire(0, 1)) {
152 return true;
153 }
154
155 // Don't steal the processor and starve the thread we're waiting
156 // on.
157 loopSpinPause();
158 }
159
160 return false;
161 }
162
163 virtual void unlock()
164 {
165 m_spinlock.testAndSetRelease(1, 0);
166 }
167
168private:
169#ifdef Q_CC_GNU
170 __attribute__((always_inline, gnu_inline, artificial))
171#endif
172 static inline void loopSpinPause()
173 {
174 // TODO: Spinning might be better in multi-core systems... but that means
175 // figuring how to find numbers of CPUs in a cross-platform way.
176#ifdef _POSIX_PRIORITY_SCHEDULING
177 sched_yield();
178#else
179 // Sleep for shortest possible time (nanosleep should round-up).
180 struct timespec wait_time = { 0 /* sec */, 100 /* ns */ };
181 ::nanosleep(&wait_time, static_cast<struct timespec*>(0));
182#endif
183 }
184
185 QBasicAtomicInt &m_spinlock;
186};
187
188#ifdef KSDC_THREAD_PROCESS_SHARED_SUPPORTED
189class pthreadLock : public KSDCLock
190{
191public:
192 pthreadLock(pthread_mutex_t &mutex)
193 : m_mutex(mutex)
194 {
195 }
196
197 virtual bool initialize(bool &processSharingSupported)
198 {
199 // Setup process-sharing.
200 pthread_mutexattr_t mutexAttr;
201 processSharingSupported = false;
202
203 // Initialize attributes, enable process-shared primitives, and setup
204 // the mutex.
205 if (::sysconf(_SC_THREAD_PROCESS_SHARED) >= 200112L && pthread_mutexattr_init(&mutexAttr) == 0) {
206 if (pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED) == 0 &&
207 pthread_mutex_init(&m_mutex, &mutexAttr) == 0)
208 {
209 processSharingSupported = true;
210 }
211 pthread_mutexattr_destroy(&mutexAttr);
212 }
213
214 // Attempt to setup for thread-only synchronization.
215 if (!processSharingSupported && pthread_mutex_init(&m_mutex, NULL) != 0) {
216 return false;
217 }
218
219 return true;
220 }
221
222 virtual bool lock()
223 {
224 return pthread_mutex_lock(&m_mutex) == 0;
225 }
226
227 virtual void unlock()
228 {
229 pthread_mutex_unlock(&m_mutex);
230 }
231
232protected:
233 pthread_mutex_t &m_mutex;
234};
235#endif
236
237#if defined(KSDC_THREAD_PROCESS_SHARED_SUPPORTED) && defined(KSDC_TIMEOUTS_SUPPORTED)
238class pthreadTimedLock : public pthreadLock
239{
240public:
241 pthreadTimedLock(pthread_mutex_t &mutex)
242 : pthreadLock(mutex)
243 {
244 }
245
246 virtual bool lock()
247 {
248 struct timespec timeout;
249
250 // Long timeout, but if we fail to meet this timeout it's probably a cache
251 // corruption (and if we take 8 seconds then it should be much much quicker
252 // the next time anyways since we'd be paged back in from disk)
253 timeout.tv_sec = 10 + ::time(NULL); // Absolute time, so 10 seconds from now
254 timeout.tv_nsec = 0;
255
256 return pthread_mutex_timedlock(&m_mutex, &timeout) == 0;
257 }
258};
259#endif
260
261#ifdef KSDC_SEMAPHORES_SUPPORTED
262class semaphoreLock : public KSDCLock
263{
264public:
265 semaphoreLock(sem_t &semaphore)
266 : m_semaphore(semaphore)
267 {
268 }
269
270 virtual bool initialize(bool &processSharingSupported)
271 {
272 processSharingSupported = false;
273 if (::sysconf(_SC_SEMAPHORES) < 200112L) {
274 return false;
275 }
276
277 // sem_init sets up process-sharing for us.
278 if (sem_init(&m_semaphore, 1, 1) == 0) {
279 processSharingSupported = true;
280 }
281 // If not successful try falling back to thread-shared.
282 else if (sem_init(&m_semaphore, 0, 1) != 0) {
283 return false;
284 }
285
286 return true;
287 }
288
289 virtual bool lock()
290 {
291 return sem_wait(&m_semaphore) == 0;
292 }
293
294 virtual void unlock()
295 {
296 sem_post(&m_semaphore);
297 }
298
299protected:
300 sem_t &m_semaphore;
301};
302#endif
303
304#if defined(KSDC_SEMAPHORES_SUPPORTED) && defined(KSDC_TIMEOUTS_SUPPORTED)
305class semaphoreTimedLock : public semaphoreLock
306{
307public:
308 semaphoreTimedLock(sem_t &semaphore)
309 : semaphoreLock(semaphore)
310 {
311 }
312
313 virtual bool lock()
314 {
315 struct timespec timeout;
316
317 // Long timeout, but if we fail to meet this timeout it's probably a cache
318 // corruption (and if we take 8 seconds then it should be much much quicker
319 // the next time anyways since we'd be paged back in from disk)
320 timeout.tv_sec = 10 + ::time(NULL); // Absolute time, so 10 seconds from now
321 timeout.tv_nsec = 0;
322
323 return sem_timedwait(&m_semaphore, &timeout) == 0;
324 }
325};
326#endif
327
328// This enum controls the type of the locking used for the cache to allow
329// for as much portability as possible. This value will be stored in the
330// cache and used by multiple processes, therefore you should consider this
331// a versioned field, do not re-arrange.
332enum SharedLockId {
333 LOCKTYPE_INVALID = 0,
334 LOCKTYPE_MUTEX = 1, // pthread_mutex
335 LOCKTYPE_SEMAPHORE = 2, // sem_t
336 LOCKTYPE_SPINLOCK = 3 // atomic int in shared memory
337};
338
339// This type is a union of all possible lock types, with a SharedLockId used
340// to choose which one is actually in use.
341struct SharedLock
342{
343 union
344 {
345#if defined(KSDC_THREAD_PROCESS_SHARED_SUPPORTED)
346 pthread_mutex_t mutex;
347#endif
348#if defined(KSDC_SEMAPHORES_SUPPORTED)
349 sem_t semaphore;
350#endif
351 QBasicAtomicInt spinlock;
352
353 // It would be highly unfortunate if a simple glibc upgrade or kernel
354 // addition caused this structure to change size when an existing
355 // lock was thought present, so reserve enough size to cover any
356 // reasonable locking structure
357 char unused[64];
358 };
359
360 SharedLockId type;
361};
362
368static SharedLockId findBestSharedLock()
369{
370 // We would prefer a process-shared capability that also supports
371 // timeouts. Failing that, process-shared is preferred over timeout
372 // support. Failing that we'll go thread-local
373 bool timeoutsSupported = false;
374 bool pthreadsProcessShared = false;
375 bool semaphoresProcessShared = false;
376
377#ifdef KSDC_TIMEOUTS_SUPPORTED
378 timeoutsSupported = ::sysconf(_SC_TIMEOUTS) >= 200112L;
379#endif
380
381 // Now that we've queried timeouts, try actually creating real locks and
382 // seeing if there's issues with that.
383#ifdef KSDC_THREAD_PROCESS_SHARED_SUPPORTED
384 {
385 pthread_mutex_t tempMutex;
386 QSharedPointer<KSDCLock> tempLock(0);
387 if (timeoutsSupported) {
388#ifdef KSDC_TIMEOUTS_SUPPORTED
389 tempLock = QSharedPointer<KSDCLock>(new pthreadTimedLock(tempMutex));
390#endif
391 }
392 else {
393 tempLock = QSharedPointer<KSDCLock>(new pthreadLock(tempMutex));
394 }
395
396 tempLock->initialize(pthreadsProcessShared);
397 }
398#endif
399
400 // Our first choice is pthread_mutex_t for compatibility.
401 if(timeoutsSupported && pthreadsProcessShared) {
402 return LOCKTYPE_MUTEX;
403 }
404
405#ifdef KSDC_SEMAPHORES_SUPPORTED
406 {
407 sem_t tempSemaphore;
408 QSharedPointer<KSDCLock> tempLock(0);
409 if (timeoutsSupported) {
410 tempLock = QSharedPointer<KSDCLock>(new semaphoreTimedLock(tempSemaphore));
411 }
412 else {
413 tempLock = QSharedPointer<KSDCLock>(new semaphoreLock(tempSemaphore));
414 }
415
416 tempLock->initialize(semaphoresProcessShared);
417 }
418#endif
419
420 if(timeoutsSupported && semaphoresProcessShared) {
421 return LOCKTYPE_SEMAPHORE;
422 }
423 else if(pthreadsProcessShared) {
424 return LOCKTYPE_MUTEX;
425 }
426 else if(semaphoresProcessShared) {
427 return LOCKTYPE_SEMAPHORE;
428 }
429
430 // Fallback to a dumb-simple but possibly-CPU-wasteful solution.
431 return LOCKTYPE_SPINLOCK;
432}
433
434static KSDCLock *createLockFromId(SharedLockId id, SharedLock &lock)
435{
436 switch(id) {
437#ifdef KSDC_THREAD_PROCESS_SHARED_SUPPORTED
438 case LOCKTYPE_MUTEX:
439#ifdef KSDC_TIMEOUTS_SUPPORTED
440 if (::sysconf(_SC_TIMEOUTS) >= 200112L) {
441 return new pthreadTimedLock(lock.mutex);
442 }
443#endif
444 return new pthreadLock(lock.mutex);
445
446 break;
447#endif
448
449#ifdef KSDC_SEMAPHORES_SUPPORTED
450 case LOCKTYPE_SEMAPHORE:
451#ifdef KSDC_TIMEOUTS_SUPPORTED
452 if (::sysconf(_SC_SEMAPHORES) >= 200112L) {
453 return new semaphoreTimedLock(lock.semaphore);
454 }
455#endif
456 return new semaphoreLock(lock.semaphore);
457
458 break;
459#endif
460
461 case LOCKTYPE_SPINLOCK:
462 return new simpleSpinLock(lock.spinlock);
463 break;
464
465 default:
466 kError(ksdcArea()) << "Creating shell of a lock!";
467 return new KSDCLock;
468 }
469}
470
471static bool ensureFileAllocated(int fd, size_t fileSize)
472{
473#ifdef KSDC_POSIX_FALLOCATE_SUPPORTED
474 int result;
475 while ((result = ::posix_fallocate(fd, 0, fileSize)) == EINTR) {
476 ;
477 }
478
479 if (result < 0) {
480 kError(ksdcArea()) << "The operating system is unable to promise"
481 << fileSize
482 << "bytes for mapped cache, "
483 "abandoning the cache for crash-safety.";
484 return false;
485 }
486
487 return true;
488#else
489
490#ifdef __GNUC__
491#warning "This system does not seem to support posix_fallocate, which is needed to ensure KSharedDataCache's underlying files are fully committed to disk to avoid crashes with low disk space."
492#endif
493 kWarning(ksdcArea()) << "This system misses support for posix_fallocate()"
494 " -- ensure this partition has room for at least"
495 << fileSize << "bytes.";
496
497 // TODO: It's possible to emulate the functionality, but doing so
498 // overwrites the data in the file so we don't do this. If you were to add
499 // this emulation, you must ensure it only happens on initial creation of a
500 // new file and not just mapping an existing cache.
501
502 return true;
503#endif
504}
505
506#endif /* KSHAREDDATACACHE_P_H */
KSDCLock
This class defines an interface used by KSharedDataCache::Private to offload proper locking and unloc...
Definition: kshareddatacache_p.h:101
KSDCLock::initialize
virtual bool initialize(bool &processSharingSupported)
Definition: kshareddatacache_p.h:109
KSDCLock::unlock
virtual void unlock()
Definition: kshareddatacache_p.h:120
KSDCLock::lock
virtual bool lock()
Definition: kshareddatacache_p.h:115
KSDCLock::~KSDCLock
virtual ~KSDCLock()
Definition: kshareddatacache_p.h:103
simpleSpinLock
This is a very basic lock that should work on any system where GCC atomic intrinsics are supported.
Definition: kshareddatacache_p.h:131
simpleSpinLock::lock
virtual bool lock()
Definition: kshareddatacache_p.h:146
simpleSpinLock::unlock
virtual void unlock()
Definition: kshareddatacache_p.h:163
simpleSpinLock::simpleSpinLock
simpleSpinLock(QBasicAtomicInt &spinlock)
Definition: kshareddatacache_p.h:133
simpleSpinLock::initialize
virtual bool initialize(bool &processSharingSupported)
Definition: kshareddatacache_p.h:138
kWarning
#define kWarning
Definition: kdebug.h:322
kError
static QDebug kError(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
Definition: kdebug.h:187
kdebug.h
timeout
int timeout
Definition: kkernel_mac.cpp:46
ensureFileAllocated
static bool ensureFileAllocated(int fd, size_t fileSize)
Definition: kshareddatacache_p.h:471
SharedLockId
SharedLockId
Definition: kshareddatacache_p.h:332
LOCKTYPE_SEMAPHORE
@ LOCKTYPE_SEMAPHORE
Definition: kshareddatacache_p.h:335
LOCKTYPE_SPINLOCK
@ LOCKTYPE_SPINLOCK
Definition: kshareddatacache_p.h:336
LOCKTYPE_MUTEX
@ LOCKTYPE_MUTEX
Definition: kshareddatacache_p.h:334
LOCKTYPE_INVALID
@ LOCKTYPE_INVALID
Definition: kshareddatacache_p.h:333
findBestSharedLock
static SharedLockId findBestSharedLock()
This is a method to determine the best lock type to use for a shared cache, based on local support.
Definition: kshareddatacache_p.h:368
createLockFromId
static KSDCLock * createLockFromId(SharedLockId id, SharedLock &lock)
Definition: kshareddatacache_p.h:434
ksdcArea
int ksdcArea()
Definition: kshareddatacache.cpp:47
posix_fallocate_mac.h
posix_fallocate
static int posix_fallocate(int fd, off_t offset, off_t len)
Definition: posix_fallocate_mac.h:51
SharedLock
Definition: kshareddatacache_p.h:342
SharedLock::spinlock
QBasicAtomicInt spinlock
Definition: kshareddatacache_p.h:351
SharedLock::unused
char unused[64]
Definition: kshareddatacache_p.h:357
SharedLock::type
SharedLockId type
Definition: kshareddatacache_p.h:360
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDECore

Skip menu "KDECore"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Modules
  • Related Pages

kdelibs-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal