• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • kdelibs
  • 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 
00027 #include <unistd.h>
00028 #include <errno.h>
00029 #include <fcntl.h>
00030 #include <time.h>
00031 
00032 #include <kdebug.h>
00033 
00034 // Our debug area, disabled by default
00035 int ksdcArea();
00036 
00037 // Mac OS X, for all its POSIX compliance, does not support timeouts on its
00038 // mutexes, which is kind of a disaster for cross-process support. However
00039 // synchronization primitives still work, they just might hang if the cache is
00040 // corrupted, so keep going.
00041 #if defined(_POSIX_TIMEOUTS) && ((_POSIX_TIMEOUTS == 0) || (_POSIX_TIMEOUTS >= 200112L))
00042 #define KSDC_TIMEOUTS_SUPPORTED 1
00043 #endif
00044 
00045 #if defined(__GNUC__) && !defined(KSDC_TIMEOUTS_SUPPORTED)
00046 #warning "No support for POSIX timeouts -- application hangs are possible if the cache is corrupt"
00047 #endif
00048 
00049 #if defined(_POSIX_THREAD_PROCESS_SHARED) && ((_POSIX_THREAD_PROCESS_SHARED == 0) || (_POSIX_THREAD_PROCESS_SHARED >= 200112L)) && !defined(__APPLE__)
00050 #include <pthread.h>
00051 #define KSDC_THREAD_PROCESS_SHARED_SUPPORTED 1
00052 #endif
00053 
00054 #if defined(_POSIX_SEMAPHORES) && ((_POSIX_SEMAPHORES == 0) || (_POSIX_SEMAPHORES >= 200112L))
00055 #include <semaphore.h>
00056 #define KSDC_SEMAPHORES_SUPPORTED 1
00057 #endif
00058 
00059 #if defined(__GNUC__) && !defined(KSDC_SEMAPHORES_SUPPORTED) && !defined(KSDC_THREAD_PROCESS_SHARED_SUPPORTED)
00060 #warning "No system support claimed for process-shared synchronization, KSharedDataCache will be mostly useless."
00061 #endif
00062 
00063 #if defined(_POSIX_MAPPED_FILES) && ((_POSIX_MAPPED_FILES == 0) || (_POSIX_MAPPED_FILES >= 200112L))
00064 #define KSDC_MAPPED_FILES_SUPPORTED 1
00065 #endif
00066 
00067 #if defined(_POSIX_SYNCHRONIZED_IO) && ((_POSIX_SYNCHRONIZED_IO == 0) || (_POSIX_SYNCHRONIZED_IO >= 200112L))
00068 #define KSDC_SYNCHRONIZED_IO_SUPPORTED 1
00069 #endif
00070 
00071 // msync(2) requires both MAPPED_FILES and SYNCHRONIZED_IO POSIX options
00072 #if defined(KSDC_MAPPED_FILES_SUPPORTED) && defined(KSDC_SYNCHRONIZED_IO_SUPPORTED)
00073 #define KSDC_MSYNC_SUPPORTED
00074 #endif
00075 
00076 // posix_fallocate is used to ensure that the file used for the cache is
00077 // actually fully committed to disk before attempting to use the file.
00078 #if defined(_POSIX_ADVISORY_INFO) && ((_POSIX_ADVISORY_INFO == 0) || (_POSIX_ADVISORY_INFO >= 200112L))
00079 #define KSDC_POSIX_FALLOCATE_SUPPORTED 1
00080 #endif
00081 
00082 // BSD/Mac OS X compat
00083 #ifdef HAVE_SYS_MMAN_H
00084 #include <sys/mman.h>
00085 #endif
00086 #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
00087 #define MAP_ANONYMOUS MAP_ANON
00088 #endif
00089 
00095 class KSDCLock {
00096 public:
00097     virtual ~KSDCLock()
00098     {
00099     }
00100 
00101     // Return value indicates if the mutex was properly initialized (including
00102     // threads-only as a fallback).
00103     virtual bool initialize(bool &processSharingSupported)
00104     {
00105         processSharingSupported = false;
00106         return false;
00107     }
00108 
00109     virtual bool lock()
00110     {
00111         return false;
00112     }
00113 
00114     virtual void unlock()
00115     {
00116     }
00117 };
00118 
00119 #ifdef KSDC_THREAD_PROCESS_SHARED_SUPPORTED
00120 class pthreadLock : public KSDCLock
00121 {
00122 public:
00123     pthreadLock(pthread_mutex_t &mutex)
00124         : m_mutex(mutex)
00125     {
00126     }
00127 
00128     virtual bool initialize(bool &processSharingSupported)
00129     {
00130         // Setup process-sharing.
00131         pthread_mutexattr_t mutexAttr;
00132         processSharingSupported = false;
00133 
00134         // Initialize attributes, enable process-shared primitives, and setup
00135         // the mutex.
00136         if (::sysconf(_SC_THREAD_PROCESS_SHARED) >= 200112L && pthread_mutexattr_init(&mutexAttr) == 0) {
00137             if (pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED) == 0 &&
00138                 pthread_mutex_init(&m_mutex, &mutexAttr) == 0)
00139             {
00140                 processSharingSupported = true;
00141             }
00142             pthread_mutexattr_destroy(&mutexAttr);
00143         }
00144 
00145         // Attempt to setup for thread-only synchronization.
00146         if (!processSharingSupported && pthread_mutex_init(&m_mutex, NULL) != 0) {
00147             return false;
00148         }
00149 
00150         return true;
00151     }
00152 
00153     virtual bool lock()
00154     {
00155         return pthread_mutex_lock(&m_mutex) == 0;
00156     }
00157 
00158     virtual void unlock()
00159     {
00160         pthread_mutex_unlock(&m_mutex);
00161     }
00162 
00163 protected:
00164     pthread_mutex_t &m_mutex;
00165 };
00166 #endif
00167 
00168 #if defined(KSDC_THREAD_PROCESS_SHARED_SUPPORTED) && defined(KSDC_TIMEOUTS_SUPPORTED)
00169 class pthreadTimedLock : public pthreadLock
00170 {
00171 public:
00172     pthreadTimedLock(pthread_mutex_t &mutex)
00173         : pthreadLock(mutex)
00174     {
00175     }
00176 
00177     virtual bool lock()
00178     {
00179         struct timespec timeout;
00180 
00181         // Long timeout, but if we fail to meet this timeout it's probably a cache
00182         // corruption (and if we take 8 seconds then it should be much much quicker
00183         // the next time anyways since we'd be paged back in from disk)
00184         timeout.tv_sec = 10 + ::time(NULL); // Absolute time, so 10 seconds from now
00185         timeout.tv_nsec = 0;
00186 
00187         return pthread_mutex_timedlock(&m_mutex, &timeout) >= 0;
00188     }
00189 };
00190 #endif
00191 
00192 #ifdef KSDC_SEMAPHORES_SUPPORTED
00193 class semaphoreLock : public KSDCLock
00194 {
00195 public:
00196     semaphoreLock(sem_t &semaphore)
00197         : m_semaphore(semaphore)
00198     {
00199     }
00200 
00201     virtual bool initialize(bool &processSharingSupported)
00202     {
00203         processSharingSupported = false;
00204         if (::sysconf(_SC_SEMAPHORES) < 200112L) {
00205             return false;
00206         }
00207 
00208         // sem_init sets up process-sharing for us.
00209         if (sem_init(&m_semaphore, 1, 1) == 0) {
00210             processSharingSupported = true;
00211         }
00212         // If not successful try falling back to thread-shared.
00213         else if (sem_init(&m_semaphore, 0, 1) != 0) {
00214             return false;
00215         }
00216 
00217         return true;
00218     }
00219 
00220     virtual bool lock()
00221     {
00222         return sem_wait(&m_semaphore) == 0;
00223     }
00224 
00225     virtual void unlock()
00226     {
00227         sem_post(&m_semaphore);
00228     }
00229 
00230 protected:
00231     sem_t &m_semaphore;
00232 };
00233 #endif
00234 
00235 #if defined(KSDC_SEMAPHORES_SUPPORTED) && defined(KSDC_TIMEOUTS_SUPPORTED)
00236 class semaphoreTimedLock : public semaphoreLock
00237 {
00238 public:
00239     semaphoreTimedLock(sem_t &semaphore)
00240         : semaphoreLock(semaphore)
00241     {
00242     }
00243 
00244     virtual bool lock()
00245     {
00246         struct timespec timeout;
00247 
00248         // Long timeout, but if we fail to meet this timeout it's probably a cache
00249         // corruption (and if we take 8 seconds then it should be much much quicker
00250         // the next time anyways since we'd be paged back in from disk)
00251         timeout.tv_sec = 10 + ::time(NULL); // Absolute time, so 10 seconds from now
00252         timeout.tv_nsec = 0;
00253 
00254         return sem_timedwait(&m_semaphore, &timeout) == 0;
00255     }
00256 };
00257 #endif
00258 
00259 // This enum controls the type of the locking used for the cache to allow
00260 // for as much portability as possible. This value will be stored in the
00261 // cache and used by multiple processes, therefore you should consider this
00262 // a versioned field, do not re-arrange.
00263 enum SharedLockId {
00264     LOCKTYPE_MUTEX     = 1,  // pthread_mutex
00265     LOCKTYPE_SEMAPHORE = 2   // sem_t
00266 };
00267 
00268 // This type is a union of all possible lock types, with a SharedLockId used
00269 // to choose which one is actually in use.
00270 struct SharedLock
00271 {
00272     union
00273     {
00274 #if defined(KSDC_THREAD_PROCESS_SHARED_SUPPORTED)
00275         pthread_mutex_t mutex;
00276 #endif
00277 #if defined(KSDC_SEMAPHORES_SUPPORTED)
00278         sem_t semaphore;
00279 #endif
00280 
00281         // It would be highly unfortunate if a simple glibc upgrade or kernel
00282         // addition caused this structure to change size when an existing
00283         // lock was thought present, so reserve enough size to cover any
00284         // reasonable locking structure
00285         char unused[64];
00286     };
00287 
00288     SharedLockId type;
00289 };
00290 
00296 static SharedLockId findBestSharedLock()
00297 {
00298     // We would prefer a process-shared capability that also supports
00299     // timeouts. Failing that, process-shared is preferred over timeout
00300     // support. Failing that we'll go thread-local
00301     bool pthreadsSupported = false;
00302     bool semaphoresSupported = false;
00303     bool timeoutsSupported = false;
00304     bool pthreadsProcessShared = false;
00305     bool semaphoresProcessShared = false;
00306 
00307 #ifdef KSDC_TIMEOUTS_SUPPORTED
00308     timeoutsSupported = ::sysconf(_SC_TIMEOUTS) >= 200112L;
00309 #endif
00310 
00311     // Now that we've queried timeouts, try actually creating real locks and
00312     // seeing if there's issues with that.
00313 #ifdef KSDC_THREAD_PROCESS_SHARED_SUPPORTED
00314     {
00315         pthread_mutex_t tempMutex;
00316         QSharedPointer<KSDCLock> tempLock(0);
00317         if (timeoutsSupported) {
00318 #ifdef KSDC_TIMEOUTS_SUPPORTED
00319             tempLock = QSharedPointer<KSDCLock>(new pthreadTimedLock(tempMutex));
00320 #endif
00321         }
00322         else {
00323             tempLock = QSharedPointer<KSDCLock>(new pthreadLock(tempMutex));
00324         }
00325 
00326         pthreadsSupported = tempLock->initialize(pthreadsProcessShared);
00327     }
00328 #endif
00329 
00330     // Our first choice is pthread_mutex_t for compatibility.
00331     if(timeoutsSupported && pthreadsProcessShared) {
00332         return LOCKTYPE_MUTEX;
00333     }
00334 
00335 #ifdef KSDC_SEMAPHORES_SUPPORTED
00336     {
00337         sem_t tempSemaphore;
00338         QSharedPointer<KSDCLock> tempLock(0);
00339         if (timeoutsSupported) {
00340             tempLock = QSharedPointer<KSDCLock>(new semaphoreTimedLock(tempSemaphore));
00341         }
00342         else {
00343             tempLock = QSharedPointer<KSDCLock>(new semaphoreLock(tempSemaphore));
00344         }
00345 
00346         semaphoresSupported = tempLock->initialize(semaphoresProcessShared);
00347     }
00348 #endif
00349 
00350     if(timeoutsSupported && semaphoresProcessShared) {
00351         return LOCKTYPE_SEMAPHORE;
00352     }
00353     else if(pthreadsProcessShared) {
00354         return LOCKTYPE_MUTEX;
00355     }
00356     else if(semaphoresProcessShared) {
00357         return LOCKTYPE_SEMAPHORE;
00358     }
00359     else if(pthreadsSupported) {
00360         return LOCKTYPE_MUTEX;
00361     }
00362     else if(semaphoresSupported) {
00363         return LOCKTYPE_SEMAPHORE;
00364     }
00365 
00366     // If we get to this point we'll likely fail later but this is the
00367     // standard behavior that has existed as well, so...
00368     return static_cast<SharedLockId>(0);
00369 }
00370 
00371 static KSDCLock *createLockFromId(SharedLockId id, SharedLock &lock)
00372 {
00373     switch(id) {
00374 #ifdef KSDC_THREAD_PROCESS_SHARED_SUPPORTED
00375     case LOCKTYPE_MUTEX:
00376 #ifdef KSDC_TIMEOUTS_SUPPORTED
00377         if (::sysconf(_SC_TIMEOUTS) >= 200112L) {
00378             return new pthreadTimedLock(lock.mutex);
00379         }
00380 #endif
00381         return new pthreadLock(lock.mutex);
00382 
00383     break;
00384 #endif
00385 
00386 #ifdef KSDC_SEMAPHORES_SUPPORTED
00387     case LOCKTYPE_SEMAPHORE:
00388 #ifdef KSDC_TIMEOUTS_SUPPORTED
00389         if (::sysconf(_SC_SEMAPHORES) >= 200112L) {
00390             return new semaphoreTimedLock(lock.semaphore);
00391         }
00392 #endif
00393         return new semaphoreLock(lock.semaphore);
00394 
00395     break;
00396 #endif
00397 
00398     default:
00399         kError(ksdcArea()) << "Creating shell of a lock!";
00400         return new KSDCLock;
00401     }
00402 }
00403 
00404 static bool ensureFileAllocated(int fd, size_t fileSize)
00405 {
00406 #ifdef KSDC_POSIX_FALLOCATE_SUPPORTED
00407     int result;
00408     while ((result = ::posix_fallocate(fd, 0, fileSize)) == EINTR) {
00409         ;
00410     }
00411 
00412     if (result < 0) {
00413         kError(ksdcArea()) << "The operating system is unable to promise"
00414                            << fileSize
00415                            << "bytes for mapped cache, "
00416                               "abandoning the cache for crash-safety.";
00417         return false;
00418     }
00419 
00420     return true;
00421 #else
00422 
00423 #ifdef __GNUC__
00424 #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."
00425 #endif
00426     kWarning(ksdcArea()) << "This system misses support for posix_fallocate()"
00427                             " -- ensure this partition has room for at least"
00428                          << fileSize << "bytes.";
00429 
00430     // TODO: It's possible to emulate the functionality, but doing so
00431     // overwrites the data in the file so we don't do this. If you were to add
00432     // this emulation, you must ensure it only happens on initial creation of a
00433     // new file and not just mapping an existing cache.
00434 
00435     return true;
00436 #endif
00437 }
00438 
00439 #endif /* KSHAREDDATACACHE_P_H */

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

Skip menu "kdelibs"
  • 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
Generated for kdelibs by doxygen 1.7.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal