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

KDECore

  • sources
  • kde-4.14
  • 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 #ifdef __APPLE__
37 #include "posix_fallocate_mac.h"
38 #endif
39 
40 // Our debug area, disabled by default
41 int 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 
101 class KSDCLock {
102 public:
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 
130 class simpleSpinLock : public KSDCLock
131 {
132 public:
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 
168 private:
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
189 class pthreadLock : public KSDCLock
190 {
191 public:
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 
232 protected:
233  pthread_mutex_t &m_mutex;
234 };
235 #endif
236 
237 #if defined(KSDC_THREAD_PROCESS_SHARED_SUPPORTED) && defined(KSDC_TIMEOUTS_SUPPORTED)
238 class pthreadTimedLock : public pthreadLock
239 {
240 public:
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
262 class semaphoreLock : public KSDCLock
263 {
264 public:
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 
299 protected:
300  sem_t &m_semaphore;
301 };
302 #endif
303 
304 #if defined(KSDC_SEMAPHORES_SUPPORTED) && defined(KSDC_TIMEOUTS_SUPPORTED)
305 class semaphoreTimedLock : public semaphoreLock
306 {
307 public:
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.
332 enum 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.
341 struct 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 
368 static 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 
434 static 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 
471 static 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 */
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
kdebug.h
simpleSpinLock::initialize
virtual bool initialize(bool &processSharingSupported)
Definition: kshareddatacache_p.h:138
SharedLock::type
SharedLockId type
Definition: kshareddatacache_p.h:360
ensureFileAllocated
static bool ensureFileAllocated(int fd, size_t fileSize)
Definition: kshareddatacache_p.h:471
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
posix_fallocate
static int posix_fallocate(int fd, off_t offset, off_t len)
Definition: posix_fallocate_mac.h:51
SharedLock::spinlock
QBasicAtomicInt spinlock
Definition: kshareddatacache_p.h:351
KSDCLock::~KSDCLock
virtual ~KSDCLock()
Definition: kshareddatacache_p.h:103
QSharedPointer< KSDCLock >
KSDCLock::unlock
virtual void unlock()
Definition: kshareddatacache_p.h:120
LOCKTYPE_SPINLOCK
Definition: kshareddatacache_p.h:336
simpleSpinLock::lock
virtual bool lock()
Definition: kshareddatacache_p.h:146
simpleSpinLock
This is a very basic lock that should work on any system where GCC atomic intrinsics are supported...
Definition: kshareddatacache_p.h:130
kWarning
#define kWarning
Definition: kdebug.h:322
simpleSpinLock::simpleSpinLock
simpleSpinLock(QBasicAtomicInt &spinlock)
Definition: kshareddatacache_p.h:133
KSDCLock::lock
virtual bool lock()
Definition: kshareddatacache_p.h:115
KSDCLock::initialize
virtual bool initialize(bool &processSharingSupported)
Definition: kshareddatacache_p.h:109
LOCKTYPE_MUTEX
Definition: kshareddatacache_p.h:334
posix_fallocate_mac.h
createLockFromId
static KSDCLock * createLockFromId(SharedLockId id, SharedLock &lock)
Definition: kshareddatacache_p.h:434
SharedLock::unused
char unused[64]
Definition: kshareddatacache_p.h:357
SharedLock
Definition: kshareddatacache_p.h:341
SharedLockId
SharedLockId
Definition: kshareddatacache_p.h:332
LOCKTYPE_INVALID
Definition: kshareddatacache_p.h:333
LOCKTYPE_SEMAPHORE
Definition: kshareddatacache_p.h:335
KSDCLock
This class defines an interface used by KSharedDataCache::Private to offload proper locking and unloc...
Definition: kshareddatacache_p.h:101
simpleSpinLock::unlock
virtual void unlock()
Definition: kshareddatacache_p.h:163
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:22:11 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
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • 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