2015-04-09 20:25:05 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2012-05-21 15:12:37 +04:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
1999-05-26 05:38:36 +04:00
|
|
|
|
2011-12-20 01:58:30 +04:00
|
|
|
#include "mozilla/Assertions.h"
|
2012-06-06 03:51:58 +04:00
|
|
|
#include "mozilla/Attributes.h"
|
2012-03-13 02:53:18 +04:00
|
|
|
#include "mozilla/HashFunctions.h"
|
2013-06-23 16:03:39 +04:00
|
|
|
#include "mozilla/MemoryReporting.h"
|
2016-05-26 04:50:37 +03:00
|
|
|
#include "mozilla/Mutex.h"
|
2013-11-09 03:29:09 +04:00
|
|
|
#include "mozilla/DebugOnly.h"
|
2017-01-26 23:43:38 +03:00
|
|
|
#include "mozilla/Sprintf.h"
|
2016-08-23 07:09:32 +03:00
|
|
|
#include "mozilla/Unused.h"
|
2011-12-20 01:58:30 +04:00
|
|
|
|
2017-10-22 00:50:25 +03:00
|
|
|
#include "nsAtom.h"
|
1999-05-26 05:38:36 +04:00
|
|
|
#include "nsAtomTable.h"
|
2018-03-14 03:11:02 +03:00
|
|
|
#include "nsAutoPtr.h"
|
1998-04-14 00:24:54 +04:00
|
|
|
#include "nsCRT.h"
|
2009-09-18 19:13:10 +04:00
|
|
|
#include "nsDataHashtable.h"
|
2018-03-14 03:11:02 +03:00
|
|
|
#include "nsGkAtoms.h"
|
2009-09-18 19:13:10 +04:00
|
|
|
#include "nsHashKeys.h"
|
2017-01-26 23:43:38 +03:00
|
|
|
#include "nsPrintfCString.h"
|
2018-03-14 03:11:02 +03:00
|
|
|
#include "nsStaticAtom.h"
|
|
|
|
#include "nsString.h"
|
|
|
|
#include "nsThreadUtils.h"
|
|
|
|
#include "nsUnicharUtils.h"
|
|
|
|
#include "PLDHashTable.h"
|
|
|
|
#include "prenv.h"
|
2003-03-25 21:55:59 +03:00
|
|
|
|
Bug 1261735 (part 1) - Overhaul the atom implementation. r=froydnj,erahm.
This patch changes things so that dynamic atoms and static atoms have distinct
implementations. This is a step towards allowing dynamic atoms and static atoms
to have different layouts in memory, which will allow static atoms to be
represented more compactly.
Specifically, the patch does the following.
- It renames AtomImpl as DynamicAtom and PermanentAtomImpl as StaticAtom, and
the latter is no longer a subclass of the former. This required duplicating
some methods from the former into the latter: ScriptableToString(),
ToUTF8String(), ScriptableEquals(), IsStaticAtom(). (This duplication will
disappear in the future if the representations of dynamic atoms and static
atoms diverge. Indeed, SizeOfIncludingThis() is already different in the two
classes.)
- It replaces all mentions of "permanent"/"non-permanent" atoms with
"static"/"dynamic".
- In ~DynamicAtom() it removes the check that causes gAtomTable to be deleted
when it becomes empty. This will only happen at shutdown and so doesn't seem
useful.
- It documents better various things, especially the basics of the
dynamic/static split, the transmutation of dynamic atoms to static atoms, and
the details of the SizeOf functions.
--HG--
extra : rebase_source : dbf903012e70ebf1a43de1e1088db1bc1b8dd4f4
2016-04-01 03:18:06 +03:00
|
|
|
// There are two kinds of atoms handled by this module.
|
|
|
|
//
|
2017-09-21 05:36:53 +03:00
|
|
|
// - Dynamic: the atom itself is heap allocated, as is the nsStringBuffer it
|
|
|
|
// points to. |gAtomTable| holds weak references to dynamic atoms. When the
|
|
|
|
// refcount of a dynamic atom drops to zero, we increment a static counter.
|
|
|
|
// When that counter reaches a certain threshold, we iterate over the atom
|
|
|
|
// table, removing and deleting dynamic atoms with refcount zero. This allows
|
2016-05-27 03:55:53 +03:00
|
|
|
// us to avoid acquiring the atom table lock during normal refcounting.
|
Bug 1261735 (part 1) - Overhaul the atom implementation. r=froydnj,erahm.
This patch changes things so that dynamic atoms and static atoms have distinct
implementations. This is a step towards allowing dynamic atoms and static atoms
to have different layouts in memory, which will allow static atoms to be
represented more compactly.
Specifically, the patch does the following.
- It renames AtomImpl as DynamicAtom and PermanentAtomImpl as StaticAtom, and
the latter is no longer a subclass of the former. This required duplicating
some methods from the former into the latter: ScriptableToString(),
ToUTF8String(), ScriptableEquals(), IsStaticAtom(). (This duplication will
disappear in the future if the representations of dynamic atoms and static
atoms diverge. Indeed, SizeOfIncludingThis() is already different in the two
classes.)
- It replaces all mentions of "permanent"/"non-permanent" atoms with
"static"/"dynamic".
- In ~DynamicAtom() it removes the check that causes gAtomTable to be deleted
when it becomes empty. This will only happen at shutdown and so doesn't seem
useful.
- It documents better various things, especially the basics of the
dynamic/static split, the transmutation of dynamic atoms to static atoms, and
the details of the SizeOf functions.
--HG--
extra : rebase_source : dbf903012e70ebf1a43de1e1088db1bc1b8dd4f4
2016-04-01 03:18:06 +03:00
|
|
|
//
|
2017-09-21 05:36:53 +03:00
|
|
|
// - Static: the atom itself is heap allocated, but it points to a static
|
|
|
|
// nsStringBuffer. |gAtomTable| effectively owns static atoms, because such
|
|
|
|
// atoms ignore all AddRef/Release calls, which ensures they stay alive until
|
Bug 1261735 (part 1) - Overhaul the atom implementation. r=froydnj,erahm.
This patch changes things so that dynamic atoms and static atoms have distinct
implementations. This is a step towards allowing dynamic atoms and static atoms
to have different layouts in memory, which will allow static atoms to be
represented more compactly.
Specifically, the patch does the following.
- It renames AtomImpl as DynamicAtom and PermanentAtomImpl as StaticAtom, and
the latter is no longer a subclass of the former. This required duplicating
some methods from the former into the latter: ScriptableToString(),
ToUTF8String(), ScriptableEquals(), IsStaticAtom(). (This duplication will
disappear in the future if the representations of dynamic atoms and static
atoms diverge. Indeed, SizeOfIncludingThis() is already different in the two
classes.)
- It replaces all mentions of "permanent"/"non-permanent" atoms with
"static"/"dynamic".
- In ~DynamicAtom() it removes the check that causes gAtomTable to be deleted
when it becomes empty. This will only happen at shutdown and so doesn't seem
useful.
- It documents better various things, especially the basics of the
dynamic/static split, the transmutation of dynamic atoms to static atoms, and
the details of the SizeOf functions.
--HG--
extra : rebase_source : dbf903012e70ebf1a43de1e1088db1bc1b8dd4f4
2016-04-01 03:18:06 +03:00
|
|
|
// |gAtomTable| itself is destroyed whereupon they are explicitly deleted.
|
2016-05-26 04:50:37 +03:00
|
|
|
//
|
2018-02-23 01:02:48 +03:00
|
|
|
// Note that gAtomTable is used on multiple threads, and has internal
|
|
|
|
// synchronization.
|
Bug 1261735 (part 1) - Overhaul the atom implementation. r=froydnj,erahm.
This patch changes things so that dynamic atoms and static atoms have distinct
implementations. This is a step towards allowing dynamic atoms and static atoms
to have different layouts in memory, which will allow static atoms to be
represented more compactly.
Specifically, the patch does the following.
- It renames AtomImpl as DynamicAtom and PermanentAtomImpl as StaticAtom, and
the latter is no longer a subclass of the former. This required duplicating
some methods from the former into the latter: ScriptableToString(),
ToUTF8String(), ScriptableEquals(), IsStaticAtom(). (This duplication will
disappear in the future if the representations of dynamic atoms and static
atoms diverge. Indeed, SizeOfIncludingThis() is already different in the two
classes.)
- It replaces all mentions of "permanent"/"non-permanent" atoms with
"static"/"dynamic".
- In ~DynamicAtom() it removes the check that causes gAtomTable to be deleted
when it becomes empty. This will only happen at shutdown and so doesn't seem
useful.
- It documents better various things, especially the basics of the
dynamic/static split, the transmutation of dynamic atoms to static atoms, and
the details of the SizeOf functions.
--HG--
extra : rebase_source : dbf903012e70ebf1a43de1e1088db1bc1b8dd4f4
2016-04-01 03:18:06 +03:00
|
|
|
|
2012-03-13 02:53:18 +04:00
|
|
|
using namespace mozilla;
|
|
|
|
|
2016-04-06 05:01:24 +03:00
|
|
|
//----------------------------------------------------------------------
|
2001-10-21 03:19:07 +04:00
|
|
|
|
2017-09-25 09:30:32 +03:00
|
|
|
enum class GCKind {
|
|
|
|
RegularOperation,
|
|
|
|
Shutdown,
|
|
|
|
};
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
2017-09-06 08:06:16 +03:00
|
|
|
// gUnusedAtomCount is incremented when an atom loses its last reference
|
|
|
|
// (and thus turned into unused state), and decremented when an unused
|
|
|
|
// atom gets a reference again. The atom table relies on this value to
|
|
|
|
// schedule GC. This value can temporarily go below zero when multiple
|
|
|
|
// threads are operating the same atom, so it has to be signed so that
|
|
|
|
// we wouldn't use overflow value for comparison.
|
2017-09-26 01:33:21 +03:00
|
|
|
// See nsAtom::AddRef() and nsAtom::Release().
|
2017-09-06 08:06:16 +03:00
|
|
|
static Atomic<int32_t, ReleaseAcquire> gUnusedAtomCount(0);
|
2016-05-27 03:55:53 +03:00
|
|
|
|
2018-03-05 05:54:06 +03:00
|
|
|
// Dynamic atoms need a ref count; this class adds that to nsAtom.
|
|
|
|
class nsDynamicAtom : public nsAtom
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
// We can't use NS_INLINE_DECL_THREADSAFE_REFCOUNTING because the refcounting
|
|
|
|
// of this type is special.
|
|
|
|
MozExternalRefCountType AddRef();
|
|
|
|
MozExternalRefCountType Release();
|
|
|
|
|
|
|
|
static nsDynamicAtom* As(nsAtom* aAtom)
|
|
|
|
{
|
2018-03-07 07:17:34 +03:00
|
|
|
MOZ_ASSERT(aAtom->IsDynamic());
|
2018-03-05 05:54:06 +03:00
|
|
|
return static_cast<nsDynamicAtom*>(aAtom);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
friend class nsAtomTable;
|
|
|
|
friend class nsAtomSubTable;
|
|
|
|
|
|
|
|
// Construction is done by |friend|s.
|
|
|
|
nsDynamicAtom(const nsAString& aString, uint32_t aHash)
|
|
|
|
: nsAtom(AtomKind::DynamicAtom, aString, aHash)
|
|
|
|
, mRefCnt(1)
|
|
|
|
{}
|
|
|
|
|
|
|
|
mozilla::ThreadSafeAutoRefCnt mRefCnt;
|
|
|
|
};
|
|
|
|
|
2018-03-07 03:57:54 +03:00
|
|
|
static char16_t*
|
|
|
|
FromStringBuffer(const nsAString& aString)
|
2014-07-09 19:15:21 +04:00
|
|
|
{
|
2018-03-07 03:57:54 +03:00
|
|
|
char16_t* str;
|
|
|
|
size_t length = aString.Length();
|
2017-09-25 09:30:32 +03:00
|
|
|
RefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString);
|
|
|
|
if (buf) {
|
2018-03-07 03:57:54 +03:00
|
|
|
str = static_cast<char16_t*>(buf->Data());
|
2017-09-25 09:30:32 +03:00
|
|
|
} else {
|
2018-03-07 03:57:54 +03:00
|
|
|
const size_t size = (length + 1) * sizeof(char16_t);
|
2017-09-25 09:30:32 +03:00
|
|
|
buf = nsStringBuffer::Alloc(size);
|
|
|
|
if (MOZ_UNLIKELY(!buf)) {
|
2018-03-07 03:57:54 +03:00
|
|
|
NS_ABORT_OOM(size); // OOM because atom allocations should be small.
|
2016-04-06 04:28:39 +03:00
|
|
|
}
|
2018-03-07 03:57:54 +03:00
|
|
|
str = static_cast<char16_t*>(buf->Data());
|
|
|
|
CopyUnicodeTo(aString, 0, str, length);
|
|
|
|
str[length] = char16_t(0);
|
2017-09-25 09:30:32 +03:00
|
|
|
}
|
2016-04-06 04:28:39 +03:00
|
|
|
|
2018-03-07 03:57:54 +03:00
|
|
|
MOZ_ASSERT(buf && buf->StorageSize() >= (length + 1) * sizeof(char16_t),
|
|
|
|
"enough storage");
|
|
|
|
|
|
|
|
// Take ownership of the string buffer.
|
|
|
|
mozilla::Unused << buf.forget();
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This constructor is for dynamic atoms and HTML5 atoms.
|
|
|
|
nsAtom::nsAtom(AtomKind aKind, const nsAString& aString, uint32_t aHash)
|
|
|
|
: mLength(aString.Length())
|
|
|
|
, mKind(static_cast<uint32_t>(aKind))
|
|
|
|
, mHash(aHash)
|
|
|
|
, mString(FromStringBuffer(aString))
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aKind == AtomKind::DynamicAtom || aKind == AtomKind::HTML5Atom);
|
|
|
|
|
2018-03-07 07:17:34 +03:00
|
|
|
MOZ_ASSERT_IF(!IsHTML5(), mHash == HashString(mString, mLength));
|
2016-04-06 04:28:39 +03:00
|
|
|
|
2017-09-26 09:43:31 +03:00
|
|
|
MOZ_ASSERT(mString[mLength] == char16_t(0), "null terminated");
|
|
|
|
MOZ_ASSERT(Equals(aString), "correct data");
|
2017-09-25 09:30:32 +03:00
|
|
|
}
|
2011-12-23 05:24:44 +04:00
|
|
|
|
2017-09-25 09:30:32 +03:00
|
|
|
// This constructor is for static atoms.
|
2017-10-12 02:52:17 +03:00
|
|
|
nsAtom::nsAtom(const char16_t* aString, uint32_t aLength, uint32_t aHash)
|
2017-09-26 09:43:31 +03:00
|
|
|
: mLength(aLength)
|
|
|
|
, mKind(static_cast<uint32_t>(AtomKind::StaticAtom))
|
|
|
|
, mHash(aHash)
|
2017-10-12 02:52:17 +03:00
|
|
|
, mString(const_cast<char16_t*>(aString))
|
2017-09-25 09:30:32 +03:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(mHash == HashString(mString, mLength));
|
2016-04-06 04:28:39 +03:00
|
|
|
|
2017-09-25 09:30:32 +03:00
|
|
|
MOZ_ASSERT(mString[mLength] == char16_t(0), "null terminated");
|
2017-10-12 02:52:17 +03:00
|
|
|
MOZ_ASSERT(NS_strlen(mString) == mLength, "correct storage");
|
2017-09-25 09:30:32 +03:00
|
|
|
}
|
2011-12-23 05:24:44 +04:00
|
|
|
|
2017-10-03 01:05:19 +03:00
|
|
|
nsAtom::~nsAtom()
|
2017-09-25 09:30:32 +03:00
|
|
|
{
|
2018-03-07 07:17:34 +03:00
|
|
|
if (!IsStatic()) {
|
|
|
|
MOZ_ASSERT(IsDynamic() || IsHTML5());
|
2018-03-07 03:57:54 +03:00
|
|
|
GetStringBuffer()->Release();
|
2017-08-24 04:10:04 +03:00
|
|
|
}
|
2017-09-25 09:30:32 +03:00
|
|
|
}
|
2016-04-06 05:01:24 +03:00
|
|
|
|
2017-10-12 02:52:17 +03:00
|
|
|
void
|
|
|
|
nsAtom::ToString(nsAString& aString) const
|
|
|
|
{
|
|
|
|
// See the comment on |mString|'s declaration.
|
2018-03-07 07:17:34 +03:00
|
|
|
if (IsStatic()) {
|
2017-10-12 02:52:17 +03:00
|
|
|
// AssignLiteral() lets us assign without copying. This isn't a string
|
|
|
|
// literal, but it's a static atom and thus has an unbounded lifetime,
|
|
|
|
// which is what's important.
|
|
|
|
aString.AssignLiteral(mString, mLength);
|
|
|
|
} else {
|
2018-03-07 03:57:54 +03:00
|
|
|
GetStringBuffer()->ToString(mLength, aString);
|
2017-10-12 02:52:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-26 01:33:21 +03:00
|
|
|
void
|
|
|
|
nsAtom::ToUTF8String(nsACString& aBuf) const
|
2016-04-06 05:01:24 +03:00
|
|
|
{
|
2018-03-07 07:17:34 +03:00
|
|
|
MOZ_ASSERT(!IsHTML5(), "Called ToUTF8String() on an HTML5 atom");
|
2016-04-06 05:01:24 +03:00
|
|
|
CopyUTF16toUTF8(nsDependentString(mString, mLength), aBuf);
|
|
|
|
}
|
|
|
|
|
2018-02-28 03:05:07 +03:00
|
|
|
void
|
|
|
|
nsAtom::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, AtomsSizes& aSizes)
|
|
|
|
const
|
2016-04-06 05:01:24 +03:00
|
|
|
{
|
2018-03-07 07:17:34 +03:00
|
|
|
MOZ_ASSERT(!IsHTML5(),
|
2018-02-28 03:05:07 +03:00
|
|
|
"Called AddSizeOfIncludingThis() on an HTML5 atom");
|
|
|
|
size_t thisSize = aMallocSizeOf(this);
|
2018-03-07 07:17:34 +03:00
|
|
|
if (IsStatic()) {
|
2018-02-28 03:05:07 +03:00
|
|
|
// String buffers pointed to by static atoms are in static memory, and so
|
|
|
|
// are not measured here.
|
|
|
|
aSizes.mStaticAtomObjects += thisSize;
|
2017-08-24 04:10:04 +03:00
|
|
|
} else {
|
2018-02-28 03:05:07 +03:00
|
|
|
aSizes.mDynamicAtomObjects += thisSize;
|
|
|
|
aSizes.mDynamicUnsharedBuffers +=
|
2018-03-07 03:57:54 +03:00
|
|
|
GetStringBuffer()->SizeOfIncludingThisIfUnshared(aMallocSizeOf);
|
2017-08-24 04:10:04 +03:00
|
|
|
}
|
2016-04-06 05:01:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
2010-03-08 18:45:00 +03:00
|
|
|
struct AtomTableKey
|
|
|
|
{
|
2014-01-04 19:02:17 +04:00
|
|
|
AtomTableKey(const char16_t* aUTF16String, uint32_t aLength,
|
2014-07-31 10:22:40 +04:00
|
|
|
uint32_t* aHashOut)
|
2014-07-09 19:15:21 +04:00
|
|
|
: mUTF16String(aUTF16String)
|
|
|
|
, mUTF8String(nullptr)
|
|
|
|
, mLength(aLength)
|
2005-11-04 22:52:18 +03:00
|
|
|
{
|
2014-07-31 10:22:40 +04:00
|
|
|
mHash = HashString(mUTF16String, mLength);
|
|
|
|
*aHashOut = mHash;
|
2005-11-04 22:52:18 +03:00
|
|
|
}
|
|
|
|
|
2014-07-31 10:22:40 +04:00
|
|
|
AtomTableKey(const char* aUTF8String, uint32_t aLength, uint32_t* aHashOut)
|
2014-07-09 19:15:21 +04:00
|
|
|
: mUTF16String(nullptr)
|
|
|
|
, mUTF8String(aUTF8String)
|
|
|
|
, mLength(aLength)
|
2005-11-04 22:52:18 +03:00
|
|
|
{
|
2014-07-31 10:22:40 +04:00
|
|
|
bool err;
|
|
|
|
mHash = HashUTF8AsUTF16(mUTF8String, mLength, &err);
|
|
|
|
if (err) {
|
|
|
|
mUTF8String = nullptr;
|
|
|
|
mLength = 0;
|
|
|
|
mHash = 0;
|
2013-11-09 03:29:09 +04:00
|
|
|
}
|
2014-07-31 10:22:40 +04:00
|
|
|
*aHashOut = mHash;
|
2005-11-04 22:52:18 +03:00
|
|
|
}
|
|
|
|
|
2014-01-04 19:02:17 +04:00
|
|
|
const char16_t* mUTF16String;
|
2010-03-08 18:45:00 +03:00
|
|
|
const char* mUTF8String;
|
2012-08-22 19:56:38 +04:00
|
|
|
uint32_t mLength;
|
2013-11-09 03:29:09 +04:00
|
|
|
uint32_t mHash;
|
2003-03-25 21:55:59 +03:00
|
|
|
};
|
|
|
|
|
2016-04-06 05:01:24 +03:00
|
|
|
struct AtomTableEntry : public PLDHashEntryHdr
|
|
|
|
{
|
2017-09-21 05:36:53 +03:00
|
|
|
// These references are either to dynamic atoms, in which case they are
|
|
|
|
// non-owning, or they are to static atoms, which aren't really refcounted.
|
2016-04-06 05:01:24 +03:00
|
|
|
// See the comment at the top of this file for more details.
|
2017-09-21 05:36:53 +03:00
|
|
|
nsAtom* MOZ_NON_OWNING_REF mAtom;
|
2016-04-06 05:01:24 +03:00
|
|
|
};
|
|
|
|
|
2018-02-24 02:53:17 +03:00
|
|
|
#define RECENTLY_USED_MAIN_THREAD_ATOM_CACHE_SIZE 31
|
|
|
|
static nsAtom*
|
|
|
|
sRecentlyUsedMainThreadAtoms[RECENTLY_USED_MAIN_THREAD_ATOM_CACHE_SIZE] = {};
|
|
|
|
|
2018-02-23 01:02:48 +03:00
|
|
|
// In order to reduce locking contention for concurrent atomization, we segment
|
|
|
|
// the atom table into N subtables, each with a separate lock. If the hash
|
|
|
|
// values we use to select the subtable are evenly distributed, this reduces the
|
|
|
|
// probability of contention by a factor of N. See bug 1440824.
|
|
|
|
//
|
2018-02-28 02:57:18 +03:00
|
|
|
// NB: This is somewhat similar to the technique used by Java's
|
|
|
|
// ConcurrentHashTable.
|
2018-02-23 01:02:48 +03:00
|
|
|
class nsAtomSubTable
|
|
|
|
{
|
|
|
|
friend class nsAtomTable;
|
|
|
|
Mutex mLock;
|
|
|
|
PLDHashTable mTable;
|
|
|
|
nsAtomSubTable();
|
|
|
|
void GCLocked(GCKind aKind);
|
2018-02-28 03:05:07 +03:00
|
|
|
void AddSizeOfExcludingThisLocked(MallocSizeOf aMallocSizeOf,
|
|
|
|
AtomsSizes& aSizes);
|
2018-02-23 01:02:48 +03:00
|
|
|
|
2018-02-27 23:34:12 +03:00
|
|
|
AtomTableEntry* Search(AtomTableKey& aKey)
|
|
|
|
{
|
|
|
|
mLock.AssertCurrentThreadOwns();
|
|
|
|
return static_cast<AtomTableEntry*>(mTable.Search(&aKey));
|
|
|
|
}
|
|
|
|
|
2018-02-23 01:02:48 +03:00
|
|
|
AtomTableEntry* Add(AtomTableKey& aKey)
|
|
|
|
{
|
|
|
|
mLock.AssertCurrentThreadOwns();
|
|
|
|
return static_cast<AtomTableEntry*>(mTable.Add(&aKey)); // Infallible
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// The outer atom table, which coordinates access to the inner array of
|
|
|
|
// subtables.
|
|
|
|
class nsAtomTable
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
nsAtomSubTable& SelectSubTable(AtomTableKey& aKey);
|
2018-02-28 03:05:07 +03:00
|
|
|
void AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, AtomsSizes& aSizes);
|
2018-02-23 01:02:48 +03:00
|
|
|
void GC(GCKind aKind);
|
|
|
|
already_AddRefed<nsAtom> Atomize(const nsAString& aUTF16String);
|
|
|
|
already_AddRefed<nsAtom> Atomize(const nsACString& aUTF8String);
|
|
|
|
already_AddRefed<nsAtom> AtomizeMainThread(const nsAString& aUTF16String);
|
2018-02-27 23:34:12 +03:00
|
|
|
nsStaticAtom* GetStaticAtom(const nsAString& aUTF16String);
|
2018-02-23 01:02:48 +03:00
|
|
|
void RegisterStaticAtoms(const nsStaticAtomSetup* aSetup, uint32_t aCount);
|
|
|
|
|
|
|
|
// The result of this function may be imprecise if other threads are operating
|
|
|
|
// on atoms concurrently. It's also slow, since it triggers a GC before
|
|
|
|
// counting.
|
|
|
|
size_t RacySlowCount();
|
|
|
|
|
|
|
|
// This hash table op is a static member of this class so that it can take
|
|
|
|
// advantage of |friend| declarations.
|
2018-02-28 02:57:18 +03:00
|
|
|
static void AtomTableClearEntry(PLDHashTable* aTable,
|
|
|
|
PLDHashEntryHdr* aEntry);
|
2018-02-23 01:02:48 +03:00
|
|
|
|
2018-02-23 02:59:00 +03:00
|
|
|
// We achieve measurable reduction in locking contention in parallel CSS
|
|
|
|
// parsing by increasing the number of subtables up to 128. This has been
|
|
|
|
// measured to have neglible impact on the performance of initialization, GC,
|
|
|
|
// and shutdown.
|
|
|
|
//
|
2018-02-28 02:57:18 +03:00
|
|
|
// Another important consideration is memory, since we're adding fixed
|
|
|
|
// overhead per content process, which we try to avoid. Measuring a
|
|
|
|
// mostly-empty page [1] with various numbers of subtables, we get the
|
|
|
|
// following deep sizes for the atom table:
|
2018-02-23 02:59:00 +03:00
|
|
|
// 1 subtable: 278K
|
|
|
|
// 8 subtables: 279K
|
|
|
|
// 16 subtables: 282K
|
|
|
|
// 64 subtables: 286K
|
|
|
|
// 128 subtables: 290K
|
|
|
|
//
|
|
|
|
// So 128 subtables costs us 12K relative to a single table, and 4K relative
|
|
|
|
// to 64 subtables. Conversely, measuring parallel (6 thread) CSS parsing on
|
2018-02-28 02:57:18 +03:00
|
|
|
// tp6-facebook, a single table provides ~150ms of locking overhead per
|
|
|
|
// thread, 64 subtables provides ~2-3ms of overhead, and 128 subtables
|
|
|
|
// provides <1ms. And so while either 64 or 128 subtables would probably be
|
|
|
|
// acceptable, achieving a measurable reduction in contention for 4k of fixed
|
|
|
|
// memory overhead is probably worth it.
|
2018-02-23 02:59:00 +03:00
|
|
|
//
|
|
|
|
// [1] The numbers will look different for content processes with complex
|
|
|
|
// pages loaded, but in those cases the actual atoms will dominate memory
|
|
|
|
// usage and the overhead of extra tables will be negligible. We're mostly
|
|
|
|
// interested in the fixed cost for nearly-empty content processes.
|
|
|
|
const static size_t kNumSubTables = 128; // Must be power of two.
|
2018-02-23 01:02:48 +03:00
|
|
|
|
|
|
|
private:
|
|
|
|
nsAtomSubTable mSubTables[kNumSubTables];
|
|
|
|
};
|
|
|
|
|
|
|
|
// Static singleton instance for the atom table.
|
|
|
|
static nsAtomTable* gAtomTable;
|
|
|
|
|
2008-10-10 19:04:34 +04:00
|
|
|
static PLDHashNumber
|
2016-03-16 07:33:44 +03:00
|
|
|
AtomTableGetHash(const void* aKey)
|
2005-11-04 22:52:18 +03:00
|
|
|
{
|
2014-07-09 19:15:21 +04:00
|
|
|
const AtomTableKey* k = static_cast<const AtomTableKey*>(aKey);
|
2013-11-09 03:29:09 +04:00
|
|
|
return k->mHash;
|
2001-10-21 03:19:07 +04:00
|
|
|
}
|
|
|
|
|
2011-09-29 10:19:26 +04:00
|
|
|
static bool
|
2016-03-16 07:33:44 +03:00
|
|
|
AtomTableMatchKey(const PLDHashEntryHdr* aEntry, const void* aKey)
|
2001-10-21 03:19:07 +04:00
|
|
|
{
|
2014-07-09 19:15:21 +04:00
|
|
|
const AtomTableEntry* he = static_cast<const AtomTableEntry*>(aEntry);
|
|
|
|
const AtomTableKey* k = static_cast<const AtomTableKey*>(aKey);
|
2005-11-04 22:52:18 +03:00
|
|
|
|
2010-03-08 18:45:00 +03:00
|
|
|
if (k->mUTF8String) {
|
2005-11-04 22:52:18 +03:00
|
|
|
return
|
2010-03-08 18:45:00 +03:00
|
|
|
CompareUTF8toUTF16(nsDependentCSubstring(k->mUTF8String,
|
|
|
|
k->mUTF8String + k->mLength),
|
|
|
|
nsDependentAtomString(he->mAtom)) == 0;
|
2007-07-12 00:46:43 +04:00
|
|
|
}
|
|
|
|
|
2017-04-02 22:40:06 +03:00
|
|
|
return he->mAtom->Equals(k->mUTF16String, k->mLength);
|
2001-10-21 03:19:07 +04:00
|
|
|
}
|
|
|
|
|
2017-09-25 09:30:32 +03:00
|
|
|
void
|
2018-02-23 01:02:48 +03:00
|
|
|
nsAtomTable::AtomTableClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
|
2001-10-21 03:19:07 +04:00
|
|
|
{
|
Bug 1261735 (part 1) - Overhaul the atom implementation. r=froydnj,erahm.
This patch changes things so that dynamic atoms and static atoms have distinct
implementations. This is a step towards allowing dynamic atoms and static atoms
to have different layouts in memory, which will allow static atoms to be
represented more compactly.
Specifically, the patch does the following.
- It renames AtomImpl as DynamicAtom and PermanentAtomImpl as StaticAtom, and
the latter is no longer a subclass of the former. This required duplicating
some methods from the former into the latter: ScriptableToString(),
ToUTF8String(), ScriptableEquals(), IsStaticAtom(). (This duplication will
disappear in the future if the representations of dynamic atoms and static
atoms diverge. Indeed, SizeOfIncludingThis() is already different in the two
classes.)
- It replaces all mentions of "permanent"/"non-permanent" atoms with
"static"/"dynamic".
- In ~DynamicAtom() it removes the check that causes gAtomTable to be deleted
when it becomes empty. This will only happen at shutdown and so doesn't seem
useful.
- It documents better various things, especially the basics of the
dynamic/static split, the transmutation of dynamic atoms to static atoms, and
the details of the SizeOf functions.
--HG--
extra : rebase_source : dbf903012e70ebf1a43de1e1088db1bc1b8dd4f4
2016-04-01 03:18:06 +03:00
|
|
|
auto entry = static_cast<AtomTableEntry*>(aEntry);
|
2017-09-21 05:36:53 +03:00
|
|
|
nsAtom* atom = entry->mAtom;
|
2018-03-07 07:17:34 +03:00
|
|
|
if (atom->IsStatic()) {
|
2017-09-21 05:36:53 +03:00
|
|
|
// This case -- when the entry being cleared holds a static atom -- only
|
|
|
|
// occurs when gAtomTable is destroyed, whereupon all static atoms within it
|
2017-08-24 04:10:04 +03:00
|
|
|
// must be explicitly deleted.
|
|
|
|
delete atom;
|
2003-03-25 21:55:59 +03:00
|
|
|
}
|
2001-10-21 03:19:07 +04:00
|
|
|
}
|
|
|
|
|
2015-02-11 20:46:40 +03:00
|
|
|
static void
|
|
|
|
AtomTableInitEntry(PLDHashEntryHdr* aEntry, const void* aKey)
|
2007-07-12 00:46:43 +04:00
|
|
|
{
|
2014-07-09 19:15:21 +04:00
|
|
|
static_cast<AtomTableEntry*>(aEntry)->mAtom = nullptr;
|
2007-07-12 00:46:43 +04:00
|
|
|
}
|
|
|
|
|
2003-04-04 19:10:37 +04:00
|
|
|
static const PLDHashTableOps AtomTableOps = {
|
2005-11-04 22:52:18 +03:00
|
|
|
AtomTableGetHash,
|
2001-10-21 03:19:07 +04:00
|
|
|
AtomTableMatchKey,
|
2015-09-15 00:23:47 +03:00
|
|
|
PLDHashTable::MoveEntryStub,
|
2018-02-23 01:02:48 +03:00
|
|
|
nsAtomTable::AtomTableClearEntry,
|
2007-07-12 00:46:43 +04:00
|
|
|
AtomTableInitEntry
|
2001-10-21 03:19:07 +04:00
|
|
|
};
|
|
|
|
|
2018-02-24 02:53:17 +03:00
|
|
|
// The atom table very quickly gets 10,000+ entries in it (or even 100,000+).
|
2018-02-23 01:02:48 +03:00
|
|
|
// But choosing the best initial subtable length has some subtleties: we add
|
|
|
|
// ~2700 static atoms at start-up, and then we start adding and removing
|
|
|
|
// dynamic atoms. If we make the tables too big to start with, when the first
|
|
|
|
// dynamic atom gets removed from a given table the load factor will be < 25%
|
|
|
|
// and we will shrink it.
|
2018-02-24 02:53:17 +03:00
|
|
|
//
|
2018-02-23 01:02:48 +03:00
|
|
|
// So we first make the simplifying assumption that the atoms are more or less
|
|
|
|
// evenly-distributed across the subtables (which is the case empirically).
|
|
|
|
// Then, we take the total atom count when the first dynamic atom is removed
|
|
|
|
// (~2700), divide that across the N subtables, and the largest capacity that
|
|
|
|
// will allow each subtable to be > 25% full with that count.
|
|
|
|
//
|
|
|
|
// So want an initial subtable capacity less than (2700 / N) * 4 = 10800 / N.
|
|
|
|
// Rounding down to the nearest power of two gives us 8192 / N. Since the
|
|
|
|
// capacity is double the initial length, we end up with (4096 / N) per subtable.
|
|
|
|
#define INITIAL_SUBTABLE_LENGTH (4096 / nsAtomTable::kNumSubTables)
|
2017-04-03 23:13:18 +03:00
|
|
|
|
2018-02-23 01:02:48 +03:00
|
|
|
nsAtomSubTable&
|
|
|
|
nsAtomTable::SelectSubTable(AtomTableKey& aKey)
|
|
|
|
{
|
|
|
|
// There are a few considerations around how we select subtables.
|
|
|
|
//
|
|
|
|
// First, we want entries to be evenly distributed across the subtables. This
|
|
|
|
// can be achieved by using any bits in the hash key, assuming the key itself
|
|
|
|
// is evenly-distributed. Empirical measurements indicate that this method
|
|
|
|
// produces a roughly-even distribution across subtables.
|
|
|
|
//
|
|
|
|
// Second, we want to use the hash bits that are least likely to influence an
|
|
|
|
// entry's position within the subtable. If we used the exact same bits used
|
|
|
|
// by the subtables, then each subtable would compute the same position for
|
|
|
|
// every entry it observes, leading to pessimal performance. In this case,
|
|
|
|
// we're using PLDHashTable, whose primary hash function uses the N leftmost
|
|
|
|
// bits of the hash value (where N is the log2 capacity of the table). This
|
|
|
|
// means we should prefer the rightmost bits here.
|
|
|
|
//
|
|
|
|
// Note that the below is equivalent to mHash % kNumSubTables, a replacement
|
|
|
|
// which an optimizing compiler should make, but let's avoid any doubt.
|
|
|
|
static_assert((kNumSubTables & (kNumSubTables - 1)) == 0, "must be power of two");
|
|
|
|
return mSubTables[aKey.mHash & (kNumSubTables - 1)];
|
|
|
|
}
|
|
|
|
|
2018-02-28 03:05:07 +03:00
|
|
|
void
|
|
|
|
nsAtomTable::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
|
|
|
|
AtomsSizes& aSizes)
|
2018-02-23 01:02:48 +03:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
2018-02-28 03:05:07 +03:00
|
|
|
aSizes.mTable += aMallocSizeOf(this);
|
2018-02-23 01:02:48 +03:00
|
|
|
for (auto& table : mSubTables) {
|
|
|
|
MutexAutoLock lock(table.mLock);
|
2018-02-28 03:05:07 +03:00
|
|
|
table.AddSizeOfExcludingThisLocked(aMallocSizeOf, aSizes);
|
2018-02-23 01:02:48 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void nsAtomTable::GC(GCKind aKind)
|
2017-01-26 23:43:38 +03:00
|
|
|
{
|
2017-04-03 23:13:18 +03:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
for (uint32_t i = 0; i < RECENTLY_USED_MAIN_THREAD_ATOM_CACHE_SIZE; ++i) {
|
|
|
|
sRecentlyUsedMainThreadAtoms[i] = nullptr;
|
|
|
|
}
|
|
|
|
|
2018-02-23 01:02:48 +03:00
|
|
|
// Note that this is effectively an incremental GC, since only one subtable
|
|
|
|
// is locked at a time.
|
|
|
|
for (auto& table: mSubTables) {
|
|
|
|
MutexAutoLock lock(table.mLock);
|
|
|
|
table.GCLocked(aKind);
|
|
|
|
}
|
|
|
|
|
|
|
|
// We would like to assert that gUnusedAtomCount matches the number of atoms
|
|
|
|
// we found in the table which we removed. However, there are two problems
|
|
|
|
// with this:
|
|
|
|
// * We have multiple subtables, each with their own lock. For optimal
|
|
|
|
// performance we only want to hold one lock at a time, but this means
|
|
|
|
// that atoms can be added and removed between GC slices.
|
|
|
|
// * Even if we held all the locks and performed all GC slices atomically,
|
|
|
|
// the locks are not acquired for AddRef() and Release() calls. This means
|
|
|
|
// we might see a gUnusedAtomCount value in between, say, AddRef()
|
|
|
|
// incrementing mRefCnt and it decrementing gUnusedAtomCount.
|
|
|
|
//
|
|
|
|
// So, we don't bother asserting that there are no unused atoms at the end of
|
|
|
|
// a regular GC. But we can (and do) assert this just after the last GC at
|
|
|
|
// shutdown.
|
|
|
|
//
|
|
|
|
// Note that, barring refcounting bugs, an atom can only go from a zero
|
|
|
|
// refcount to a non-zero refcount while the atom table lock is held, so
|
|
|
|
// so we won't try to resurrect a zero refcount atom while trying to delete
|
|
|
|
// it.
|
|
|
|
|
|
|
|
MOZ_ASSERT_IF(aKind == GCKind::Shutdown, gUnusedAtomCount == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
nsAtomTable::RacySlowCount()
|
|
|
|
{
|
|
|
|
// Trigger a GC so that the result is deterministic modulo other threads.
|
|
|
|
GC(GCKind::RegularOperation);
|
|
|
|
size_t count = 0;
|
|
|
|
for (auto& table: mSubTables) {
|
|
|
|
MutexAutoLock lock(table.mLock);
|
|
|
|
count += table.mTable.EntryCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsAtomSubTable::nsAtomSubTable()
|
|
|
|
: mLock("Atom Sub-Table Lock")
|
|
|
|
, mTable(&AtomTableOps, sizeof(AtomTableEntry), INITIAL_SUBTABLE_LENGTH)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsAtomSubTable::GCLocked(GCKind aKind)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
mLock.AssertCurrentThreadOwns();
|
|
|
|
|
2018-02-28 02:57:18 +03:00
|
|
|
int32_t removedCount = 0; // A non-atomic temporary for cheaper increments.
|
2017-02-01 19:47:00 +03:00
|
|
|
nsAutoCString nonZeroRefcountAtoms;
|
|
|
|
uint32_t nonZeroRefcountAtomsCount = 0;
|
2018-02-23 01:02:48 +03:00
|
|
|
for (auto i = mTable.Iter(); !i.Done(); i.Next()) {
|
2016-05-27 03:55:53 +03:00
|
|
|
auto entry = static_cast<AtomTableEntry*>(i.Get());
|
2018-03-07 07:17:34 +03:00
|
|
|
if (entry->mAtom->IsStatic()) {
|
2017-01-26 23:43:38 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-09-21 05:36:53 +03:00
|
|
|
nsAtom* atom = entry->mAtom;
|
2018-03-07 07:17:34 +03:00
|
|
|
MOZ_ASSERT(!atom->IsHTML5());
|
|
|
|
if (atom->IsDynamic() && nsDynamicAtom::As(atom)->mRefCnt == 0) {
|
2017-01-26 23:43:38 +03:00
|
|
|
i.Remove();
|
|
|
|
delete atom;
|
|
|
|
++removedCount;
|
2017-03-06 22:50:30 +03:00
|
|
|
}
|
|
|
|
#ifdef NS_FREE_PERMANENT_DATA
|
|
|
|
else if (aKind == GCKind::Shutdown && PR_GetEnv("XPCOM_MEM_BLOAT_LOG")) {
|
2018-02-28 02:57:18 +03:00
|
|
|
// Only report leaking atoms in leak-checking builds in a run where we
|
|
|
|
// are checking for leaks, during shutdown. If something is anomalous,
|
|
|
|
// then we'll assert later in this function.
|
2017-01-26 23:43:38 +03:00
|
|
|
nsAutoCString name;
|
|
|
|
atom->ToUTF8String(name);
|
2017-02-01 19:47:00 +03:00
|
|
|
if (nonZeroRefcountAtomsCount == 0) {
|
|
|
|
nonZeroRefcountAtoms = name;
|
|
|
|
} else if (nonZeroRefcountAtomsCount < 20) {
|
|
|
|
nonZeroRefcountAtoms += NS_LITERAL_CSTRING(",") + name;
|
|
|
|
} else if (nonZeroRefcountAtomsCount == 20) {
|
|
|
|
nonZeroRefcountAtoms += NS_LITERAL_CSTRING(",...");
|
|
|
|
}
|
|
|
|
nonZeroRefcountAtomsCount++;
|
2016-05-27 03:55:53 +03:00
|
|
|
}
|
2017-03-06 22:50:30 +03:00
|
|
|
#endif
|
|
|
|
|
2016-05-27 03:55:53 +03:00
|
|
|
}
|
2017-02-01 19:47:00 +03:00
|
|
|
if (nonZeroRefcountAtomsCount) {
|
|
|
|
nsPrintfCString msg("%d dynamic atom(s) with non-zero refcount: %s",
|
|
|
|
nonZeroRefcountAtomsCount, nonZeroRefcountAtoms.get());
|
|
|
|
NS_ASSERTION(nonZeroRefcountAtomsCount == 0, msg.get());
|
|
|
|
}
|
2016-05-27 03:55:53 +03:00
|
|
|
|
2017-01-26 23:43:38 +03:00
|
|
|
gUnusedAtomCount -= removedCount;
|
2016-05-27 03:55:53 +03:00
|
|
|
}
|
2016-04-06 05:01:24 +03:00
|
|
|
|
2017-09-25 09:30:32 +03:00
|
|
|
static void
|
|
|
|
GCAtomTable()
|
|
|
|
{
|
2018-02-23 01:02:48 +03:00
|
|
|
MOZ_ASSERT(gAtomTable);
|
2017-09-25 09:30:32 +03:00
|
|
|
if (NS_IsMainThread()) {
|
2018-02-23 01:02:48 +03:00
|
|
|
gAtomTable->GC(GCKind::RegularOperation);
|
2017-09-25 09:30:32 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-05 05:54:06 +03:00
|
|
|
MOZ_ALWAYS_INLINE MozExternalRefCountType
|
|
|
|
nsDynamicAtom::AddRef()
|
2016-05-27 03:55:53 +03:00
|
|
|
{
|
2017-09-26 01:33:21 +03:00
|
|
|
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
|
2016-05-27 03:55:53 +03:00
|
|
|
nsrefcnt count = ++mRefCnt;
|
|
|
|
if (count == 1) {
|
|
|
|
gUnusedAtomCount--;
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2018-03-05 05:54:06 +03:00
|
|
|
MOZ_ALWAYS_INLINE MozExternalRefCountType
|
|
|
|
nsDynamicAtom::Release()
|
2016-05-27 03:55:53 +03:00
|
|
|
{
|
2017-09-26 01:33:21 +03:00
|
|
|
#ifdef DEBUG
|
|
|
|
// We set a lower GC threshold for atoms in debug builds so that we exercise
|
|
|
|
// the GC machinery more often.
|
|
|
|
static const int32_t kAtomGCThreshold = 20;
|
|
|
|
#else
|
|
|
|
static const int32_t kAtomGCThreshold = 10000;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
|
2016-05-27 03:55:53 +03:00
|
|
|
nsrefcnt count = --mRefCnt;
|
|
|
|
if (count == 0) {
|
|
|
|
if (++gUnusedAtomCount >= kAtomGCThreshold) {
|
|
|
|
GCAtomTable();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2018-03-05 05:54:06 +03:00
|
|
|
MozExternalRefCountType
|
|
|
|
nsAtom::AddRef()
|
|
|
|
{
|
2018-03-07 07:17:34 +03:00
|
|
|
MOZ_ASSERT(!IsHTML5(), "Attempt to AddRef an HTML5 atom");
|
2018-03-05 05:54:06 +03:00
|
|
|
|
2018-03-07 07:17:34 +03:00
|
|
|
return IsStatic() ? 2 : nsDynamicAtom::As(this)->AddRef();
|
2018-03-05 05:54:06 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
MozExternalRefCountType
|
|
|
|
nsAtom::Release()
|
|
|
|
{
|
2018-03-07 07:17:34 +03:00
|
|
|
MOZ_ASSERT(!IsHTML5(), "Attempt to Release an HTML5 atom");
|
2018-03-05 05:54:06 +03:00
|
|
|
|
2018-03-07 07:17:34 +03:00
|
|
|
return IsStatic() ? 1 : nsDynamicAtom::As(this)->Release();
|
2018-03-05 05:54:06 +03:00
|
|
|
}
|
|
|
|
|
2016-04-06 05:01:24 +03:00
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
2018-02-27 23:34:12 +03:00
|
|
|
// Have the static atoms been inserted into the table?
|
|
|
|
static bool gStaticAtomsDone = false;
|
2016-04-06 05:01:24 +03:00
|
|
|
|
2005-11-04 22:52:18 +03:00
|
|
|
void
|
2016-05-26 04:45:17 +03:00
|
|
|
NS_InitAtomTable()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!gAtomTable);
|
2018-02-23 01:02:48 +03:00
|
|
|
gAtomTable = new nsAtomTable();
|
2017-02-25 01:02:44 +03:00
|
|
|
|
2018-03-14 03:11:02 +03:00
|
|
|
// Bug 1340710 has caused us to use an empty atom at arbitrary times after
|
|
|
|
// startup. If we end up creating one before nsGkAtoms::_empty is registered,
|
|
|
|
// we get an assertion about transmuting a dynamic atom into a static atom.
|
|
|
|
// In order to avoid that, we register nsGkAtoms immediately after creating
|
|
|
|
// the atom table to guarantee that the empty string atom will always be
|
|
|
|
// static.
|
|
|
|
nsGkAtoms::AddRefAtoms();
|
2016-05-26 04:45:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
NS_ShutdownAtomTable()
|
1999-10-01 01:40:00 +04:00
|
|
|
{
|
2018-02-23 01:02:48 +03:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_ASSERT(gAtomTable);
|
2009-09-18 19:13:10 +04:00
|
|
|
|
2016-05-27 03:55:53 +03:00
|
|
|
#ifdef NS_FREE_PERMANENT_DATA
|
|
|
|
// Do a final GC to satisfy leak checking. We skip this step in release
|
|
|
|
// builds.
|
2018-02-23 01:02:48 +03:00
|
|
|
gAtomTable->GC(GCKind::Shutdown);
|
2016-05-27 03:55:53 +03:00
|
|
|
#endif
|
|
|
|
|
2016-05-26 04:45:17 +03:00
|
|
|
delete gAtomTable;
|
|
|
|
gAtomTable = nullptr;
|
2010-03-08 18:44:59 +03:00
|
|
|
}
|
2003-03-25 21:55:59 +03:00
|
|
|
|
2018-02-28 03:05:07 +03:00
|
|
|
void
|
|
|
|
NS_AddSizeOfAtoms(MallocSizeOf aMallocSizeOf, AtomsSizes& aSizes)
|
2014-07-31 10:06:57 +04:00
|
|
|
{
|
2018-02-23 01:02:48 +03:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_ASSERT(gAtomTable);
|
2018-02-28 03:05:07 +03:00
|
|
|
return gAtomTable->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
|
2011-12-23 05:24:43 +04:00
|
|
|
}
|
|
|
|
|
2018-02-28 03:05:07 +03:00
|
|
|
void
|
|
|
|
nsAtomSubTable::AddSizeOfExcludingThisLocked(MallocSizeOf aMallocSizeOf,
|
|
|
|
AtomsSizes& aSizes)
|
2005-11-04 22:52:18 +03:00
|
|
|
{
|
2018-02-23 01:02:48 +03:00
|
|
|
mLock.AssertCurrentThreadOwns();
|
2018-02-28 03:05:07 +03:00
|
|
|
aSizes.mTable += mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
2018-02-23 01:02:48 +03:00
|
|
|
for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
|
|
|
|
auto entry = static_cast<AtomTableEntry*>(iter.Get());
|
2018-02-28 03:05:07 +03:00
|
|
|
entry->mAtom->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
|
2018-02-23 01:02:48 +03:00
|
|
|
}
|
1998-04-14 00:24:54 +04:00
|
|
|
}
|
|
|
|
|
2016-03-18 11:03:43 +03:00
|
|
|
void
|
2018-02-23 01:02:48 +03:00
|
|
|
nsAtomTable::RegisterStaticAtoms(const nsStaticAtomSetup* aSetup,
|
|
|
|
uint32_t aCount)
|
2003-03-25 21:55:59 +03:00
|
|
|
{
|
2018-02-23 01:02:48 +03:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
2018-02-27 23:34:12 +03:00
|
|
|
MOZ_RELEASE_ASSERT(!gStaticAtomsDone, "Static atom insertion is finished!");
|
2014-07-09 19:15:21 +04:00
|
|
|
|
2017-10-20 06:04:47 +03:00
|
|
|
for (uint32_t i = 0; i < aCount; ++i) {
|
|
|
|
const char16_t* string = aSetup[i].mString;
|
2018-03-08 03:42:06 +03:00
|
|
|
nsStaticAtom** atomp = aSetup[i].mAtomp;
|
Bug 1261735 (part 1) - Overhaul the atom implementation. r=froydnj,erahm.
This patch changes things so that dynamic atoms and static atoms have distinct
implementations. This is a step towards allowing dynamic atoms and static atoms
to have different layouts in memory, which will allow static atoms to be
represented more compactly.
Specifically, the patch does the following.
- It renames AtomImpl as DynamicAtom and PermanentAtomImpl as StaticAtom, and
the latter is no longer a subclass of the former. This required duplicating
some methods from the former into the latter: ScriptableToString(),
ToUTF8String(), ScriptableEquals(), IsStaticAtom(). (This duplication will
disappear in the future if the representations of dynamic atoms and static
atoms diverge. Indeed, SizeOfIncludingThis() is already different in the two
classes.)
- It replaces all mentions of "permanent"/"non-permanent" atoms with
"static"/"dynamic".
- In ~DynamicAtom() it removes the check that causes gAtomTable to be deleted
when it becomes empty. This will only happen at shutdown and so doesn't seem
useful.
- It documents better various things, especially the basics of the
dynamic/static split, the transmutation of dynamic atoms to static atoms, and
the details of the SizeOf functions.
--HG--
extra : rebase_source : dbf903012e70ebf1a43de1e1088db1bc1b8dd4f4
2016-04-01 03:18:06 +03:00
|
|
|
|
2017-10-12 02:52:17 +03:00
|
|
|
MOZ_ASSERT(nsCRT::IsAscii(string));
|
2007-07-12 00:46:43 +04:00
|
|
|
|
2017-10-12 02:52:17 +03:00
|
|
|
uint32_t stringLen = NS_strlen(string);
|
2007-07-12 00:46:43 +04:00
|
|
|
|
2014-07-31 10:22:40 +04:00
|
|
|
uint32_t hash;
|
2018-02-23 01:02:48 +03:00
|
|
|
AtomTableKey key(string, stringLen, &hash);
|
|
|
|
nsAtomSubTable& table = SelectSubTable(key);
|
|
|
|
MutexAutoLock lock(table.mLock);
|
|
|
|
AtomTableEntry* he = table.Add(key);
|
2007-07-12 00:46:43 +04:00
|
|
|
|
2017-10-27 02:31:13 +03:00
|
|
|
nsStaticAtom* atom;
|
|
|
|
if (he->mAtom) {
|
2018-02-28 02:57:18 +03:00
|
|
|
// Disallow creating a dynamic atom, and then later, while the dynamic
|
|
|
|
// atom is still alive, registering that same atom as a static atom. It
|
|
|
|
// causes subtle bugs, and we're programming in C++ here, not Smalltalk.
|
2018-03-07 07:17:34 +03:00
|
|
|
if (!he->mAtom->IsStatic()) {
|
2017-01-26 23:43:38 +03:00
|
|
|
nsAutoCString name;
|
2017-10-27 02:31:13 +03:00
|
|
|
he->mAtom->ToUTF8String(name);
|
2017-02-21 20:02:03 +03:00
|
|
|
MOZ_CRASH_UNSAFE_PRINTF(
|
|
|
|
"Static atom registration for %s should be pushed back", name.get());
|
2003-03-25 21:55:59 +03:00
|
|
|
}
|
2017-10-27 02:31:13 +03:00
|
|
|
atom = static_cast<nsStaticAtom*>(he->mAtom);
|
2014-07-09 19:15:21 +04:00
|
|
|
} else {
|
2017-10-27 02:31:13 +03:00
|
|
|
atom = new nsStaticAtom(string, stringLen, hash);
|
2010-03-08 18:45:00 +03:00
|
|
|
he->mAtom = atom;
|
2014-07-31 10:22:42 +04:00
|
|
|
}
|
Bug 1261735 (part 1) - Overhaul the atom implementation. r=froydnj,erahm.
This patch changes things so that dynamic atoms and static atoms have distinct
implementations. This is a step towards allowing dynamic atoms and static atoms
to have different layouts in memory, which will allow static atoms to be
represented more compactly.
Specifically, the patch does the following.
- It renames AtomImpl as DynamicAtom and PermanentAtomImpl as StaticAtom, and
the latter is no longer a subclass of the former. This required duplicating
some methods from the former into the latter: ScriptableToString(),
ToUTF8String(), ScriptableEquals(), IsStaticAtom(). (This duplication will
disappear in the future if the representations of dynamic atoms and static
atoms diverge. Indeed, SizeOfIncludingThis() is already different in the two
classes.)
- It replaces all mentions of "permanent"/"non-permanent" atoms with
"static"/"dynamic".
- In ~DynamicAtom() it removes the check that causes gAtomTable to be deleted
when it becomes empty. This will only happen at shutdown and so doesn't seem
useful.
- It documents better various things, especially the basics of the
dynamic/static split, the transmutation of dynamic atoms to static atoms, and
the details of the SizeOf functions.
--HG--
extra : rebase_source : dbf903012e70ebf1a43de1e1088db1bc1b8dd4f4
2016-04-01 03:18:06 +03:00
|
|
|
*atomp = atom;
|
2003-03-25 21:55:59 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-25 09:30:32 +03:00
|
|
|
void
|
2017-10-20 06:04:47 +03:00
|
|
|
RegisterStaticAtoms(const nsStaticAtomSetup* aSetup, uint32_t aCount)
|
2017-09-25 09:30:32 +03:00
|
|
|
{
|
2018-02-23 01:02:48 +03:00
|
|
|
MOZ_ASSERT(gAtomTable);
|
|
|
|
gAtomTable->RegisterStaticAtoms(aSetup, aCount);
|
2017-09-25 09:30:32 +03:00
|
|
|
}
|
|
|
|
|
2017-09-26 01:33:21 +03:00
|
|
|
already_AddRefed<nsAtom>
|
2016-03-29 02:09:43 +03:00
|
|
|
NS_Atomize(const char* aUTF8String)
|
1998-04-14 00:24:54 +04:00
|
|
|
{
|
2018-02-23 01:02:48 +03:00
|
|
|
MOZ_ASSERT(gAtomTable);
|
|
|
|
return gAtomTable->Atomize(nsDependentCString(aUTF8String));
|
2007-07-12 00:46:43 +04:00
|
|
|
}
|
|
|
|
|
2017-09-26 01:33:21 +03:00
|
|
|
already_AddRefed<nsAtom>
|
2018-02-23 01:02:48 +03:00
|
|
|
nsAtomTable::Atomize(const nsACString& aUTF8String)
|
2007-07-12 00:46:43 +04:00
|
|
|
{
|
2014-07-31 10:22:40 +04:00
|
|
|
uint32_t hash;
|
2018-02-23 01:02:48 +03:00
|
|
|
AtomTableKey key(aUTF8String.Data(), aUTF8String.Length(), &hash);
|
|
|
|
nsAtomSubTable& table = SelectSubTable(key);
|
|
|
|
MutexAutoLock lock(table.mLock);
|
|
|
|
AtomTableEntry* he = table.Add(key);
|
2003-03-25 21:55:59 +03:00
|
|
|
|
2010-03-08 18:45:00 +03:00
|
|
|
if (he->mAtom) {
|
2017-09-26 01:33:21 +03:00
|
|
|
RefPtr<nsAtom> atom = he->mAtom;
|
2005-11-04 22:52:18 +03:00
|
|
|
|
2013-04-22 15:13:22 +04:00
|
|
|
return atom.forget();
|
2010-03-08 18:45:00 +03:00
|
|
|
}
|
2005-11-04 22:52:18 +03:00
|
|
|
|
2010-03-08 18:45:00 +03:00
|
|
|
// This results in an extra addref/release of the nsStringBuffer.
|
|
|
|
// Unfortunately there doesn't seem to be any APIs to avoid that.
|
2012-03-01 06:40:51 +04:00
|
|
|
// Actually, now there is, sort of: ForgetSharedBuffer.
|
2010-03-08 18:45:00 +03:00
|
|
|
nsString str;
|
|
|
|
CopyUTF8toUTF16(aUTF8String, str);
|
2018-03-05 05:54:06 +03:00
|
|
|
RefPtr<nsAtom> atom = dont_AddRef(new nsDynamicAtom(str, hash));
|
2010-03-08 18:45:00 +03:00
|
|
|
|
2010-03-08 18:45:00 +03:00
|
|
|
he->mAtom = atom;
|
2010-03-08 18:44:59 +03:00
|
|
|
|
2013-04-22 15:13:22 +04:00
|
|
|
return atom.forget();
|
2003-03-25 21:55:59 +03:00
|
|
|
}
|
|
|
|
|
2017-09-26 01:33:21 +03:00
|
|
|
already_AddRefed<nsAtom>
|
2017-09-25 09:30:32 +03:00
|
|
|
NS_Atomize(const nsACString& aUTF8String)
|
|
|
|
{
|
2018-02-23 01:02:48 +03:00
|
|
|
MOZ_ASSERT(gAtomTable);
|
|
|
|
return gAtomTable->Atomize(aUTF8String);
|
2017-09-25 09:30:32 +03:00
|
|
|
}
|
|
|
|
|
2017-09-26 01:33:21 +03:00
|
|
|
already_AddRefed<nsAtom>
|
2016-03-29 02:09:43 +03:00
|
|
|
NS_Atomize(const char16_t* aUTF16String)
|
2003-03-25 21:55:59 +03:00
|
|
|
{
|
2018-02-23 01:02:48 +03:00
|
|
|
MOZ_ASSERT(gAtomTable);
|
|
|
|
return gAtomTable->Atomize(nsDependentString(aUTF16String));
|
2005-11-04 22:52:18 +03:00
|
|
|
}
|
|
|
|
|
2017-09-26 01:33:21 +03:00
|
|
|
already_AddRefed<nsAtom>
|
2018-02-23 01:02:48 +03:00
|
|
|
nsAtomTable::Atomize(const nsAString& aUTF16String)
|
2005-11-04 22:52:18 +03:00
|
|
|
{
|
2014-07-31 10:22:40 +04:00
|
|
|
uint32_t hash;
|
2018-02-23 01:02:48 +03:00
|
|
|
AtomTableKey key(aUTF16String.Data(), aUTF16String.Length(), &hash);
|
|
|
|
nsAtomSubTable& table = SelectSubTable(key);
|
|
|
|
MutexAutoLock lock(table.mLock);
|
|
|
|
AtomTableEntry* he = table.Add(key);
|
2003-03-25 21:55:59 +03:00
|
|
|
|
2010-03-08 18:45:00 +03:00
|
|
|
if (he->mAtom) {
|
2017-09-26 01:33:21 +03:00
|
|
|
RefPtr<nsAtom> atom = he->mAtom;
|
2010-03-08 18:45:00 +03:00
|
|
|
|
2013-04-22 15:13:22 +04:00
|
|
|
return atom.forget();
|
2010-03-08 18:45:00 +03:00
|
|
|
}
|
2003-03-21 09:26:32 +03:00
|
|
|
|
2018-03-05 05:54:06 +03:00
|
|
|
RefPtr<nsAtom> atom = dont_AddRef(new nsDynamicAtom(aUTF16String, hash));
|
2010-03-08 18:45:00 +03:00
|
|
|
he->mAtom = atom;
|
2000-08-23 21:27:06 +04:00
|
|
|
|
2013-04-22 15:13:22 +04:00
|
|
|
return atom.forget();
|
2003-03-25 21:55:59 +03:00
|
|
|
}
|
2000-08-23 21:27:06 +04:00
|
|
|
|
2017-09-26 01:33:21 +03:00
|
|
|
already_AddRefed<nsAtom>
|
2017-09-25 09:30:32 +03:00
|
|
|
NS_Atomize(const nsAString& aUTF16String)
|
|
|
|
{
|
2018-02-23 01:02:48 +03:00
|
|
|
MOZ_ASSERT(gAtomTable);
|
|
|
|
return gAtomTable->Atomize(aUTF16String);
|
2017-09-25 09:30:32 +03:00
|
|
|
}
|
|
|
|
|
2017-09-26 01:33:21 +03:00
|
|
|
already_AddRefed<nsAtom>
|
2018-02-23 01:02:48 +03:00
|
|
|
nsAtomTable::AtomizeMainThread(const nsAString& aUTF16String)
|
2017-04-03 23:13:18 +03:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
2017-09-26 01:33:21 +03:00
|
|
|
RefPtr<nsAtom> retVal;
|
2017-04-03 23:13:18 +03:00
|
|
|
uint32_t hash;
|
|
|
|
AtomTableKey key(aUTF16String.Data(), aUTF16String.Length(), &hash);
|
|
|
|
uint32_t index = hash % RECENTLY_USED_MAIN_THREAD_ATOM_CACHE_SIZE;
|
2017-09-21 05:36:53 +03:00
|
|
|
nsAtom* atom = sRecentlyUsedMainThreadAtoms[index];
|
2017-04-03 23:13:18 +03:00
|
|
|
if (atom) {
|
|
|
|
uint32_t length = atom->GetLength();
|
|
|
|
if (length == key.mLength &&
|
|
|
|
(memcmp(atom->GetUTF16String(),
|
|
|
|
key.mUTF16String, length * sizeof(char16_t)) == 0)) {
|
|
|
|
retVal = atom;
|
|
|
|
return retVal.forget();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-23 01:02:48 +03:00
|
|
|
nsAtomSubTable& table = SelectSubTable(key);
|
|
|
|
MutexAutoLock lock(table.mLock);
|
|
|
|
AtomTableEntry* he = table.Add(key);
|
2017-04-03 23:13:18 +03:00
|
|
|
|
|
|
|
if (he->mAtom) {
|
|
|
|
retVal = he->mAtom;
|
|
|
|
} else {
|
2018-03-05 05:54:06 +03:00
|
|
|
RefPtr<nsAtom> newAtom = dont_AddRef(new nsDynamicAtom(aUTF16String, hash));
|
2017-08-24 04:10:04 +03:00
|
|
|
he->mAtom = newAtom;
|
|
|
|
retVal = newAtom.forget();
|
2017-04-03 23:13:18 +03:00
|
|
|
}
|
|
|
|
|
2017-08-24 04:10:04 +03:00
|
|
|
sRecentlyUsedMainThreadAtoms[index] = he->mAtom;
|
2017-04-03 23:13:18 +03:00
|
|
|
return retVal.forget();
|
|
|
|
}
|
|
|
|
|
2017-09-26 01:33:21 +03:00
|
|
|
already_AddRefed<nsAtom>
|
2017-09-25 09:30:32 +03:00
|
|
|
NS_AtomizeMainThread(const nsAString& aUTF16String)
|
|
|
|
{
|
2018-02-23 01:02:48 +03:00
|
|
|
MOZ_ASSERT(gAtomTable);
|
|
|
|
return gAtomTable->AtomizeMainThread(aUTF16String);
|
2017-09-25 09:30:32 +03:00
|
|
|
}
|
|
|
|
|
2011-08-18 17:46:39 +04:00
|
|
|
nsrefcnt
|
2005-11-04 22:52:18 +03:00
|
|
|
NS_GetNumberOfAtoms(void)
|
1998-04-14 00:24:54 +04:00
|
|
|
{
|
2018-02-23 01:02:48 +03:00
|
|
|
MOZ_ASSERT(gAtomTable);
|
|
|
|
return gAtomTable->RacySlowCount();
|
1998-04-14 00:24:54 +04:00
|
|
|
}
|
2003-03-25 21:55:59 +03:00
|
|
|
|
2017-09-06 08:06:16 +03:00
|
|
|
int32_t
|
2017-09-07 03:08:31 +03:00
|
|
|
NS_GetUnusedAtomCount(void)
|
|
|
|
{
|
|
|
|
return gUnusedAtomCount;
|
|
|
|
}
|
|
|
|
|
2017-10-27 02:31:13 +03:00
|
|
|
nsStaticAtom*
|
2009-09-18 19:13:10 +04:00
|
|
|
NS_GetStaticAtom(const nsAString& aUTF16String)
|
|
|
|
{
|
2018-02-27 23:34:12 +03:00
|
|
|
MOZ_ASSERT(gStaticAtomsDone, "Static atom setup not yet done.");
|
|
|
|
MOZ_ASSERT(gAtomTable);
|
|
|
|
return gAtomTable->GetStaticAtom(aUTF16String);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsStaticAtom*
|
|
|
|
nsAtomTable::GetStaticAtom(const nsAString& aUTF16String)
|
|
|
|
{
|
|
|
|
uint32_t hash;
|
|
|
|
AtomTableKey key(aUTF16String.Data(), aUTF16String.Length(), &hash);
|
|
|
|
nsAtomSubTable& table = SelectSubTable(key);
|
|
|
|
MutexAutoLock lock(table.mLock);
|
|
|
|
AtomTableEntry* he = table.Search(key);
|
2018-03-07 07:17:34 +03:00
|
|
|
return he && he->mAtom->IsStatic()
|
2018-02-27 23:34:12 +03:00
|
|
|
? static_cast<nsStaticAtom*>(he->mAtom)
|
|
|
|
: nullptr;
|
2009-09-18 19:13:10 +04:00
|
|
|
}
|
|
|
|
|
2011-08-18 17:46:39 +04:00
|
|
|
void
|
2018-02-27 23:34:12 +03:00
|
|
|
NS_SetStaticAtomsDone()
|
2009-09-18 19:13:10 +04:00
|
|
|
{
|
2018-02-23 01:02:48 +03:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
2018-02-27 23:34:12 +03:00
|
|
|
gStaticAtomsDone = true;
|
2009-09-18 19:13:10 +04:00
|
|
|
}
|