• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.5 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.h> // HAVE_SYS_MMAN_H
24 
25 #include <QtCore/QSharedPointer>
26 
27 #include <unistd.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <time.h>
31 
32 #include <kdebug.h>
33 
34 // Our debug area, disabled by default
35 int ksdcArea();
36 
37 // Mac OS X, for all its POSIX compliance, does not support timeouts on its
38 // mutexes, which is kind of a disaster for cross-process support. However
39 // synchronization primitives still work, they just might hang if the cache is
40 // corrupted, so keep going.
41 #if defined(_POSIX_TIMEOUTS) && ((_POSIX_TIMEOUTS == 0) || (_POSIX_TIMEOUTS >= 200112L))
42 #define KSDC_TIMEOUTS_SUPPORTED 1
43 #endif
44 
45 #if defined(__GNUC__) && !defined(KSDC_TIMEOUTS_SUPPORTED)
46 #warning "No support for POSIX timeouts -- application hangs are possible if the cache is corrupt"
47 #endif
48 
49 #if defined(_POSIX_THREAD_PROCESS_SHARED) && ((_POSIX_THREAD_PROCESS_SHARED == 0) || (_POSIX_THREAD_PROCESS_SHARED >= 200112L)) && !defined(__APPLE__)
50 #include <pthread.h>
51 #define KSDC_THREAD_PROCESS_SHARED_SUPPORTED 1
52 #endif
53 
54 #if defined(_POSIX_SEMAPHORES) && ((_POSIX_SEMAPHORES == 0) || (_POSIX_SEMAPHORES >= 200112L))
55 #include <semaphore.h>
56 #define KSDC_SEMAPHORES_SUPPORTED 1
57 #endif
58 
59 #if defined(__GNUC__) && !defined(KSDC_SEMAPHORES_SUPPORTED) && !defined(KSDC_THREAD_PROCESS_SHARED_SUPPORTED)
60 #warning "No system support claimed for process-shared synchronization, KSharedDataCache will be mostly useless."
61 #endif
62 
63 #if defined(_POSIX_MAPPED_FILES) && ((_POSIX_MAPPED_FILES == 0) || (_POSIX_MAPPED_FILES >= 200112L))
64 #define KSDC_MAPPED_FILES_SUPPORTED 1
65 #endif
66 
67 #if defined(_POSIX_SYNCHRONIZED_IO) && ((_POSIX_SYNCHRONIZED_IO == 0) || (_POSIX_SYNCHRONIZED_IO >= 200112L))
68 #define KSDC_SYNCHRONIZED_IO_SUPPORTED 1
69 #endif
70 
71 // msync(2) requires both MAPPED_FILES and SYNCHRONIZED_IO POSIX options
72 #if defined(KSDC_MAPPED_FILES_SUPPORTED) && defined(KSDC_SYNCHRONIZED_IO_SUPPORTED)
73 #define KSDC_MSYNC_SUPPORTED
74 #endif
75 
76 // posix_fallocate is used to ensure that the file used for the cache is
77 // actually fully committed to disk before attempting to use the file.
78 #if defined(_POSIX_ADVISORY_INFO) && ((_POSIX_ADVISORY_INFO == 0) || (_POSIX_ADVISORY_INFO >= 200112L))
79 #define KSDC_POSIX_FALLOCATE_SUPPORTED 1
80 #endif
81 
82 // BSD/Mac OS X compat
83 #ifdef HAVE_SYS_MMAN_H
84 #include <sys/mman.h>
85 #endif
86 #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
87 #define MAP_ANONYMOUS MAP_ANON
88 #endif
89 
95 class KSDCLock {
96 public:
97  virtual ~KSDCLock()
98  {
99  }
100 
101  // Return value indicates if the mutex was properly initialized (including
102  // threads-only as a fallback).
103  virtual bool initialize(bool &processSharingSupported)
104  {
105  processSharingSupported = false;
106  return false;
107  }
108 
109  virtual bool lock()
110  {
111  return false;
112  }
113 
114  virtual void unlock()
115  {
116  }
117 };
118 
119 #ifdef KSDC_THREAD_PROCESS_SHARED_SUPPORTED
120 class pthreadLock : public KSDCLock
121 {
122 public:
123  pthreadLock(pthread_mutex_t &mutex)
124  : m_mutex(mutex)
125  {
126  }
127 
128  virtual bool initialize(bool &processSharingSupported)
129  {
130  // Setup process-sharing.
131  pthread_mutexattr_t mutexAttr;
132  processSharingSupported = false;
133 
134  // Initialize attributes, enable process-shared primitives, and setup
135  // the mutex.
136  if (::sysconf(_SC_THREAD_PROCESS_SHARED) >= 200112L && pthread_mutexattr_init(&mutexAttr) == 0) {
137  if (pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED) == 0 &&
138  pthread_mutex_init(&m_mutex, &mutexAttr) == 0)
139  {
140  processSharingSupported = true;
141  }
142  pthread_mutexattr_destroy(&mutexAttr);
143  }
144 
145  // Attempt to setup for thread-only synchronization.
146  if (!processSharingSupported && pthread_mutex_init(&m_mutex, NULL) != 0) {
147  return false;
148  }
149 
150  return true;
151  }
152 
153  virtual bool lock()
154  {
155  return pthread_mutex_lock(&m_mutex) == 0;
156  }
157 
158  virtual void unlock()
159  {
160  pthread_mutex_unlock(&m_mutex);
161  }
162 
163 protected:
164  pthread_mutex_t &m_mutex;
165 };
166 #endif
167 
168 #if defined(KSDC_THREAD_PROCESS_SHARED_SUPPORTED) && defined(KSDC_TIMEOUTS_SUPPORTED)
169 class pthreadTimedLock : public pthreadLock
170 {
171 public:
172  pthreadTimedLock(pthread_mutex_t &mutex)
173  : pthreadLock(mutex)
174  {
175  }
176 
177  virtual bool lock()
178  {
179  struct timespec timeout;
180 
181  // Long timeout, but if we fail to meet this timeout it's probably a cache
182  // corruption (and if we take 8 seconds then it should be much much quicker
183  // the next time anyways since we'd be paged back in from disk)
184  timeout.tv_sec = 10 + ::time(NULL); // Absolute time, so 10 seconds from now
185  timeout.tv_nsec = 0;
186 
187  return pthread_mutex_timedlock(&m_mutex, &timeout) >= 0;
188  }
189 };
190 #endif
191 
192 #ifdef KSDC_SEMAPHORES_SUPPORTED
193 class semaphoreLock : public KSDCLock
194 {
195 public:
196  semaphoreLock(sem_t &semaphore)
197  : m_semaphore(semaphore)
198  {
199  }
200 
201  virtual bool initialize(bool &processSharingSupported)
202  {
203  processSharingSupported = false;
204  if (::sysconf(_SC_SEMAPHORES) < 200112L) {
205  return false;
206  }
207 
208  // sem_init sets up process-sharing for us.
209  if (sem_init(&m_semaphore, 1, 1) == 0) {
210  processSharingSupported = true;
211  }
212  // If not successful try falling back to thread-shared.
213  else if (sem_init(&m_semaphore, 0, 1) != 0) {
214  return false;
215  }
216 
217  return true;
218  }
219 
220  virtual bool lock()
221  {
222  return sem_wait(&m_semaphore) == 0;
223  }
224 
225  virtual void unlock()
226  {
227  sem_post(&m_semaphore);
228  }
229 
230 protected:
231  sem_t &m_semaphore;
232 };
233 #endif
234 
235 #if defined(KSDC_SEMAPHORES_SUPPORTED) && defined(KSDC_TIMEOUTS_SUPPORTED)
236 class semaphoreTimedLock : public semaphoreLock
237 {
238 public:
239  semaphoreTimedLock(sem_t &semaphore)
240  : semaphoreLock(semaphore)
241  {
242  }
243 
244  virtual bool lock()
245  {
246  struct timespec timeout;
247 
248  // Long timeout, but if we fail to meet this timeout it's probably a cache
249  // corruption (and if we take 8 seconds then it should be much much quicker
250  // the next time anyways since we'd be paged back in from disk)
251  timeout.tv_sec = 10 + ::time(NULL); // Absolute time, so 10 seconds from now
252  timeout.tv_nsec = 0;
253 
254  return sem_timedwait(&m_semaphore, &timeout) == 0;
255  }
256 };
257 #endif
258 
259 // This enum controls the type of the locking used for the cache to allow
260 // for as much portability as possible. This value will be stored in the
261 // cache and used by multiple processes, therefore you should consider this
262 // a versioned field, do not re-arrange.
263 enum SharedLockId {
264  LOCKTYPE_INVALID = 0,
265  LOCKTYPE_MUTEX = 1, // pthread_mutex
266  LOCKTYPE_SEMAPHORE = 2 // sem_t
267 };
268 
269 // This type is a union of all possible lock types, with a SharedLockId used
270 // to choose which one is actually in use.
271 struct SharedLock
272 {
273  union
274  {
275 #if defined(KSDC_THREAD_PROCESS_SHARED_SUPPORTED)
276  pthread_mutex_t mutex;
277 #endif
278 #if defined(KSDC_SEMAPHORES_SUPPORTED)
279  sem_t semaphore;
280 #endif
281 
282  // It would be highly unfortunate if a simple glibc upgrade or kernel
283  // addition caused this structure to change size when an existing
284  // lock was thought present, so reserve enough size to cover any
285  // reasonable locking structure
286  char unused[64];
287  };
288 
289  SharedLockId type;
290 };
291 
297 static SharedLockId findBestSharedLock()
298 {
299  // We would prefer a process-shared capability that also supports
300  // timeouts. Failing that, process-shared is preferred over timeout
301  // support. Failing that we'll go thread-local
302  bool pthreadsSupported = false;
303  bool semaphoresSupported = false;
304  bool timeoutsSupported = false;
305  bool pthreadsProcessShared = false;
306  bool semaphoresProcessShared = false;
307 
308 #ifdef KSDC_TIMEOUTS_SUPPORTED
309  timeoutsSupported = ::sysconf(_SC_TIMEOUTS) >= 200112L;
310 #endif
311 
312  // Now that we've queried timeouts, try actually creating real locks and
313  // seeing if there's issues with that.
314 #ifdef KSDC_THREAD_PROCESS_SHARED_SUPPORTED
315  {
316  pthread_mutex_t tempMutex;
317  QSharedPointer<KSDCLock> tempLock(0);
318  if (timeoutsSupported) {
319 #ifdef KSDC_TIMEOUTS_SUPPORTED
320  tempLock = QSharedPointer<KSDCLock>(new pthreadTimedLock(tempMutex));
321 #endif
322  }
323  else {
324  tempLock = QSharedPointer<KSDCLock>(new pthreadLock(tempMutex));
325  }
326 
327  pthreadsSupported = tempLock->initialize(pthreadsProcessShared);
328  }
329 #endif
330 
331  // Our first choice is pthread_mutex_t for compatibility.
332  if(timeoutsSupported && pthreadsProcessShared) {
333  return LOCKTYPE_MUTEX;
334  }
335 
336 #ifdef KSDC_SEMAPHORES_SUPPORTED
337  {
338  sem_t tempSemaphore;
339  QSharedPointer<KSDCLock> tempLock(0);
340  if (timeoutsSupported) {
341  tempLock = QSharedPointer<KSDCLock>(new semaphoreTimedLock(tempSemaphore));
342  }
343  else {
344  tempLock = QSharedPointer<KSDCLock>(new semaphoreLock(tempSemaphore));
345  }
346 
347  semaphoresSupported = tempLock->initialize(semaphoresProcessShared);
348  }
349 #endif
350 
351  if(timeoutsSupported && semaphoresProcessShared) {
352  return LOCKTYPE_SEMAPHORE;
353  }
354  else if(pthreadsProcessShared) {
355  return LOCKTYPE_MUTEX;
356  }
357  else if(semaphoresProcessShared) {
358  return LOCKTYPE_SEMAPHORE;
359  }
360  else if(pthreadsSupported) {
361  return LOCKTYPE_MUTEX;
362  }
363  else if(semaphoresSupported) {
364  return LOCKTYPE_SEMAPHORE;
365  }
366 
367  // If we get to this point we'll likely fail later but this is the
368  // standard behavior that has existed as well, so...
369  return static_cast<SharedLockId>(0);
370 }
371 
372 static KSDCLock *createLockFromId(SharedLockId id, SharedLock &lock)
373 {
374  switch(id) {
375 #ifdef KSDC_THREAD_PROCESS_SHARED_SUPPORTED
376  case LOCKTYPE_MUTEX:
377 #ifdef KSDC_TIMEOUTS_SUPPORTED
378  if (::sysconf(_SC_TIMEOUTS) >= 200112L) {
379  return new pthreadTimedLock(lock.mutex);
380  }
381 #endif
382  return new pthreadLock(lock.mutex);
383 
384  break;
385 #endif
386 
387 #ifdef KSDC_SEMAPHORES_SUPPORTED
388  case LOCKTYPE_SEMAPHORE:
389 #ifdef KSDC_TIMEOUTS_SUPPORTED
390  if (::sysconf(_SC_SEMAPHORES) >= 200112L) {
391  return new semaphoreTimedLock(lock.semaphore);
392  }
393 #endif
394  return new semaphoreLock(lock.semaphore);
395 
396  break;
397 #endif
398 
399  default:
400  kError(ksdcArea()) << "Creating shell of a lock!";
401  return new KSDCLock;
402  }
403 }
404 
405 static bool ensureFileAllocated(int fd, size_t fileSize)
406 {
407 #ifdef KSDC_POSIX_FALLOCATE_SUPPORTED
408  int result;
409  while ((result = ::posix_fallocate(fd, 0, fileSize)) == EINTR) {
410  ;
411  }
412 
413  if (result < 0) {
414  kError(ksdcArea()) << "The operating system is unable to promise"
415  << fileSize
416  << "bytes for mapped cache, "
417  "abandoning the cache for crash-safety.";
418  return false;
419  }
420 
421  return true;
422 #else
423 
424 #ifdef __GNUC__
425 #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."
426 #endif
427  kWarning(ksdcArea()) << "This system misses support for posix_fallocate()"
428  " -- ensure this partition has room for at least"
429  << fileSize << "bytes.";
430 
431  // TODO: It's possible to emulate the functionality, but doing so
432  // overwrites the data in the file so we don't do this. If you were to add
433  // this emulation, you must ensure it only happens on initial creation of a
434  // new file and not just mapping an existing cache.
435 
436  return true;
437 #endif
438 }
439 
440 #endif /* KSHAREDDATACACHE_P_H */
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Fri Dec 7 2012 15:57:21 by doxygen 1.8.1 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.8.5 API Reference

Skip menu "kdelibs-4.8.5 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