generic: get rid of KImageCache and KSharedDataCache

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2016-04-05 03:48:27 +00:00
parent ef15158907
commit c6f33f23aa
17 changed files with 30 additions and 3050 deletions

View file

@ -177,7 +177,6 @@ install(
KIconLoader
KIconTheme
KIdentityProxyModel
KImageCache
KImageFilePreview
KImageIO
KInputDialog
@ -299,7 +298,6 @@ install(
KServiceTypeTrader
KSessionManager
KSharedConfig
KSharedDataCache
KSharedPtr
KShell
KShellCompletion

View file

@ -1 +0,0 @@
#include "../kimagecache.h"

View file

@ -1 +0,0 @@
#include "../kshareddatacache.h"

View file

@ -297,7 +297,6 @@ set(kdecore_LIB_SRCS
util/kshell_unix.cpp
util/kuser_unix.cpp
util/kmacroexpander_unix.cpp
util/kshareddatacache.cpp
fakes.c
${kdecore_OPTIONAL_SRCS}
@ -305,12 +304,6 @@ set(kdecore_LIB_SRCS
${KAUTH_HELPER_BACKEND_SRCS}
)
# KSharedDataCache uses exceptions as of 4.8.4, the rest of kdecore
# doesn't. Try to enable exceptions only for that file.
set_source_files_properties(util/kshareddatacache.cpp
PROPERTIES COMPILE_FLAGS "${KDE4_ENABLE_EXCEPTIONS}"
)
if(NOT Q_WS_X11 AND NOT Q_WS_QWS)
add_definitions(-DNO_DISPLAY)
endif()
@ -511,7 +504,6 @@ install(
util/kshell.h
util/krandom.h
util/krandomsequence.h
util/kshareddatacache.h
util/ksharedptr.h
util/ksortablelist.h
util/kuser.h

View file

@ -42,7 +42,6 @@ KDECORE_UNIT_TESTS(
klockfiletest
ktempdirtest
ksharedptrtest
kshareddatacachetest
kdatetimetest
ksavefiletest
kautosavefiletest

View file

@ -1,75 +0,0 @@
/* This file is part of the KDE libraries
* Copyright (c) 2012 David Faure <faure@kde.org>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License or ( at
* your option ) version 3 or, at the discretion of KDE e.V. ( which shall
* act as a proxy as in section 14 of the GPLv3 ), any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <kshareddatacache.h>
#include <qtest_kde.h>
#include <QtCore/QObject>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QDir>
#include <kstandarddirs.h>
#include <string.h> // strcpy
class KSharedDataCacheTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void simpleInsert();
};
void KSharedDataCacheTest::initTestCase()
{
}
void KSharedDataCacheTest::simpleInsert()
{
const QLatin1String cacheName("myTestCache");
const QLatin1String key("mypic");
// clear the cache
QString cacheFile = KGlobal::dirs()->locateLocal("cache", cacheName + QLatin1String(".kcache"));
QFile file(cacheFile);
if (file.exists())
QVERIFY(file.remove());
// insert something into it
KSharedDataCache cache(cacheName, 5*1024*1024);
QVERIFY(file.exists()); // make sure we got the cache filename right
QByteArray data;
data.resize(9228);
strcpy(data.data(), "Hello world");
QVERIFY(cache.insert(key, data));
// read it out again
QByteArray result;
QVERIFY(cache.find(key, &result));
QCOMPARE(result, data);
// another insert
strcpy(data.data(), "Hello KDE");
QVERIFY(cache.insert(key, data));
// and another read
QVERIFY(cache.find(key, &result));
QCOMPARE(result, data);
}
QTEST_KDEMAIN_CORE(KSharedDataCacheTest)
#include "kshareddatacachetest.moc"

File diff suppressed because it is too large Load diff

View file

@ -1,222 +0,0 @@
/*
* This file is part of the KDE project.
* Copyright © 2010 Michael Pyne <mpyne@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License version 2 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KSHAREDDATACACHE_H
#define KSHAREDDATACACHE_H
#include <kdecore_export.h>
class QString;
class QByteArray;
/**
* @brief A simple data cache which uses shared memory to quickly access data
* stored on disk.
*
* This class is meant to be used with KImageCache and similar classes but can
* be used directly if used with care.
*
* Example usage:
*
* @code
* QString loadTranslatedDocument(KSharedDataCache *cache) {
*
* // Find the data
* QByteArray document;
*
* if (!cache->find("translated-doc-template", &document)) {
* // Entry is not cached, manually generate and then add to cache.
* document = translateDocument(globalTemplate());
* cache->insert(document);
* }
*
* // Don't forget to encode/decode properly
* return QString::fromUtf8(document);
* }
* @endcode
*
* @author Michael Pyne <mpyne@kde.org>
* @see KImageCache
* @since 4.5
*/
class KDECORE_EXPORT KSharedDataCache
{
public:
/**
* Attaches to a shared cache, creating it if necessary. If supported, this
* data cache will be shared across all processes using this cache (with
* subsequent memory savings). If shared memory is unsupported or a
* failure occurs, caching will still be supported, but only in the same
* process, and only using the same KSharedDataCache object.
*
* @param cacheName Name of the cache to use/share.
* @param defaultCacheSize Amount of data to be able to store, in bytes. The
* actual size will be slightly larger on disk due to accounting
* overhead. If the cache already existed then it <em>will not</em> be
* resized. For this reason you should specify some reasonable size.
* @param expectedItemSize The average size of an item that would be stored
* in the cache, in bytes. Choosing an average size of zero bytes causes
* KSharedDataCache to use whatever it feels is the best default for the
* system.
*/
KSharedDataCache(const QString &cacheName,
unsigned defaultCacheSize,
unsigned expectedItemSize = 0);
~KSharedDataCache();
enum EvictionPolicy
{
// The default value for data in our shared memory will be 0, so it is
// important that whatever we want for the default value is also 0.
NoEvictionPreference = 0,
EvictLeastRecentlyUsed,
EvictLeastOftenUsed,
EvictOldest
};
/**
* @return The removal policy in use by the shared cache.
* @see EvictionPolicy
*/
EvictionPolicy evictionPolicy() const;
/**
* Sets the entry removal policy for the shared cache to
* @p newPolicy. The default is EvictionPolicy::NoEvictionPreference.
*
* @see EvictionPolicy
*/
void setEvictionPolicy(EvictionPolicy newPolicy);
/**
* Attempts to insert the entry @p data into the shared cache, named by
* @p key, and returns true only if successful.
*
* Note that even if the insert was successful, that the newly added entry
* may be evicted by other processes contending for the cache.
*/
bool insert(const QString &key, const QByteArray &data);
/**
* Returns the data in the cache named by @p key (even if it's some other
* process's data named with the same key!), stored in @p destination. If there is
* no entry named by @p key then @p destination is left unchanged. The return value
* is used to tell what happened.
*
* If you simply want to verify whether an entry is present in the cache then
* see contains().
*
* @param key The key to find in the cache.
* @param destination Is set to the value of @p key in the cache if @p key is
* present, left unchanged otherwise.
* @return true if @p key was present in the cache (@p destination will also be
* updated), false if @p key was not present (@p destination will be
* unchanged).
*/
bool find(const QString &key, QByteArray *destination) const;
/**
* Removes all entries from the cache.
*/
void clear();
/**
* Removes the underlying file from the cache. Note that this is *all* that this
* function does. The shared memory segment is still attached and will still contain
* all the data until all processes currently attached remove the mapping.
*
* In order to remove the data see clear().
*/
static void deleteCache(const QString &cacheName);
/**
* Returns true if the cache currently contains the image for the given
* filename.
*
* NOTE: Calling this function is threadsafe, but it is in general not
* possible to guarantee the image stays cached immediately afterwards,
* so if you need the result use find().
*/
bool contains(const QString &key) const;
/**
* Returns the usable cache size in bytes. The actual amount of memory
* used will be slightly larger than this to account for required
* accounting overhead.
*/
unsigned totalSize() const;
/**
* Returns the amount of free space in the cache, in bytes. Due to
* implementation details it is possible to still not be able to fit an
* entry in the cache at any given time even if it is smaller than the
* amount of space remaining.
*/
unsigned freeSize() const;
/**
* @return The shared timestamp of the cache. The interpretation of the
* timestamp returned is up to the application. KSharedDataCache
* will initialize the timestamp to the time returned by @c time(2)
* on cache creation, but KSharedDataCache will not touch the
* timestamp again.
* @see setTimestamp()
* @since 4.6
*/
unsigned timestamp() const;
/**
* Sets the shared timestamp of the cache. Timestamping is supported to
* allow applications to more effectively "version" the data stored in the
* cache. However, the timestamp is shared with <em>all</em> applications
* using the cache so you should always be prepared for invalid
* timestamps.
*
* When the cache is first created (note that this is different from
* attaching to an existing shared cache on disk), the cache timestamp is
* initialized to the time returned by @c time(2). KSharedDataCache will
* not update the timestamp again, it is only updated through this method.
*
* Example:
* @code
* QImage loadCachedImage(const QString &key)
* {
* // Check timestamp
* if (m_sharedCache->timestamp() < m_currentThemeTimestamp) {
* // Cache is stale, clean it out.
* m_sharedCache->clear();
* m_sharedCache->setTimestamp(m_currentThemeTimestamp);
* }
*
* // Check cache and load image as usual...
* }
* @endcode
*
* @param newTimestamp The new timestamp to mark the shared cache with.
* @see timestamp()
* @since 4.6
*/
void setTimestamp(unsigned newTimestamp);
private:
class Private;
Private *d;
};
#endif

View file

@ -1,502 +0,0 @@
/*
* This file is part of the KDE project.
* Copyright © 2010 Michael Pyne <mpyne@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License version 2 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KSHAREDDATACACHE_P_H
#define KSHAREDDATACACHE_P_H
#include <config-util.h> // HAVE_SYS_MMAN_H
#include <QtCore/QSharedPointer>
#include <QtCore/qbasicatomic.h>
#include <unistd.h> // Check for sched_yield
#include <sched.h> // sched_yield
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <kdebug.h>
// Our debug area, disabled by default
int ksdcArea();
// Mac OS X, for all its POSIX compliance, does not support timeouts on its
// mutexes, which is kind of a disaster for cross-process support. However
// synchronization primitives still work, they just might hang if the cache is
// corrupted, so keep going.
#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
#if defined(_POSIX_MAPPED_FILES) && ((_POSIX_MAPPED_FILES == 0) || (_POSIX_MAPPED_FILES >= 200112L))
#define KSDC_MAPPED_FILES_SUPPORTED 1
#endif
#if defined(_POSIX_SYNCHRONIZED_IO) && ((_POSIX_SYNCHRONIZED_IO == 0) || (_POSIX_SYNCHRONIZED_IO >= 200112L))
#define KSDC_SYNCHRONIZED_IO_SUPPORTED 1
#endif
// msync(2) requires both MAPPED_FILES and SYNCHRONIZED_IO POSIX options
#if defined(KSDC_MAPPED_FILES_SUPPORTED) && defined(KSDC_SYNCHRONIZED_IO_SUPPORTED)
#define KSDC_MSYNC_SUPPORTED
#endif
// posix_fallocate is used to ensure that the file used for the cache is
// actually fully committed to disk before attempting to use the file.
#if defined(_POSIX_ADVISORY_INFO) && ((_POSIX_ADVISORY_INFO == 0) || (_POSIX_ADVISORY_INFO >= 200112L))
#define KSDC_POSIX_FALLOCATE_SUPPORTED 1
#endif
// BSD/Mac OS X compat
#if HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
#define MAP_ANONYMOUS MAP_ANON
#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()
{
}
// Return value indicates if the mutex was properly initialized (including
// threads-only as a fallback).
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)
{
}
virtual bool initialize(bool &processSharingSupported)
{
// Clear the spinlock
m_spinlock = 0;
processSharingSupported = true;
return true;
}
virtual bool lock()
{
// Spin a few times attempting to gain the lock, as upper-level code won't
// attempt again without assuming the cache is corrupt.
for (unsigned i = 50; i > 0; --i) {
if (m_spinlock.testAndSetAcquire(0, 1)) {
return true;
}
// Don't steal the processor and starve the thread we're waiting
// on.
loopSpinPause();
}
return false;
}
virtual void unlock()
{
m_spinlock.testAndSetRelease(1, 0);
}
private:
#ifdef Q_CC_GNU
__attribute__((always_inline, gnu_inline, artificial))
#endif
static inline void loopSpinPause()
{
// TODO: Spinning might be better in multi-core systems... but that means
// figuring how to find numbers of CPUs in a cross-platform way.
#ifdef _POSIX_PRIORITY_SCHEDULING
sched_yield();
#else
// Sleep for shortest possible time (nanosleep should round-up).
struct timespec wait_time = { 0 /* sec */, 100 /* ns */ };
::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)
{
}
virtual bool initialize(bool &processSharingSupported)
{
// Setup process-sharing.
pthread_mutexattr_t mutexAttr;
processSharingSupported = false;
// Initialize attributes, enable process-shared primitives, and setup
// the mutex.
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);
}
// Attempt to setup for thread-only synchronization.
if (!processSharingSupported && pthread_mutex_init(&m_mutex, NULL) != 0) {
return false;
}
return true;
}
virtual bool lock()
{
return pthread_mutex_lock(&m_mutex) == 0;
}
virtual void unlock()
{
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)
{
}
virtual bool lock()
{
struct timespec timeout;
// Long timeout, but if we fail to meet this timeout it's probably a cache
// corruption (and if we take 8 seconds then it should be much much quicker
// the next time anyways since we'd be paged back in from disk)
timeout.tv_sec = 10 + ::time(NULL); // Absolute time, so 10 seconds from now
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)
{
}
virtual bool initialize(bool &processSharingSupported)
{
processSharingSupported = false;
if (::sysconf(_SC_SEMAPHORES) < 200112L) {
return false;
}
// sem_init sets up process-sharing for us.
if (sem_init(&m_semaphore, 1, 1) == 0) {
processSharingSupported = true;
}
// If not successful try falling back to thread-shared.
else if (sem_init(&m_semaphore, 0, 1) != 0) {
return false;
}
return true;
}
virtual bool lock()
{
return sem_wait(&m_semaphore) == 0;
}
virtual void unlock()
{
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)
{
}
virtual bool lock()
{
struct timespec timeout;
// Long timeout, but if we fail to meet this timeout it's probably a cache
// corruption (and if we take 8 seconds then it should be much much quicker
// the next time anyways since we'd be paged back in from disk)
timeout.tv_sec = 10 + ::time(NULL); // Absolute time, so 10 seconds from now
timeout.tv_nsec = 0;
return sem_timedwait(&m_semaphore, &timeout) == 0;
}
};
#endif
// This enum controls the type of the locking used for the cache to allow
// for as much portability as possible. This value will be stored in the
// cache and used by multiple processes, therefore you should consider this
// a versioned field, do not re-arrange.
enum SharedLockId {
LOCKTYPE_INVALID = 0,
LOCKTYPE_MUTEX = 1, // pthread_mutex
LOCKTYPE_SEMAPHORE = 2, // sem_t
LOCKTYPE_SPINLOCK = 3 // atomic int in shared memory
};
// This type is a union of all possible lock types, with a SharedLockId used
// to choose which one is actually in use.
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;
// It would be highly unfortunate if a simple glibc upgrade or kernel
// addition caused this structure to change size when an existing
// lock was thought present, so reserve enough size to cover any
// reasonable locking structure
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().
*/
static SharedLockId findBestSharedLock()
{
// We would prefer a process-shared capability that also supports
// timeouts. Failing that, process-shared is preferred over timeout
// support. Failing that we'll go thread-local
bool timeoutsSupported = false;
bool pthreadsProcessShared = false;
bool semaphoresProcessShared = false;
#ifdef KSDC_TIMEOUTS_SUPPORTED
timeoutsSupported = ::sysconf(_SC_TIMEOUTS) >= 200112L;
#endif
// Now that we've queried timeouts, try actually creating real locks and
// seeing if there's issues with that.
#ifdef KSDC_THREAD_PROCESS_SHARED_SUPPORTED
{
pthread_mutex_t tempMutex;
QSharedPointer<KSDCLock> tempLock(0);
if (timeoutsSupported) {
#ifdef KSDC_TIMEOUTS_SUPPORTED
tempLock = QSharedPointer<KSDCLock>(new pthreadTimedLock(tempMutex));
#endif
}
else {
tempLock = QSharedPointer<KSDCLock>(new pthreadLock(tempMutex));
}
tempLock->initialize(pthreadsProcessShared);
}
#endif
// Our first choice is pthread_mutex_t for compatibility.
if(timeoutsSupported && pthreadsProcessShared) {
return LOCKTYPE_MUTEX;
}
#ifdef KSDC_SEMAPHORES_SUPPORTED
{
sem_t tempSemaphore;
QSharedPointer<KSDCLock> tempLock(0);
if (timeoutsSupported) {
tempLock = QSharedPointer<KSDCLock>(new semaphoreTimedLock(tempSemaphore));
}
else {
tempLock = QSharedPointer<KSDCLock>(new semaphoreLock(tempSemaphore));
}
tempLock->initialize(semaphoresProcessShared);
}
#endif
if(timeoutsSupported && semaphoresProcessShared) {
return LOCKTYPE_SEMAPHORE;
}
else if(pthreadsProcessShared) {
return LOCKTYPE_MUTEX;
}
else if(semaphoresProcessShared) {
return LOCKTYPE_SEMAPHORE;
}
// Fallback to a dumb-simple but possibly-CPU-wasteful solution.
return LOCKTYPE_SPINLOCK;
}
static KSDCLock *createLockFromId(SharedLockId id, SharedLock &lock)
{
switch(id) {
#ifdef KSDC_THREAD_PROCESS_SHARED_SUPPORTED
case LOCKTYPE_MUTEX:
#ifdef KSDC_TIMEOUTS_SUPPORTED
if (::sysconf(_SC_TIMEOUTS) >= 200112L) {
return new pthreadTimedLock(lock.mutex);
}
#endif
return new pthreadLock(lock.mutex);
break;
#endif
#ifdef KSDC_SEMAPHORES_SUPPORTED
case LOCKTYPE_SEMAPHORE:
#ifdef KSDC_TIMEOUTS_SUPPORTED
if (::sysconf(_SC_SEMAPHORES) >= 200112L) {
return new semaphoreTimedLock(lock.semaphore);
}
#endif
return new semaphoreLock(lock.semaphore);
break;
#endif
case LOCKTYPE_SPINLOCK:
return new simpleSpinLock(lock.spinlock);
break;
default:
kError(ksdcArea()) << "Creating shell of a lock!";
return new KSDCLock;
}
}
static bool ensureFileAllocated(int fd, size_t fileSize)
{
#ifdef KSDC_POSIX_FALLOCATE_SUPPORTED
int result;
while ((result = ::posix_fallocate(fd, 0, fileSize)) == EINTR) {
;
}
if (result < 0) {
kError(ksdcArea()) << "The operating system is unable to promise"
<< fileSize
<< "bytes for mapped cache, "
"abandoning the cache for crash-safety.";
return false;
}
return true;
#else
#ifdef __GNUC__
#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."
#endif
kWarning(ksdcArea()) << "This system misses support for posix_fallocate()"
" -- ensure this partition has room for at least"
<< fileSize << "bytes.";
// TODO: It's possible to emulate the functionality, but doing so
// overwrites the data in the file so we don't do this. If you were to add
// this emulation, you must ensure it only happens on initial creation of a
// new file and not just mapping an existing cache.
return true;
#endif
}
#endif /* KSHAREDDATACACHE_P_H */

