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

KDECore

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

Skip menu "kdelibs 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
  • kjsembed
  •   WTF
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Nepomuk-Core
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



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