Closed Bug 1261735 Opened 3 years ago Closed 3 years ago

Overhaul the atom implementation

Categories

(Core :: XPCOM, defect)

defect
Not set

Tracking

()

RESOLVED FIXED
mozilla48
Tracking Status
firefox48 --- fixed

People

(Reporter: njn, Assigned: njn)

References

(Blocks 1 open bug)

Details

Attachments

(4 files, 1 obsolete file)

The implementation of AtomImpl and PermanentAtomImpl in nsAtomTable.cpp is a bit ugly and can be improved.
Apologies for the hard-to-review patch. I couldn't break this up into smaller
pieces.
Attachment #8737661 - Flags: review?(nfroyd)
Attachment #8737661 - Flags: review?(erahm)
Assignee: nobody → n.nethercote
Status: NEW → ASSIGNED
This avoids the need for some virtual function calls and also will help lead to
distinct representations for dynamic and static atoms.
Attachment #8737663 - Flags: review?(nfroyd)
Attachment #8737663 - Flags: review?(erahm)
Comment on attachment 8737662 [details] [diff] [review]
(part 2) - Inline some {Dynamic,Static}Atom methods

+      buf = nsStringBuffer::Alloc((mLength + 1) * sizeof(char16_t));
+      mString = static_cast<char16_t*>(buf->Data());

Hmm, isn't nsStringBuffer::Alloc fallible?
Blocks: 1261744
> +      buf = nsStringBuffer::Alloc((mLength + 1) * sizeof(char16_t));
> +      mString = static_cast<char16_t*>(buf->Data());
> 
> Hmm, isn't nsStringBuffer::Alloc fallible?

Indeed, it is. I checked all the calls to that function. There's another unchecked call in nsHtml5Atom's constructor. It's a pre-existing problem so I'll fix it separately. I've file bug 1261744.
Comment on attachment 8737661 [details] [diff] [review]
(part 1) - Overhaul the atom implementation

Review of attachment 8737661 [details] [diff] [review]:
-----------------------------------------------------------------

r=me with a couple changes noted below.  Thanks for the comments.

::: xpcom/ds/nsAtomTable.cpp
@@ +30,5 @@
> +//   points to. |gAtomTable| holds weak references to them DynamicAtoms, and
> +//   when they are destroyed (due to their refcount reaching zero) they remove
> +//   themselves from |gAtomTable|.
> +//
> +// - Static: the atom itself is heap allocated, but it points to a static

Nit: given "DynamicAtom" in the item above, maybe this should be "StaticAtom"?

@@ +139,5 @@
> +  StaticAtom(nsStringBuffer* aData, uint32_t aLength, PLDHashNumber aKeyHash);
> +
> +  // This constructor must only be used in conjunction with placement new on an
> +  // existing DynamicAtom, in order to transmute that DynamicAtom into a
> +  // StaticAtom. The constructor does three notable things.

o/~ sketchy o/~