View file

@ -191,7 +191,6 @@ set(kdeui_LIB_SRCS
util/kcrash.cpp
util/kcursor.cpp
util/kguiitem.cpp
util/kimagecache.cpp
util/kkeyserver.cpp
util/kmanagerselection.cpp
util/kmodifierkeyinfo.cpp
@ -574,7 +573,6 @@ install(
util/knumvalidator.h
util/kpassivepopup.h
util/kpassivepopupmessagehandler.h
util/kimagecache.h
util/kstandardguiitem.h
util/kwallet.h
util/kwordwrap.h

View file

@ -54,7 +54,6 @@
#include <kglobalsettings.h>
#include <kcomponentdata.h>
#include <kde_file.h>
#include <kshareddatacache.h>
// kdeui
#include "kicontheme.h"
@ -748,7 +747,7 @@ void KIconLoaderPrivate::normalizeIconMetadata(KIconLoader::Group &group, int &s
QString KIconLoaderPrivate::makeCacheKey(const QString &name, KIconLoader::Group group,
const QStringList &overlays, int size, int state) const
{
// The KSharedDataCache is shared so add some namespacing. The following code
// The icon cache is shared so add some namespacing. The following code
// uses QStringBuilder (new in Qt 4.6)
return (group == KIconLoader::User

View file

@ -1,200 +0,0 @@
/*
* This file is part of the KDE project.
* Copyright © 2010 Michael Pyne <mpyne@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License version 2 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kimagecache.h"
#include <QtGui/QPixmap>
#include <QtGui/QImage>
#include <QtCore/QBuffer>
#include <QtCore/QCache>
#include <QtCore/QCoreApplication>
#include <time.h>
/**
* This is a QObject subclass so we can catch the signal that the application is about
* to close and properly release any QPixmaps we have cached.
*/
class KImageCache::Private : public QObject
{
Q_OBJECT
public:
Private(QObject *parent = 0)
: QObject(parent)
, timestamp(::time(0))
, enablePixmapCaching(true)
{
QObject::connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()),
this, SLOT(clearPixmaps()));
}
/**
* Inserts a pixmap into the pixmap cache if the pixmap cache is enabled, with
* weighting based on image size and bit depth.
*/
bool insertPixmap(const QString &key, QPixmap *pixmap)
{
if (enablePixmapCaching && pixmap && !pixmap->isNull()) {
// "cost" parameter is based on both image size and depth to make cost
// based on size in bytes instead of area on-screen.
return pixmapCache.insert(key, pixmap,
pixmap->width() * pixmap->height() * pixmap->depth() / 8);
}
return false;
}
public slots:
void clearPixmaps()
{
pixmapCache.clear();
}
public:
time_t timestamp;
/**
* This is used to cache pixmaps as they are inserted, instead of always
* converting to image data and storing that in shared memory.
*/
QCache<QString, QPixmap> pixmapCache;
bool enablePixmapCaching;
};
KImageCache::KImageCache(const QString &cacheName,
unsigned defaultCacheSize,
unsigned expectedItemSize)
: KSharedDataCache(cacheName, defaultCacheSize, expectedItemSize)
, d(new Private)
{
// Use at least 16 KiB for the pixmap cache
d->pixmapCache.setMaxCost(qMax(defaultCacheSize / 8, (unsigned int) 16384));
}
KImageCache::~KImageCache()
{
delete d;
}
bool KImageCache::insertImage(const QString &key, const QImage &image)
{
QBuffer buffer;
buffer.open(QBuffer::WriteOnly);
image.save(&buffer, "PNG");
if (this->insert(key, buffer.buffer())) {
d->timestamp = ::time(0);
return true;
}
return false;
}
bool KImageCache::insertPixmap(const QString &key, const QPixmap &pixmap)
{
d->insertPixmap(key, new QPixmap(pixmap));
// One thing to think about is only inserting things to the shared cache
// that are frequently used. But that would require tracking the use count
// in our local cache too, which I think is probably too much work.
return insertImage(key, pixmap.toImage());
}
bool KImageCache::findImage(const QString &key, QImage *destination) const
{
QByteArray cachedData;
if (!this->find(key, &cachedData) || cachedData.isNull()) {
return false;
}
if (destination) {
destination->loadFromData(cachedData, "PNG");
}
return true;
}
bool KImageCache::findPixmap(const QString &key, QPixmap *destination) const
{
if (d->enablePixmapCaching) {
QPixmap *cachedPixmap = d->pixmapCache.object(key);
if (cachedPixmap) {
if (destination) {
*destination = *cachedPixmap;
}
return true;
}
}
QByteArray cachedData;
if (!this->find(key, &cachedData) || cachedData.isNull()) {
return false;
}
if (destination) {
destination->loadFromData(cachedData, "PNG");
// Manually re-insert to pixmap cache if we'll be using this one.
d->insertPixmap(key, new QPixmap(*destination));
}
return true;
}
void KImageCache::clear()
{
d->pixmapCache.clear();
KSharedDataCache::clear();
}
time_t KImageCache::lastModifiedTime() const
{
return d->timestamp;
}
bool KImageCache::pixmapCaching() const
{
return d->enablePixmapCaching;
}
void KImageCache::setPixmapCaching(bool enable)
{
if (enable != d->enablePixmapCaching) {
d->enablePixmapCaching = enable;
if (!enable) {
d->pixmapCache.clear();
}
}
}
int KImageCache::pixmapCacheLimit() const
{
return d->pixmapCache.maxCost();
}
void KImageCache::setPixmapCacheLimit(int size)
{
d->pixmapCache.setMaxCost(size);
}
#include "kimagecache.moc"

View file

@ -1,175 +0,0 @@
/*
* This file is part of the KDE project.
* Copyright © 2010 Michael Pyne <mpyne@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License version 2 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KIMAGECACHE_H
#define KIMAGECACHE_H
#include <kdeui_export.h>
#include <kshareddatacache.h>
#include <time.h>
class QImage;
class QPixmap;
/**
* @brief A simple wrapping layer over KSharedDataCache to support caching
* images and pixmaps.
*
* This class can be used to share images between different processes, which
* is useful when it is known that such images will be used across many
* processes, or when creating the image is expensive.
*
* In addition, the class also supports caching QPixmaps <em>in a single
* process</em> using the setPixmapCaching() function.
*
* Tips for use: If you already have QPixmaps that you intend to use, and
* you do not need access to the actual image data, then try to store and
* retrieve QPixmaps for use.
*
* On the other hand, if you will need to store and retrieve actual image
* data (to modify the image after retrieval for instance) then you should
* use QImage to save the conversion cost from QPixmap to QImage.
*
* KImageCache is a subclass of KSharedDataCache, so all of the methods that
* can be used with KSharedDataCache can be used with KImageCache,
* <em>with the exception of KSharedDataCache::insert() and
* KSharedDataCache::find()</em>.
*
* @author Michael Pyne <mpyne@kde.org>
* @since 4.5
*/
class KDEUI_EXPORT KImageCache : public KSharedDataCache
{
public:
/**
* Constructs an image cache, named by @p cacheName, with a default
* size of @p defaultCacheSize.
*
* @param cacheName Name of the cache to use.
* @param defaultCacheSize The default size, in bytes, of the cache.
* The actual on-disk size will be slightly larger. If the cache already
* exists, it will not be resized. If it is required to resize the
* cache then use the deleteCache() function to remove that cache first.
* @param expectedItemSize The expected general size of the items to be
* added to the image cache, in bytes. Use 0 if you just want a default
* item size.
*/
KImageCache(const QString &cacheName,
unsigned defaultCacheSize,
unsigned expectedItemSize = 0);
/**
* Deconstructor
*/
~KImageCache();
/**
* Inserts the pixmap given by @p pixmap to the cache, accessible with
* @p key. The pixmap must be converted to a QImage in order to be stored
* into shared memory. In order to prevent unnecessary conversions from
* taking place @p pixmap will also be cached (but not in stored
* memory) and would be accessible using findPixmap() if pixmap caching is
* enabled.
*
* @param key Name to access @p pixmap with.
* @param pixmap The pixmap to add to the cache.
* @return true if the pixmap was successfully cached, false otherwise.
* @see setPixmapCaching()
*/
bool insertPixmap(const QString &key, const QPixmap &pixmap);
/**
* Inserts the @p image into the shared cache, accessible with @p key. This
* variant is preferred over insertPixmap() if your source data is already a
* QImage, if it is essential that the image be in shared memory (such as
* for SVG icons which have a high render time), or if it will need to be
* in QImage form after it is retrieved from the cache.
*
* @param key Name to access @p image with.
* @param image The image to add to the shared cache.
* @return true if the image was successfully cached, false otherwise.
*/
bool insertImage(const QString &key, const QImage &image);
/**
* Copies the cached pixmap identified by @p key to @p destination. If no such
* pixmap exists @p destination is unchanged.
*
* @return true if the pixmap identified by @p key existed, false otherwise.
* @see setPixmapCaching()
*/
bool findPixmap(const QString &key, QPixmap *destination) const;
/**
* Copies the cached image identified by @p key to @p destination. If no such
* image exists @p destination is unchanged.
*
* @return true if the image identified by @p key existed, false otherwise.
*/
bool findImage(const QString &key, QImage *destination) const;
/**
* Removes all entries from the cache. In addition any cached pixmaps (as per
* setPixmapCaching()) are also removed.
*/
void clear();
/**
* @return The time that an image or pixmap was last inserted into a cache.
*/
time_t lastModifiedTime() const;
/**
* @return if QPixmaps added with insertPixmap() will be stored in a local
* pixmap cache as well as the shared image cache. The default is to cache
* pixmaps locally.
*/
bool pixmapCaching() const;
/**
* Enables or disables local pixmap caching. If it is anticipated that a pixmap
* will be frequently needed then this can actually save memory overall since the
* X server or graphics card will not have to store duplicate copies of the same
* image.
*
* @param enable Enables pixmap caching if true, disables otherwise.
*/
void setPixmapCaching(bool enable);
/**
* @return The highest memory size in bytes to be used by cached pixmaps.
* @since 4.6
*/
int pixmapCacheLimit() const;
/**
* Sets the highest memory size the pixmap cache should use.
*
* @param size The size in bytes
* @since 4.6
*/
void setPixmapCacheLimit(int size);
private:
class Private;
Private *const d; ///< @internal
};
#endif /* KIMAGECACHE_H */

View file

@ -107,7 +107,6 @@ public:
QSizeF size;
QSizeF naturalSize;
QChar styleCrc;
unsigned int lastModified;
bool multipleImages : 1;
bool themed : 1;
bool applyColors : 1;

View file

@ -138,7 +138,6 @@ SvgPrivate::SvgPrivate(Svg *svg)
: q(svg),
renderer(0),
styleCrc(0),
lastModified(0),
multipleImages(false),
themed(false),
applyColors(false),
@ -228,12 +227,6 @@ bool SvgPrivate::setImagePath(const QString &imagePath)
}
}
if (!themed) {
QFile f(imagePath);
QFileInfo info(f);
lastModified = info.lastModified().toTime_t();
}
return updateNeeded;
}
@ -336,7 +329,7 @@ QPixmap SvgPrivate::findInCache(const QString &elementId, const QSizeF &s)
//kDebug() << "id is " << id;
QPixmap p;
if (cacheRendering && cacheAndColorsTheme()->findInCache(id, p, lastModified)) {
if (cacheRendering && cacheAndColorsTheme()->findInCache(id, p)) {
//kDebug() << "found cached version of " << id << p.size();
return p;
}

View file

@ -26,6 +26,8 @@
#include <QPair>
#include <QStringBuilder>
#include <QTimer>
#include <QCache>
#include <QBuffer>
#ifdef Q_WS_X11
#include <QtGui/qx11info_x11.h>
#include "private/effectwatcher_p.h"
@ -39,7 +41,6 @@
#include <kglobal.h>
#include <kglobalsettings.h>
#include <kmanagerselection.h>
#include <kimagecache.h>
#include <ksharedconfig.h>
#include <kstandarddirs.h>
#include <kwindowsystem.h>
@ -84,7 +85,6 @@ public:
defaultWallpaperSuffix(DEFAULT_WALLPAPER_SUFFIX),
defaultWallpaperWidth(DEFAULT_WALLPAPER_WIDTH),
defaultWallpaperHeight(DEFAULT_WALLPAPER_HEIGHT),
pixmapCache(0),
cachesToDiscard(NoCache),
locolor(false),
compositingActive(KWindowSystem::self()->compositingActive()),
@ -98,6 +98,9 @@ public:
ThemeConfig config;
cacheTheme = config.cacheTheme();
pixmapCache = new QSharedPointer<QCache<QString, QPixmap> >(new QCache<QString, QPixmap>());
pixmapCache->data()->setMaxCost(config.themeCacheKb() * 1024);
saveTimer = new QTimer(q);
saveTimer->setSingleShot(true);
saveTimer->setInterval(600);
@ -184,7 +187,7 @@ public:
QString defaultWallpaperSuffix;
int defaultWallpaperWidth;
int defaultWallpaperHeight;
KImageCache *pixmapCache;
QSharedPointer<QCache<QString, QPixmap> > *pixmapCache;
KSharedConfigPtr svgElementsCache;
QHash<QString, QSet<QString> > invalidElements;
QHash<QString, QPixmap> pixmapsToCache;
@ -223,11 +226,8 @@ EffectWatcher *ThemePrivate::s_blurEffectWatcher = 0;
bool ThemePrivate::useCache()
{
bool cachesTooOld = false;
if (cacheTheme && !pixmapCache) {
const bool isRegularTheme = themeName != systemColorsTheme;
QString cacheFile = "plasma_theme_" + themeName;
// clear any cached values from the previous theme cache
themeVersion.clear();
@ -237,66 +237,18 @@ bool ThemePrivate::useCache()
}
themeMetadataPath = KStandardDirs::locate("data", "desktoptheme/" + themeName + "/metadata.desktop");
if (isRegularTheme) {
const QString cacheFileBase = cacheFile + "*.kcache";
// if the path is empty, then we haven't found the theme and so
// we will leave currentCacheFileName empty, resulting in the deletion of
// *all* matching cache files
QString currentCacheFileName;
if (!themeMetadataPath.isEmpty()) {
// now we record the theme version, if we can
const KPluginInfo pluginInfo(themeMetadataPath);
themeVersion = pluginInfo.version();
if (!themeVersion.isEmpty()) {
cacheFile += "_v" + themeVersion;
currentCacheFileName = cacheFile + ".kcache";
}
// watch the metadata file for changes at runtime
KDirWatch::self()->addFile(themeMetadataPath);
QObject::connect(KDirWatch::self(), SIGNAL(created(QString)),
q, SLOT(settingsFileChanged(QString)),
Qt::UniqueConnection);
QObject::connect(KDirWatch::self(), SIGNAL(dirty(QString)),
q, SLOT(settingsFileChanged(QString)),
Qt::UniqueConnection);
}
// now we check for (and remove) old caches
foreach (const QString &file, KGlobal::dirs()->findAllResources("cache", cacheFileBase)) {
if (currentCacheFileName.isEmpty() ||
!file.endsWith(currentCacheFileName)) {
QFile::remove(file);
}
}
}
// now we do a sanity check: if the metadata.desktop file is newer than the cache, drop
// the cache
if (isRegularTheme && !themeMetadataPath.isEmpty()) {
// now we check to see if the theme metadata file itself is newer than the pixmap cache
// this is done before creating the pixmapCache object since that can change the mtime
// on the cache file
// FIXME: when using the system colors, if they change while the application is not running
// the cache should be dropped; we need a way to detect system color change when the
// application is not running.
// check for expired cache
const QString cacheFilePath = KStandardDirs::locateLocal("cache", cacheFile);
if (!cacheFilePath.isEmpty()) {
const QFileInfo cacheFileInfo(cacheFilePath);
const QFileInfo metadataFileInfo(themeMetadataPath);
cachesTooOld = cacheFileInfo.lastModified().toTime_t() > metadataFileInfo.lastModified().toTime_t();
}
// watch the metadata file for changes at runtime
KDirWatch::self()->addFile(themeMetadataPath);
QObject::connect(KDirWatch::self(), SIGNAL(created(QString)),
q, SLOT(settingsFileChanged(QString)),
Qt::UniqueConnection);
QObject::connect(KDirWatch::self(), SIGNAL(dirty(QString)),
q, SLOT(settingsFileChanged(QString)),
Qt::UniqueConnection);
}
ThemeConfig config;
pixmapCache = new KImageCache(cacheFile, config.themeCacheKb() * 1024);
if (cachesTooOld) {
discardCache(PixmapCache | SvgElementsCache);
}
// TODO: discardCache(PixmapCache | SvgElementsCache); ?
}
if (cacheTheme && !svgElementsCache) {
@ -308,7 +260,7 @@ bool ThemePrivate::useCache()
// now we check for (and remove) old caches
foreach (const QString &file, KGlobal::dirs()->findAllResources("cache", svgElementsFileNameBase + "*")) {
if (cachesTooOld || !file.endsWith(svgElementsFileName)) {
if (!file.endsWith(svgElementsFileName)) {
QFile::remove(file);
}
}
@ -376,13 +328,10 @@ void ThemePrivate::discardCache(CacheTypes caches)
if (caches & PixmapCache) {
pixmapsToCache.clear();
saveTimer->stop();
if (pixmapCache) {
pixmapCache->clear();
}
} else {
// This deletes the object but keeps the on-disk cache for later use
delete pixmapCache;
pixmapCache = 0;
}
// FIXME: this is always done to properly change theme but should be done only on demand
if (pixmapCache) {
pixmapCache->data()->clear();
}
cachedStyleSheets.clear();
@ -400,7 +349,7 @@ void ThemePrivate::scheduledCacheUpdate()
QHashIterator<QString, QPixmap> it(pixmapsToCache);
while (it.hasNext()) {
it.next();
pixmapCache->insertPixmap(idsToCache[it.key()], it.value());
pixmapCache->data()->insert(idsToCache[it.key()], new QPixmap(it.value()));
}
}
@ -1031,9 +980,12 @@ bool Theme::findInCache(const QString &key, QPixmap &pix)
return !pix.isNull();
}
QPixmap temp;
if (d->pixmapCache->findPixmap(key, &temp) && !temp.isNull()) {
pix = temp;
QPixmap *temp = d->pixmapCache->data()->object(key);
if (temp && !temp->isNull()) {
QBuffer buffer(this);
buffer.open(QIODevice::WriteOnly);
temp->save(&buffer, "PNG");
pix.loadFromData(buffer.buffer(), "PNG");
return true;
}
}
@ -1041,20 +993,10 @@ bool Theme::findInCache(const QString &key, QPixmap &pix)
return false;
}
// BIC FIXME: Should be merged with the other findInCache method above when we break BC
bool Theme::findInCache(const QString &key, QPixmap &pix, unsigned int lastModified)
{
if (d->useCache() && lastModified > uint(d->pixmapCache->lastModifiedTime())) {
return false;
}
return findInCache(key, pix);
}
void Theme::insertIntoCache(const QString& key, const QPixmap& pix)
{
if (d->useCache()) {
d->pixmapCache->insertPixmap(key, pix);
d->pixmapCache->data()->insert(key, new QPixmap(pix));
}
}

View file

@ -273,20 +273,6 @@ class PLASMA_EXPORT Theme : public QObject
**/
bool findInCache(const QString &key, QPixmap &pix);
/**
* This is an overloaded member provided to check with file timestamp
* where cache is still valid.
*
* @param key the name to use in the cache for this image
* @param pix the pixmap object to populate with the resulting data if found
* @param lastModified if non-zero, the time stamp is also checked on the file,
* and must be newer than the timestamp to be loaded
*
* @return true when pixmap was found and loaded from cache, false otherwise
* @since 4.3
**/
bool findInCache(const QString &key, QPixmap &pix, unsigned int lastModified);
/**
* Insert specified pixmap into the cache.
* If the cache already contains pixmap with the specified key then it is