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 */
KDE 4.7 API Reference