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

KDECore

kshareddatacache_p.h
Go to the documentation of this file.
00001 /*
00002  * This file is part of the KDE project.
00003  * Copyright © 2010 Michael Pyne <mpyne@kde.org>
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Library General Public
00007  * License version 2 as published by the Free Software Foundation.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Library General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Library General Public License
00015  * along with this library; see the file COPYING.LIB.  If not, write to
00016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018  */
00019 
00020 #ifndef KSHAREDDATACACHE_P_H
00021 #define KSHAREDDATACACHE_P_H
00022 
00023 #include <config.h> // HAVE_SYS_MMAN_H
00024 
00025 #include <QtCore/QSharedPointer>
00026 #include <QtCore/QBasicAtomicInt>
00027 
00028 #include <unistd.h> // Check for sched_yield
00029 #include <sched.h>  // sched_yield
00030 #include <errno.h>
00031 #include <fcntl.h>
00032 #include <time.h>
00033 
00034 #include <kdebug.h>
00035 
00036 // Our debug area, disabled by default
00037 int ksdcArea();
00038 
00039 // Mac OS X, for all its POSIX compliance, does not support timeouts on its
00040 // mutexes, which is kind of a disaster for cross-process support. However
00041 // synchronization primitives still work, they just might hang if the cache is
00042 // corrupted, so keep going.
00043 #if defined(_POSIX_TIMEOUTS) && ((_POSIX_TIMEOUTS == 0) || (_POSIX_TIMEOUTS >= 200112L))
00044 #define KSDC_TIMEOUTS_SUPPORTED 1
00045 #endif
00046 
00047 #if defined(__GNUC__) && !defined(KSDC_TIMEOUTS_SUPPORTED)
00048 #warning "No support for POSIX timeouts -- application hangs are possible if the cache is corrupt"
00049 #endif
00050 
00051 #if defined(_POSIX_THREAD_PROCESS_SHARED) && ((_POSIX_THREAD_PROCESS_SHARED == 0) || (_POSIX_THREAD_PROCESS_SHARED >= 200112L)) && !defined(__APPLE__)
00052 #include <pthread.h>
00053 #define KSDC_THREAD_PROCESS_SHARED_SUPPORTED 1
00054 #endif
00055 
00056 #if defined(_POSIX_SEMAPHORES) && ((_POSIX_SEMAPHORES == 0) || (_POSIX_SEMAPHORES >= 200112L))
00057 #include <semaphore.h>
00058 #define KSDC_SEMAPHORES_SUPPORTED 1
00059 #endif
00060 
00061 #if defined(__GNUC__) && !defined(KSDC_SEMAPHORES_SUPPORTED) && !defined(KSDC_THREAD_PROCESS_SHARED_SUPPORTED)
00062 #warning "No system support claimed for process-shared synchronization, KSharedDataCache will be mostly useless."
00063 #endif
00064 
00065 #if defined(_POSIX_MAPPED_FILES) && ((_POSIX_MAPPED_FILES == 0) || (_POSIX_MAPPED_FILES >= 200112L))
00066 #define KSDC_MAPPED_FILES_SUPPORTED 1
00067 #endif
00068 
00069 #if defined(_POSIX_SYNCHRONIZED_IO) && ((_POSIX_SYNCHRONIZED_IO == 0) || (_POSIX_SYNCHRONIZED_IO >= 200112L))
00070 #define KSDC_SYNCHRONIZED_IO_SUPPORTED 1
00071 #endif
00072 
00073 // msync(2) requires both MAPPED_FILES and SYNCHRONIZED_IO POSIX options
00074 #if defined(KSDC_MAPPED_FILES_SUPPORTED) && defined(KSDC_SYNCHRONIZED_IO_SUPPORTED)
00075 #define KSDC_MSYNC_SUPPORTED
00076 #endif
00077 
00078 // posix_fallocate is used to ensure that the file used for the cache is
00079 // actually fully committed to disk before attempting to use the file.
00080 #if defined(_POSIX_ADVISORY_INFO) && ((_POSIX_ADVISORY_INFO == 0) || (_POSIX_ADVISORY_INFO >= 200112L))
00081 #define KSDC_POSIX_FALLOCATE_SUPPORTED 1
00082 #endif
00083 
00084 // BSD/Mac OS X compat
00085 #ifdef HAVE_SYS_MMAN_H
00086 #include <sys/mman.h>
00087 #endif
00088 #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
00089 #define MAP_ANONYMOUS MAP_ANON
00090 #endif
00091 
00097 class KSDCLock {
00098 public:
00099     virtual ~KSDCLock()
00100     {
00101     }
00102 
00103     // Return value indicates if the mutex was properly initialized (including
00104     // threads-only as a fallback).
00105     virtual bool initialize(bool &processSharingSupported)
00106     {
00107         processSharingSupported = false;
00108         return false;
00109     }
00110 
00111     virtual bool lock()
00112     {
00113         return false;
00114     }
00115 
00116     virtual void unlock()
00117     {
00118     }
00119 };
00120 
00126 class simpleSpinLock : public KSDCLock
00127 {
00128 public:
00129     simpleSpinLock(QBasicAtomicInt &spinlock)
00130         : m_spinlock(spinlock)
00131     {
00132     }
00133 
00134     virtual bool initialize(bool &processSharingSupported)
00135     {
00136         // Clear the spinlock
00137         m_spinlock = 0;
00138         processSharingSupported = true;
00139         return true;
00140     }
00141 
00142     virtual bool lock()
00143     {
00144         // Spin a few times attempting to gain the lock, as upper-level code won't
00145         // attempt again without assuming the cache is corrupt.
00146         for (unsigned i = 50; i > 0; --i) {
00147             if (m_spinlock.testAndSetAcquire(0, 1)) {
00148                 return true;
00149             }
00150 
00151             // Don't steal the processor and starve the thread we're waiting
00152             // on.
00153             loopSpinPause();
00154         }
00155 
00156         return false;
00157     }
00158 
00159     virtual void unlock()
00160     {
00161         m_spinlock.testAndSetRelease(1, 0);
00162     }
00163 
00164 private:
00165 #ifdef Q_CC_GNU
00166     __attribute__((always_inline, gnu_inline, artificial))
00167 #endif
00168     static inline void loopSpinPause()
00169     {
00170         // TODO: Spinning might be better in multi-core systems... but that means
00171         // figuring how to find numbers of CPUs in a cross-platform way.
00172 #ifdef _POSIX_PRIORITY_SCHEDULING
00173         sched_yield();
00174 #else
00175         // Sleep for shortest possible time (nanosleep should round-up).
00176         struct timespec wait_time = { 0 /* sec */, 100 /* ns */ };
00177         ::nanosleep(&wait_time, static_cast<struct timespec*>(0));
00178 #endif
00179     }
00180 
00181     QBasicAtomicInt &m_spinlock;
00182 };
00183 
00184 #ifdef KSDC_THREAD_PROCESS_SHARED_SUPPORTED
00185 class pthreadLock : public KSDCLock
00186 {
00187 public:
00188     pthreadLock(pthread_mutex_t &mutex)
00189         : m_mutex(mutex)
00190     {
00191     }
00192 
00193     virtual bool initialize(bool &processSharingSupported)
00194     {
00195         // Setup process-sharing.
00196         pthread_mutexattr_t mutexAttr;
00197         processSharingSupported = false;
00198 
00199         // Initialize attributes, enable process-shared primitives, and setup
00200         // the mutex.
00201         if (::sysconf(_SC_THREAD_PROCESS_SHARED) >= 200112L && pthread_mutexattr_init(&mutexAttr) == 0) {
00202             if (pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED) == 0 &&
00203                 pthread_mutex_init(&m_mutex, &mutexAttr) == 0)
00204             {
00205                 processSharingSupported = true;
00206             }
00207             pthread_mutexattr_destroy(&mutexAttr);
00208         }
00209 
00210         // Attempt to setup for thread-only synchronization.
00211         if (!processSharingSupported && pthread_mutex_init(&m_mutex, NULL) != 0) {
00212             return false;
00213         }
00214 
00215         return true;
00216     }
00217 
00218     virtual bool lock()
00219     {
00220         return pthread_mutex_lock(&m_mutex) == 0;
00221     }
00222 
00223     virtual void unlock()
00224     {
00225         pthread_mutex_unlock(&m_mutex);
00226     }
00227 
00228 protected:
00229     pthread_mutex_t &m_mutex;
00230 };
00231 #endif
00232 
00233 #if defined(KSDC_THREAD_PROCESS_SHARED_SUPPORTED) && defined(KSDC_TIMEOUTS_SUPPORTED)
00234 class pthreadTimedLock : public pthreadLock
00235 {
00236 public:
00237     pthreadTimedLock(pthread_mutex_t &mutex)
00238         : pthreadLock(mutex)
00239     {
00240     }
00241 
00242     virtual bool lock()
00243     {
00244         struct timespec timeout;
00245 
00246         // Long timeout, but if we fail to meet this timeout it's probably a cache
00247         // corruption (and if we take 8 seconds then it should be much much quicker
00248         // the next time anyways since we'd be paged back in from disk)
00249         timeout.tv_sec = 10 + ::time(NULL); // Absolute time, so 10 seconds from now
00250         timeout.tv_nsec = 0;
00251 
00252         return pthread_mutex_timedlock(&m_mutex, &timeout) == 0;
00253     }
00254 };
00255 #endif
00256 
00257 #ifdef KSDC_SEMAPHORES_SUPPORTED
00258 class semaphoreLock : public KSDCLock
00259 {
00260 public:
00261     semaphoreLock(sem_t &semaphore)
00262         : m_semaphore(semaphore)
00263     {
00264     }
00265 
00266     virtual bool initialize(bool &processSharingSupported)
00267     {
00268         processSharingSupported = false;
00269         if (::sysconf(_SC_SEMAPHORES) < 200112L) {
00270             return false;
00271         }
00272 
00273         // sem_init sets up process-sharing for us.
00274         if (sem_init(&m_semaphore, 1, 1) == 0) {
00275             processSharingSupported = true;
00276         }
00277         // If not successful try falling back to thread-shared.
00278         else if (sem_init(&m_semaphore, 0, 1) != 0) {
00279             return false;
00280         }
00281 
00282         return true;
00283     }
00284 
00285     virtual bool lock()
00286     {
00287         return sem_wait(&m_semaphore) == 0;
00288     }
00289 
00290     virtual void unlock()
00291     {
00292         sem_post(&m_semaphore);
00293     }
00294 
00295 protected:
00296     sem_t &m_semaphore;
00297 };
00298 #endif
00299 
00300 #if defined(KSDC_SEMAPHORES_SUPPORTED) && defined(KSDC_TIMEOUTS_SUPPORTED)
00301 class semaphoreTimedLock : public semaphoreLock
00302 {
00303 public:
00304     semaphoreTimedLock(sem_t &semaphore)
00305         : semaphoreLock(semaphore)
00306     {
00307     }
00308 
00309     virtual bool lock()
00310     {
00311         struct timespec timeout;
00312 
00313         // Long timeout, but if we fail to meet this timeout it's probably a cache
00314         // corruption (and if we take 8 seconds then it should be much much quicker
00315         // the next time anyways since we'd be paged back in from disk)
00316         timeout.tv_sec = 10 + ::time(NULL); // Absolute time, so 10 seconds from now
00317         timeout.tv_nsec = 0;
00318 
00319         return sem_timedwait(&m_semaphore, &timeout) == 0;
00320     }
00321 };
00322 #endif
00323 
00324 // This enum controls the type of the locking used for the cache to allow
00325 // for as much portability as possible. This value will be stored in the
00326 // cache and used by multiple processes, therefore you should consider this
00327 // a versioned field, do not re-arrange.
00328 enum SharedLockId {
00329     LOCKTYPE_INVALID   = 0,
00330     LOCKTYPE_MUTEX     = 1,  // pthread_mutex
00331     LOCKTYPE_SEMAPHORE = 2,  // sem_t
00332     LOCKTYPE_SPINLOCK  = 3   // atomic int in shared memory
00333 };
00334 
00335 // This type is a union of all possible lock types, with a SharedLockId used
00336 // to choose which one is actually in use.
00337 struct SharedLock
00338 {
00339     union
00340     {
00341 #if defined(KSDC_THREAD_PROCESS_SHARED_SUPPORTED)
00342         pthread_mutex_t mutex;
00343 #endif
00344 #if defined(KSDC_SEMAPHORES_SUPPORTED)
00345         sem_t semaphore;
00346 #endif
00347         QBasicAtomicInt spinlock;
00348 
00349         // It would be highly unfortunate if a simple glibc upgrade or kernel
00350         // addition caused this structure to change size when an existing
00351         // lock was thought present, so reserve enough size to cover any
00352         // reasonable locking structure
00353         char unused[64];
00354     };
00355 
00356     SharedLockId type;
00357 };
00358 
00364 static SharedLockId findBestSharedLock()
00365 {
00366     // We would prefer a process-shared capability that also supports
00367     // timeouts. Failing that, process-shared is preferred over timeout
00368     // support. Failing that we'll go thread-local
00369     bool timeoutsSupported = false;
00370     bool pthreadsProcessShared = false;
00371     bool semaphoresProcessShared = false;
00372 
00373 #ifdef KSDC_TIMEOUTS_SUPPORTED
00374     timeoutsSupported = ::sysconf(_SC_TIMEOUTS) >= 200112L;
00375 #endif
00376 
00377     // Now that we've queried timeouts, try actually creating real locks and
00378     // seeing if there's issues with that.
00379 #ifdef KSDC_THREAD_PROCESS_SHARED_SUPPORTED
00380     {
00381         pthread_mutex_t tempMutex;
00382         QSharedPointer<KSDCLock> tempLock(0);
00383         if (timeoutsSupported) {
00384 #ifdef KSDC_TIMEOUTS_SUPPORTED
00385             tempLock = QSharedPointer<KSDCLock>(new pthreadTimedLock(tempMutex));
00386 #endif
00387         }
00388         else {
00389             tempLock = QSharedPointer<KSDCLock>(new pthreadLock(tempMutex));
00390         }
00391 
00392         tempLock->initialize(pthreadsProcessShared);
00393     }
00394 #endif
00395 
00396     // Our first choice is pthread_mutex_t for compatibility.
00397     if(timeoutsSupported && pthreadsProcessShared) {
00398         return LOCKTYPE_MUTEX;
00399     }
00400 
00401 #ifdef KSDC_SEMAPHORES_SUPPORTED
00402     {
00403         sem_t tempSemaphore;
00404         QSharedPointer<KSDCLock> tempLock(0);
00405         if (timeoutsSupported) {
00406             tempLock = QSharedPointer<KSDCLock>(new semaphoreTimedLock(tempSemaphore));
00407         }
00408         else {
00409             tempLock = QSharedPointer<KSDCLock>(new semaphoreLock(tempSemaphore));
00410         }
00411 
00412         tempLock->initialize(semaphoresProcessShared);
00413     }
00414 #endif
00415 
00416     if(timeoutsSupported && semaphoresProcessShared) {
00417         return LOCKTYPE_SEMAPHORE;
00418     }
00419     else if(pthreadsProcessShared) {
00420         return LOCKTYPE_MUTEX;
00421     }
00422     else if(semaphoresProcessShared) {
00423         return LOCKTYPE_SEMAPHORE;
00424     }
00425 
00426     // Fallback to a dumb-simple but possibly-CPU-wasteful solution.
00427     return LOCKTYPE_SPINLOCK;
00428 }
00429 
00430 static KSDCLock *createLockFromId(SharedLockId id, SharedLock &lock)
00431 {
00432     switch(id) {
00433 #ifdef KSDC_THREAD_PROCESS_SHARED_SUPPORTED
00434     case LOCKTYPE_MUTEX:
00435 #ifdef KSDC_TIMEOUTS_SUPPORTED
00436         if (::sysconf(_SC_TIMEOUTS) >= 200112L) {
00437             return new pthreadTimedLock(lock.mutex);
00438         }
00439 #endif
00440         return new pthreadLock(lock.mutex);
00441 
00442     break;
00443 #endif
00444 
00445 #ifdef KSDC_SEMAPHORES_SUPPORTED
00446     case LOCKTYPE_SEMAPHORE:
00447 #ifdef KSDC_TIMEOUTS_SUPPORTED
00448         if (::sysconf(_SC_SEMAPHORES) >= 200112L) {
00449             return new semaphoreTimedLock(lock.semaphore);
00450         }
00451 #endif
00452         return new semaphoreLock(lock.semaphore);
00453 
00454     break;
00455 #endif
00456 
00457     case LOCKTYPE_SPINLOCK:
00458         return new simpleSpinLock(lock.spinlock);
00459     break;
00460 
00461     default:
00462         kError(ksdcArea()) << "Creating shell of a lock!";
00463         return new KSDCLock;
00464     }
00465 }
00466 
00467 static bool ensureFileAllocated(int fd, size_t fileSize)
00468 {
00469 #ifdef KSDC_POSIX_FALLOCATE_SUPPORTED
00470     int result;
00471     while ((result = ::posix_fallocate(fd, 0, fileSize)) == EINTR) {
00472         ;
00473     }
00474 
00475     if (result < 0) {
00476         kError(ksdcArea()) << "The operating system is unable to promise"
00477                            << fileSize
00478                            << "bytes for mapped cache, "
00479                               "abandoning the cache for crash-safety.";
00480         return false;
00481     }
00482 
00483     return true;
00484 #else
00485 
00486 #ifdef __GNUC__
00487 #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."
00488 #endif
00489     kWarning(ksdcArea()) << "This system misses support for posix_fallocate()"
00490                             " -- ensure this partition has room for at least"
00491                          << fileSize << "bytes.";
00492 
00493     // TODO: It's possible to emulate the functionality, but doing so
00494     // overwrites the data in the file so we don't do this. If you were to add
00495     // this emulation, you must ensure it only happens on initial creation of a
00496     // new file and not just mapping an existing cache.
00497 
00498     return true;
00499 #endif
00500 }
00501 
00502 #endif /* KSHAREDDATACACHE_P_H */
This file is part of the KDE documentation.
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:28:12 by doxygen 1.7.5.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.9.5 API Reference

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