You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
amarok/amarok/src/atomicstring.h

192 lines
6.1 KiB

/*
Copyright (c) 2006 Gábor Lehel <illissius@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) 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 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.
*/
/**
* A thin wrapper over TQString which ensures only a single copy of string data
* is stored for equivalent strings. As a side benefit, testing for equality
* is reduced to a pointer comparison. Construction is slower than a TQString,
* as it must be checked for equality with existing strings. (A hash set is
* used for this purpose. According to benchmarks, Paul Hsieh's SuperFastHash
* (which is currently used -- see http://www.azillionmonkeys.com/qed/hash.html)
* can hash 5 million 256 byte strings in 1.34s on a 1.62GHz Athlon XP.) For
* other use, the overhead compared to a plain TQString should be minimal.
*
* Added note: due to TQString's thread unsafe refcounting, special precautions have to be
* taken to avoid memory corruption, while still maintaining some level of efficiency.
* We deepCopy strings, unless we are in the same thread that *first* used
* AtomicStrings. Also, deletions from other threads are delayed until that first thread
* calls AtomicString again. Thus, we would appear to leak memory if many AtomicStrings
* are deleted in a different thread than the main thread, and the main thread would
* never call AtomicString again. But this is unlikely since the GUI thread is the one
* manipulating AtomicStrings mostly. You can call the static method
* AtomicString::isMainString first thing in the app to make sure the GUI thread is
* identified correctly. This workaround can be removed with QT4.
*
* @author Gábor Lehel <illissius@gmail.com>
*/
#ifndef AMAROK_ATOMICSTRING_H
#define AMAROK_ATOMICSTRING_H
#include "config.h"
#include <set>
#include "amarok_export.h"
#include <tqstring.h>
#include <tqptrlist.h>
#include <tqmutex.h>
class LIBAMAROK_EXPORT AtomicString
{
public:
/**
* Constructs an empty string.
* @see isEmpty
*/
AtomicString();
/**
* Constructs a copy of \p string. This operation takes constant time.
* @param string the string to copy
* @see operator=
*/
AtomicString( const AtomicString &other );
/**
* Constructs a copy of \p string.
* @param string the string to copy
*/
AtomicString( const TQString &string );
/**
* Destroys the string.
* Note: this isn't virtual, to halve sizeof().
*/
~AtomicString();
/**
* Makes this string a copy of \p string. This operation takes constant time.
* @param string the string to copy
*/
AtomicString &operator=( const AtomicString &other );
/**
* This operation takes constant time.
* @return whether this string and \p string are equivalent
*/
bool operator==( const AtomicString &other ) const { return m_string == other.m_string; }
bool operator<( const AtomicString &other ) const { return m_string < other.m_string; }
// bool operator!=( const AtomicString &other ) const { return m_string != other.m_string; }
/**
* Returns a reference to this string, avoiding copies if possible.
*
* @return the string.
*/
TQString string() const;
/**
* Implicitly casts to a TQString.
* @return the string
*/
inline operator TQString() const { return string(); }
/**
* Useful for threading.
* @return a deep copy of the string
*/
TQString deepCopy() const;
/**
* For convenience. Equivalent to isNull().
* @return whether the string is empty
* @see isNull
*/
bool isEmpty() const;
/**
* For convenience. Equivalent to isEmpty().
* @return whether the string is empty
* @see isEmpty
*/
inline bool isNull() const { return isEmpty(); }
/**
* Returns the internal pointer to the string.
* Guaranteed to be equivalent for equivalent strings, and different for
* different ones. This can be useful for certain kinds of hacks, but
* shouldn't normally be used.
*
* Note: DO NOT COPY this pointer with TQString() or TQString=. It is not
* thread safe to do it (TQString internal refcount)
* @return the internal pointer to the string
*/
const TQString *ptr() const;
/**
* For convenience, so you can do atomicstring->TQStringfunction(),
* instead of atomicstring.string().TQStringfunction(). The same warning
* applies as for the above ptr() function.
*/
inline const TQString *operator->() const { return ptr(); }
/**
* For debugging purposes.
* @return the number of nonempty AtomicStrings equivalent to this one
*/
uint refcount() const;
/**
* If called first thing in the app, this makes sure that AtomicString optimizes
* string usage for the main thread.
* @return true if this thread is considered the "main thread".
*/
static bool isMainThread();
private:
struct less
{
bool operator()( const TQString *a, const TQString *b ) const
{ return *a < *b; }
};
typedef std::set<TQString*, less> set_type;
class Data;
friend class Data;
void ref( Data* );
void deref( Data* );
static void checkLazyDeletes();
Data *m_string;
// static data
static set_type s_store; // main string store
static TQPtrList<TQString> s_lazyDeletes; // strings scheduled for deletion
// by main thread
static TQMutex s_storeMutex; // protects the static data above
};
#endif