* This file is part of the KDE project.
*
* SPDX-FileCopyrightText: 2010 Michael Pyne <mpyne@kde.org>
* SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef KSDCLOCK_P_H
#define KSDCLOCK_P_H
#include <qbasicatomic.h>
#include <sched.h>
#include <unistd.h>
#if defined(_POSIX_TIMEOUTS) && ((_POSIX_TIMEOUTS == 0) || (_POSIX_TIMEOUTS >= 200112L))
#define KSDC_TIMEOUTS_SUPPORTED 1
#endif
#if defined(__GNUC__) && !defined(KSDC_TIMEOUTS_SUPPORTED)
#warning "No support for POSIX timeouts -- application hangs are possible if the cache is corrupt"
#endif
#if defined(_POSIX_THREAD_PROCESS_SHARED) && ((_POSIX_THREAD_PROCESS_SHARED == 0) || (_POSIX_THREAD_PROCESS_SHARED >= 200112L)) && !defined(__APPLE__)
#include <pthread.h>
#define KSDC_THREAD_PROCESS_SHARED_SUPPORTED 1
#endif
#if defined(_POSIX_SEMAPHORES) && ((_POSIX_SEMAPHORES == 0) || (_POSIX_SEMAPHORES >= 200112L))
#include <semaphore.h>
#define KSDC_SEMAPHORES_SUPPORTED 1
#endif
#if defined(__GNUC__) && !defined(KSDC_SEMAPHORES_SUPPORTED) && !defined(KSDC_THREAD_PROCESS_SHARED_SUPPORTED)
#warning "No system support claimed for process-shared synchronization, KSharedDataCache will be mostly useless."
#endif
* This class defines an interface used by KSharedDataCache::Private to offload
* proper locking and unlocking depending on what the platform supports at
* runtime and compile-time.
*/
class KSDCLock
{
public:
virtual ~KSDCLock()
{
}
virtual bool initialize(bool &processSharingSupported)
{
processSharingSupported = false;
return false;
}
virtual bool lock()
{
return false;
}
virtual void unlock()
{
}
};
* This is a very basic lock that should work on any system where GCC atomic
* intrinsics are supported. It can waste CPU so better primitives should be
* used if available on the system.
*/
class simpleSpinLock : public KSDCLock
{
public:
simpleSpinLock(QBasicAtomicInt &spinlock)
: m_spinlock(spinlock)
{
}
bool initialize(bool &processSharingSupported) override
{
m_spinlock.storeRelaxed(0);
processSharingSupported = true;
return true;
}
bool lock() override
{
for (unsigned i = 50; i > 0; --i) {
if (m_spinlock.testAndSetAcquire(0, 1)) {
return true;
}
loopSpinPause();
}
return false;
}
void unlock() override
{
m_spinlock.testAndSetRelease(1, 0);
}
private:
#ifdef Q_CC_GNU
__attribute__((always_inline,
gnu_inline
#if !defined(Q_CC_INTEL) && !defined(Q_CC_CLANG)
,
artificial
#endif
))
#endif
static inline void
loopSpinPause()
{
#ifdef _POSIX_PRIORITY_SCHEDULING
sched_yield();
#else
struct timespec wait_time = {0 , 100 };
::nanosleep(&wait_time, static_cast<struct timespec *>(0));
#endif
}
QBasicAtomicInt &m_spinlock;
};
#ifdef KSDC_THREAD_PROCESS_SHARED_SUPPORTED
class pthreadLock : public KSDCLock
{
public:
pthreadLock(pthread_mutex_t &mutex)
: m_mutex(mutex)
{
}
bool initialize(bool &processSharingSupported) override
{
pthread_mutexattr_t mutexAttr;
processSharingSupported = false;
if (::sysconf(_SC_THREAD_PROCESS_SHARED) >= 200112L && pthread_mutexattr_init(&mutexAttr) == 0) {
if (pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED) == 0 && pthread_mutex_init(&m_mutex, &mutexAttr) == 0) {
processSharingSupported = true;
}
pthread_mutexattr_destroy(&mutexAttr);
}
if (!processSharingSupported && pthread_mutex_init(&m_mutex, nullptr) != 0) {
return false;
}
return true;
}
bool lock() override
{
return pthread_mutex_lock(&m_mutex) == 0;
}
void unlock() override
{
pthread_mutex_unlock(&m_mutex);
}
protected:
pthread_mutex_t &m_mutex;
};
#endif
#if defined(KSDC_THREAD_PROCESS_SHARED_SUPPORTED) && defined(KSDC_TIMEOUTS_SUPPORTED)
class pthreadTimedLock : public pthreadLock
{
public:
pthreadTimedLock(pthread_mutex_t &mutex)
: pthreadLock(mutex)
{
}
bool lock() override
{
struct timespec timeout;
timeout.tv_sec = 10 + ::time(nullptr);
timeout.tv_nsec = 0;
return pthread_mutex_timedlock(&m_mutex, &timeout) == 0;
}
};
#endif
#ifdef KSDC_SEMAPHORES_SUPPORTED
class semaphoreLock : public KSDCLock
{
public:
semaphoreLock(sem_t &semaphore)
: m_semaphore(semaphore)
{
}
bool initialize(bool &processSharingSupported) override
{
processSharingSupported = false;
if (::sysconf(_SC_SEMAPHORES) < 200112L) {
return false;
}
if (sem_init(&m_semaphore, 1, 1) == 0) {
processSharingSupported = true;
}
else if (sem_init(&m_semaphore, 0, 1) != 0) {
return false;
}
return true;
}
bool lock() override
{
return sem_wait(&m_semaphore) == 0;
}
void unlock() override
{
sem_post(&m_semaphore);
}
protected:
sem_t &m_semaphore;
};
#endif
#if defined(KSDC_SEMAPHORES_SUPPORTED) && defined(KSDC_TIMEOUTS_SUPPORTED)
class semaphoreTimedLock : public semaphoreLock
{
public:
semaphoreTimedLock(sem_t &semaphore)
: semaphoreLock(semaphore)
{
}
bool lock() override
{
struct timespec timeout;
timeout.tv_sec = 10 + ::time(nullptr);
timeout.tv_nsec = 0;
return sem_timedwait(&m_semaphore, &timeout) == 0;
}
};
#endif
enum SharedLockId {
LOCKTYPE_INVALID = 0,
LOCKTYPE_MUTEX = 1,
LOCKTYPE_SEMAPHORE = 2,
LOCKTYPE_SPINLOCK = 3,
};
struct SharedLock {
union {
#if defined(KSDC_THREAD_PROCESS_SHARED_SUPPORTED)
pthread_mutex_t mutex;
#endif
#if defined(KSDC_SEMAPHORES_SUPPORTED)
sem_t semaphore;
#endif
QBasicAtomicInt spinlock;
char unused[64];
};
SharedLockId type;
};
* This is a method to determine the best lock type to use for a
* shared cache, based on local support. An identifier to the appropriate
* SharedLockId is returned, which can be passed to createLockFromId().
*/
SharedLockId findBestSharedLock();
KSDCLock *createLockFromId(SharedLockId id, SharedLock &lock);
#endif