@@ +266,5 @@
> +  auto entry = static_cast<AtomTableEntry*>(aEntry);
> +  nsIAtom* atom = entry->mAtom;
> +  if (atom->IsStaticAtom()) {
> +    // This case only occurs when gAtomTable is destroyed, whereupon all
> +    // StaticAtoms within it must be explicitly deleted. The cast is required

Just for clarification, this code *can* get called when we're deleting DynamicAtoms from the table, so we can't simply MOZ_ASSERT staticness here, correct?

@@ +561,2 @@
>  
> +    MOZ_ASSERT(nsCRT::IsAscii((char16_t*)stringBuffer->Data()));

As long as you're touching lines in here, it would be splendid to convert the C-style casts to something C++-y.

@@ +571,5 @@
>      if (atom) {
> +      if (!atom->IsStaticAtom()) {
> +        // A rare case: we're creating a StaticAtom but there is already a
> +        // DynamicAtom of the same name. Transmute the StaticAtom into a
> +        // DynamicAtom. See the comment on the relevant StaticAtom constructor

I think the transmutation goes the other way, from DynamicAtom into StaticAtom.

::: xpcom/ds/nsIAtom.idl
@@ +72,5 @@
> +  // Any subclass of nsIAtom for which this function might be called must
> +  // implement it.
> +  virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
> +    MOZ_CRASH("Should not call this function");
> +  };

You could make this [noscript,notxpcom] in the idl itself and then every subclass would have to implement it, which might be a little nicer.
Attachment #8737661 - Flags: review?(nfroyd) → review+
Attachment #8737663 - Flags: review?(nfroyd) → review+
Comment on attachment 8737661 [details] [diff] [review]
(part 1) - Overhaul the atom implementation

Review of attachment 8737661 [details] [diff] [review]:
-----------------------------------------------------------------

f+ for now, I have a few questions below.

::: xpcom/ds/nsAtomTable.cpp
@@ +82,3 @@
>    // really refcounted.  But since these entries live in a global hashtable,
>    // this reference is essentially owning.
>    nsIAtom* MOZ_OWNING_REF mAtom;

I wonder why this isn't just a StaticAtom* if it only points to objects of type StaticAtom.

@@ +118,3 @@
>  
> +#ifdef DEBUG
> +static bool EqualStrings(char16_t* aStr1, char16_t* aStr2)

Why are you reimplementing strcmp?

@@ +147,5 @@
> +  //   the refcount.
> +  // - Releases the existing heap-allocated string buffer (explicitly),
> +  //   replacing it with the static string buffer (which must contain identical
> +  //   chars).
> +  explicit StaticAtom(nsStringBuffer* aStaticBuffer)

It would make me a lot happier if this were private. I liked the previous "Promote" wording, maybe add a:

>  static StaticAtom* StaticAtom::PromoteToStatic(DynamicAtom*)

This could do the in place new, and even static_assert that DynamicAtom is in fact large enough to in place new a StaticAtom over it.

@@ +152,3 @@
>    {
> +    char16_t* staticString = static_cast<char16_t*>(aStaticBuffer->Data());
> +    MOZ_ASSERT(EqualStrings(staticString, mString));

|nsCRT::strcmp(...) == 0| would suffice here.

@@ +153,5 @@
> +    char16_t* staticString = static_cast<char16_t*>(aStaticBuffer->Data());
> +    MOZ_ASSERT(EqualStrings(staticString, mString));
> +    nsStringBuffer* dynamicBuffer = nsStringBuffer::FromData(mString);
> +    mString = staticString;
> +    dynamicBuffer->Release();

I think this is okay, but I'm just going to write it out as we've diverged somewhat from the previous implementation.

Previously the "promote" was simply an in-place new, we didn't swap the buffers. Now we do, so we need to manually release the buffer. Is the swapping strictly necessary?

This is also missing the |NS_BUILD_REFCNT_LOGGING| shenanigans from |PromoteToPermanent|, which to be honest I don't fully follow but I suppose it's there for a reason.
Attachment #8737661 - Flags: review?(erahm) → feedback+
> @@ +266,5 @@
> > +  auto entry = static_cast<AtomTableEntry*>(aEntry);
> > +  nsIAtom* atom = entry->mAtom;
> > +  if (atom->IsStaticAtom()) {
> > +    // This case only occurs when gAtomTable is destroyed, whereupon all
> > +    // StaticAtoms within it must be explicitly deleted. The cast is required
> 
> Just for clarification, this code *can* get called when we're deleting
> DynamicAtoms from the table, so we can't simply MOZ_ASSERT staticness here,
> correct?

If "this code" means "the function" -- yes. I put the comment inside the |if| to indicate that "This case" refers to static atoms only. I'll see if I can reword to make it clearer.
> ::: xpcom/ds/nsIAtom.idl
> @@ +72,5 @@
> > +  // Any subclass of nsIAtom for which this function might be called must
> > +  // implement it.
> > +  virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
> > +    MOZ_CRASH("Should not call this function");
> > +  };
> 
> You could make this [noscript,notxpcom] in the idl itself and then every
> subclass would have to implement it, which might be a little nicer.

I think I tried that and I had trouble with using mozilla::MallocSizeOf in IDL...
Attachment #8737662 - Flags: review?(erahm) → review+
Attachment #8737663 - Flags: review?(erahm) → review+
(In reply to Nicholas Nethercote [:njn] from comment #9)
> > ::: xpcom/ds/nsIAtom.idl
> > @@ +72,5 @@
> > > +  // Any subclass of nsIAtom for which this function might be called must
> > > +  // implement it.
> > > +  virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
> > > +    MOZ_CRASH("Should not call this function");
> > > +  };
> > 
> > You could make this [noscript,notxpcom] in the idl itself and then every
> > subclass would have to implement it, which might be a little nicer.
> 
> I think I tried that and I had trouble with using mozilla::MallocSizeOf in
> IDL...

Hm, looking at other instances something like:

native size_t(size_t)
native MallocSizeOf(mozilla::MallocSizeOf);

[noscript,notxpcom] size_t SizeOfIncludingThis(MallocSizeOf);

ought to work?

If some permutation on that doesn't work, how about just defining this as a pure virtual function, then?
> ::: xpcom/ds/nsAtomTable.cpp
> @@ +82,3 @@
> >    // really refcounted.  But since these entries live in a global hashtable,
> >    // this reference is essentially owning.
> >    nsIAtom* MOZ_OWNING_REF mAtom;
> 
> I wonder why this isn't just a StaticAtom* if it only points to objects of
> type StaticAtom.

Good question. Looks like it can be, but I have to move a bunch of code around so that things are declared in the appropriate order. I'll do that as a follow-up bug.


> Why are you reimplementing strcmp?

Ah... nsCRT::strcmp, that's where it is. I looked and couldn't find it and nobody on #developers was able to help me either. Thank you.


> @@ +147,5 @@
> > +  //   the refcount.
> > +  // - Releases the existing heap-allocated string buffer (explicitly),
> > +  //   replacing it with the static string buffer (which must contain identical
> > +  //   chars).
> > +  explicit StaticAtom(nsStringBuffer* aStaticBuffer)
> 
> It would make me a lot happier if this were private.
> I liked the previous "Promote" wording, maybe add a:
> 
> >  static StaticAtom* StaticAtom::PromoteToStatic(DynamicAtom*)
> 
> This could do the in place new,

I'll add something like that.


> and even static_assert that DynamicAtom is
> in fact large enough to in place new a StaticAtom over it.

Good idea.


> @@ +153,5 @@
> > +    char16_t* staticString = static_cast<char16_t*>(aStaticBuffer->Data());
> > +    MOZ_ASSERT(EqualStrings(staticString, mString));
> > +    nsStringBuffer* dynamicBuffer = nsStringBuffer::FromData(mString);
> > +    mString = staticString;
> > +    dynamicBuffer->Release();
> 
> I think this is okay, but I'm just going to write it out as we've diverged
> somewhat from the previous implementation.
> 
> Previously the "promote" was simply an in-place new, we didn't swap the
> buffers. Now we do, so we need to manually release the buffer. Is the
> swapping strictly necessary?

At the moment it isn't necessary. It's ok (though perhaps surprising) for a StaticAtom to point to a heap-allocated nsStringBuffer.

However, in the long run I hope to change StaticAtom so it can point to something other than an nsStringBuffer, because nsStringBuffer has a refcnt and a length, neither of which are useful for static atoms.


> This is also missing the |NS_BUILD_REFCNT_LOGGING| shenanigans from
> |PromoteToPermanent|, which to be honest I don't fully follow but I suppose
> it's there for a reason.

I also didn't fully follow it and I did a try push with it gone and there were no complaints :)
New version addresses all the comments.
Attachment #8738327 - Flags: review?(erahm)
Attachment #8737661 - Attachment is obsolete: true
Comment on attachment 8738327 [details] [diff] [review]
(part 1) - Overhaul the atom implementation

Review of attachment 8738327 [details] [diff] [review]:
-----------------------------------------------------------------

Looks good. Just want to double-check, you did a try run of a debug build w/ trace ref counting enabled previously?
Attachment #8738327 - Flags: review?(erahm) → review+
> Looks good. Just want to double-check, you did a try run of a debug build w/
> trace ref counting enabled previously?

Hmm, not with trace ref counting enabled. I'll try that.
(In reply to Nicholas Nethercote [:njn] from comment #14)
> > Looks good. Just want to double-check, you did a try run of a debug build w/
> > trace ref counting enabled previously?
> 
> Hmm, not with trace ref counting enabled. I'll try that.

Oh, it's enabled by default on debug builds. So I have tried it. I'll do another try run before landing, too, since there were quite a few changes.

Thank you both for the good review comments.
The diff is a nightmare. I promise that the only change I made other than
reordering things and adding some "//----" separator comments was the type
change for StaticAtomEntry::mAtom :)
Attachment #8738353 - Flags: review?(erahm)
Comment on attachment 8738353 [details] [diff] [review]
(part 4) - Change StaticAtomEntry::mAtom to |StaticAtom*|

Review of attachment 8738353 [details] [diff] [review]:
-----------------------------------------------------------------

Thanks for following up.

::: xpcom/ds/nsAtomTable.cpp
@@ +451,5 @@
> +  enum { ALLOW_MEMMOVE = true };
> +
> +  // mAtom only points to objects of type StaticAtom, which are not
> +  // really refcounted.  But since these entries live in a global hashtable,
> +  // this reference is essentially owning.

This comment could probably be tweaked now.
Attachment #8738353 - Flags: review?(erahm) → review+
You need to log in before you can comment on or make changes to this bug.