mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-23 18:32:49 +00:00
generic: get rid of KImageCache and KSharedDataCache
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
parent
ef15158907
commit
c6f33f23aa
17 changed files with 30 additions and 3050 deletions
|
@ -177,7 +177,6 @@ install(
|
|||
KIconLoader
|
||||
KIconTheme
|
||||
KIdentityProxyModel
|
||||
KImageCache
|
||||
KImageFilePreview
|
||||
KImageIO
|
||||
KInputDialog
|
||||
|
@ -299,7 +298,6 @@ install(
|
|||
KServiceTypeTrader
|
||||
KSessionManager
|
||||
KSharedConfig
|
||||
KSharedDataCache
|
||||
KSharedPtr
|
||||
KShell
|
||||
KShellCompletion
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
#include "../kimagecache.h"
|
|
@ -1 +0,0 @@
|
|||
#include "../kshareddatacache.h"
|
|
@ -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
|
||||
|
|
|
@ -42,7 +42,6 @@ KDECORE_UNIT_TESTS(
|
|||
klockfiletest
|
||||
ktempdirtest
|
||||
ksharedptrtest
|
||||
kshareddatacachetest
|
||||
kdatetimetest
|
||||
ksavefiletest
|
||||
kautosavefiletest
|
||||
|
|
|
@ -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
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
|
@ -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 */
|
|
@ -107,7 +107,6 @@ public:
|
|||
QSizeF size;
|
||||
QSizeF naturalSize;
|
||||
QChar styleCrc;
|
||||
unsigned int lastModified;
|
||||
bool multipleImages : 1;
|
||||
bool themed : 1;
|
||||
bool applyColors : 1;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
114
plasma/theme.cpp
114
plasma/theme.cpp
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue