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