source: webkit/trunk/JavaScriptCore/kjs/identifier.cpp@ 20204

Last change on this file since 20204 was 20004, checked in by ggaren, 18 years ago

JavaScriptCore:

Reviewed by Maciej Stachowiak.


Fixed all known crashers exposed by run-webkit-tests --threaded. This covers:

<rdar://problem/4565394> | http://bugs.webkit.org/show_bug.cgi?id=12585

PAC file: after closing a window that contains macworld.com, new window
crashes (KJS::PropertyMap::mark()) (12585)

<rdar://problem/4571215> | http://bugs.webkit.org/show_bug.cgi?id=9211

PAC file: Crash occurs when clicking on the navigation tabs at http://www.businessweek.com/ (9211)

<rdar://problem/4557926>

PAC file: Crash occurs when attempting to view image in slideshow mode
at http://d.smugmug.com/gallery/581716 ( KJS::IfNode::execute (KJS::
ExecState*) + 312) if you use a PAC file

(1) Added some missing JSLocks, along with related ASSERTs.


(2) Fully implemented support for objects that can only be garbage collected
on the main thread. So far, only WebCore uses this. We can add it to API
later if we learn that it's needed.


The implementation uses a "main thread only" flag inside each object. When
collecting on a secondary thread, the Collector does an extra pass through
the heap to mark all flagged objects before sweeping. This solution makes
the common case -- flag lots of objects, but never collect on a secondary
thread -- very fast, even though the uncommon case of garbage collecting
on a secondary thread isn't as fast as it could be. I left some notes
about how to speed it up, if we ever care.


For posterity, here are some things I learned about GC while investigating:


  • Each collect must either mark or delete every heap object. "Zombie" objects, which are neither marked nor deleted, raise these issues:
  • On the next pass, the conservative marking algorithm might mark a zombie, causing it to mark freed objects.
  • The client might try to use a zombie, which would seem live because its finalizer had not yet run.
  • A collect on the main thread is free to delete any object. Presumably, objects allocated on secondary threads have thread-safe finalizers.
  • A collect on a secondary thread must not delete thread-unsafe objects.
  • The mark function must be thread-safe.


Line by line comments:

  • API/JSObjectRef.h: Added comment specifying that the finalize callback may run on any thread.
  • bindings/npruntime.cpp: (_NPN_GetStringIdentifier): Added JSLock.
  • bindings/objc/objc_instance.h:
  • bindings/objc/objc_instance.mm: (ObjcInstance::~ObjcInstance): Use an autorelease pool. The other callers to CFRelease needed one, too, but they were dead code, so I removed them instead. (This fixes a leak seen while running run-webkit-tests --threaded, although I don't think it's specifically a threading issue.)


  • kjs/collector.cpp: (KJS::Collector::collectOnMainThreadOnly): New function. Tells the collector to collect a value only if it's collecting on the main thread. (KJS::Collector::markMainThreadOnlyObjects): New function. Scans the heap for "main thread only" objects and marks them.
  • kjs/date_object.cpp: (KJS::DateObjectImp::DateObjectImp): To make the new ASSERTs happy, allocate our globals on the heap, avoiding a seemingly unsafe destructor call at program exit time.
  • kjs/function_object.cpp: (FunctionPrototype::FunctionPrototype): ditto
  • kjs/interpreter.cpp: (KJS::Interpreter::mark): Removed boolean parameter, which was an incomplete and arguably hackish way to implement markMainThreadOnlyObjects() inside WebCore.
  • kjs/interpreter.h:
  • kjs/identifier.cpp: (KJS::identifierTable): Added some ASSERTs to check for thread safety problems.
  • kjs/list.cpp: Added some ASSERTs to check for thread safety problems. (KJS::allocateListImp): (KJS::List::release): (KJS::List::append): (KJS::List::empty): Make the new ASSERTs happy.
  • kjs/object.h: (KJS::JSObject::JSObject): "m_destructorIsThreadSafe" => "m_collectOnMainThreadOnly". I removed the constructor parameter because m_collectOnMainThreadOnly, like m_marked, is a Collector bit, so only the Collector should set or get it.
  • kjs/object_object.cpp: (ObjectPrototype::ObjectPrototype): Make the ASSERTs happy.
  • kjs/regexp_object.cpp: (RegExpPrototype::RegExpPrototype): ditto
  • kjs/ustring.cpp: Added some ASSERTs to check for thread safety problems. (KJS::UCharReference::ref): (KJS::UString::Rep::createCopying): (KJS::UString::Rep::create): (KJS::UString::Rep::destroy): (KJS::UString::null): Make the new ASSERTs happy.
  • kjs/ustring.h: (KJS::UString::Rep::ref): Added some ASSERTs to check for thread safety problems. (KJS::UString::Rep::deref):
  • kjs/value.h: (KJS::JSCell::JSCell):

JavaScriptGlue:

Reviewed by Maciej Stachowiak.

Fixed all known crashers exposed by run-webkit-tests --threaded while using
a PAC file (for maximum carnage). See JavaScriptCore ChangeLog for
more details.

  • JSBase.cpp: (JSBase::Release): Lock when deleting, because we may be deleting an object (like a JSRun) that holds thread-unsafe data.
  • JSUtils.cpp: (CFStringToUString): Don't lock, because our caller locks. Also, locking inside a function that returns thread-unsafe data by copy will only mask threading problems.
  • JavaScriptGlue.cpp: (JSRunEvaluate): Added missing JSLock. (JSRunCheckSyntax): Converted to JSLock.
  • JavaScriptGlue.xcodeproj/project.pbxproj:

WebCore:

Reviewed by Maciej Stachowiak.

Fixed all known crashers exposed by run-webkit-tests --threaded [*]. See
JavaScriptCore ChangeLog for more details.

  • bindings/js/kjs_binding.cpp: (KJS::domNodesPerDocument): Added thread safety ASSERT. (KJS::ScriptInterpreter::mark): Removed obsolete logic for marking unsafe objects when collecting on a secondary thread. The Collector takes care of this now.
  • bindings/js/kjs_binding.h: (KJS::DOMObject::DOMObject): Used new API for specifying that WebCore objects should be garbage collected on the main thread only.
  • bindings/js/kjs_window.cpp: (KJS::ScheduledAction::execute): Moved JSLock to cover implementedsCall() call, which, for some subclasses, ends up allocating garbage collected objects. (This fix was speculative. I didn't actually see a crash from this.) (KJS::Window::timerFired): Added JSLock around ScheduleAction destruction, since it destroys a KJS::List.
  • bindings/objc/WebScriptObject.mm: (-[WebScriptObject setException:]): Added JSLock. (This fix was speculative. I didn't actually see a crash from this.)
  • bridge/mac/WebCoreScriptDebugger.mm: (-[WebCoreScriptCallFrame evaluateWebScript:]): Added JSLock. (This fix was speculative. I didn't actually see a crash from this.)
  • dom/Document.cpp: (WebCore::Document::~Document): Added JSLock around modification to domNodesPerDocument(), which can be accessed concurrently during garbage collection.
  • dom/Node.cpp: (WebCore::Node::setDocument): ditto.


[*] fast/js/toString-stack-overflow.html is an exception. --threaded mode
crashes this test because it causes the garbage collector to run frequently,
and this test crashes if you happen to garbage collect while it's running.
This is a known issue with stack overflow during the mark phase. It's
not related to threading.

  • Property svn:eol-style set to native
File size: 6.9 KB
Line 
1/*
2 * This file is part of the KDE libraries
3 * Copyright (C) 2003 Apple Computer, Inc
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 *
20 */
21
22#include "config.h"
23// For JavaScriptCore we need to avoid having static constructors.
24// Our strategy is to declare the global objects with a different type (initialized to 0)
25// and then use placement new to initialize the global objects later. This is not completely
26// portable, and it would be good to figure out a 100% clean way that still avoids code that
27// runs at init time.
28
29#if !PLATFORM(WIN_OS) // can't get this to compile on Visual C++ yet
30#define AVOID_STATIC_CONSTRUCTORS 1
31#else
32#define AVOID_STATIC_CONSTRUCTORS 0
33#endif
34
35#if AVOID_STATIC_CONSTRUCTORS
36#define KJS_IDENTIFIER_HIDE_GLOBALS 1
37#endif
38
39#include "identifier.h"
40
41#include "JSLock.h"
42#include <new> // for placement new
43#include <string.h> // for strlen
44#include <wtf/Assertions.h>
45#include <wtf/FastMalloc.h>
46#include <wtf/HashSet.h>
47
48namespace WTF {
49
50 template<typename T> struct DefaultHash;
51 template<typename T> struct StrHash;
52
53 template<> struct StrHash<KJS::UString::Rep *> {
54 static unsigned hash(const KJS::UString::Rep *key) { return key->hash(); }
55 static bool equal(const KJS::UString::Rep *a, const KJS::UString::Rep *b) { return KJS::Identifier::equal(a, b); }
56 };
57
58 template<> struct DefaultHash<KJS::UString::Rep *> {
59 typedef StrHash<KJS::UString::Rep *> Hash;
60 };
61
62}
63
64namespace KJS {
65
66typedef HashSet<UString::Rep *> IdentifierTable;
67static IdentifierTable *table;
68
69static inline IdentifierTable& identifierTable()
70{
71 ASSERT(JSLock::lockCount() > 0);
72
73 if (!table)
74 table = new IdentifierTable;
75 return *table;
76}
77
78
79bool Identifier::equal(const UString::Rep *r, const char *s)
80{
81 int length = r->len;
82 const UChar *d = r->data();
83 for (int i = 0; i != length; ++i)
84 if (d[i].uc != (unsigned char)s[i])
85 return false;
86 return s[length] == 0;
87}
88
89bool Identifier::equal(const UString::Rep *r, const UChar *s, int length)
90{
91 if (r->len != length)
92 return false;
93 const UChar *d = r->data();
94 for (int i = 0; i != length; ++i)
95 if (d[i].uc != s[i].uc)
96 return false;
97 return true;
98}
99
100bool Identifier::equal(const UString::Rep *r, const UString::Rep *b)
101{
102 int length = r->len;
103 if (length != b->len)
104 return false;
105 const UChar *d = r->data();
106 const UChar *s = b->data();
107 for (int i = 0; i != length; ++i)
108 if (d[i].uc != s[i].uc)
109 return false;
110 return true;
111}
112
113struct CStringTranslator
114{
115 static unsigned hash(const char *c)
116 {
117 return UString::Rep::computeHash(c);
118 }
119
120 static bool equal(UString::Rep *r, const char *s)
121 {
122 return Identifier::equal(r, s);
123 }
124
125 static void translate(UString::Rep*& location, const char *c, unsigned hash)
126 {
127 size_t length = strlen(c);
128 UChar *d = static_cast<UChar *>(fastMalloc(sizeof(UChar) * length));
129 for (size_t i = 0; i != length; i++)
130 d[i] = c[i];
131
132 UString::Rep *r = UString::Rep::create(d, static_cast<int>(length)).releaseRef();
133 r->isIdentifier = 1;
134 r->rc = 0;
135 r->_hash = hash;
136
137 location = r;
138 }
139};
140
141PassRefPtr<UString::Rep> Identifier::add(const char *c)
142{
143 if (!c)
144 return &UString::Rep::null;
145 size_t length = strlen(c);
146 if (length == 0)
147 return &UString::Rep::empty;
148
149 return *identifierTable().add<const char *, CStringTranslator>(c).first;
150}
151
152struct UCharBuffer {
153 const UChar *s;
154 unsigned int length;
155};
156
157struct UCharBufferTranslator
158{
159 static unsigned hash(const UCharBuffer& buf)
160 {
161 return UString::Rep::computeHash(buf.s, buf.length);
162 }
163
164 static bool equal(UString::Rep *str, const UCharBuffer& buf)
165 {
166 return Identifier::equal(str, buf.s, buf.length);
167 }
168
169 static void translate(UString::Rep *& location, const UCharBuffer& buf, unsigned hash)
170 {
171 UChar *d = static_cast<UChar *>(fastMalloc(sizeof(UChar) * buf.length));
172 for (unsigned i = 0; i != buf.length; i++)
173 d[i] = buf.s[i];
174
175 UString::Rep *r = UString::Rep::create(d, buf.length).releaseRef();
176 r->isIdentifier = 1;
177 r->rc = 0;
178 r->_hash = hash;
179
180 location = r;
181 }
182};
183
184PassRefPtr<UString::Rep> Identifier::add(const UChar *s, int length)
185{
186 if (length == 0)
187 return &UString::Rep::empty;
188
189 UCharBuffer buf = {s, length};
190 return *identifierTable().add<UCharBuffer, UCharBufferTranslator>(buf).first;
191}
192
193PassRefPtr<UString::Rep> Identifier::add(UString::Rep *r)
194{
195 if (r->isIdentifier)
196 return r;
197
198 if (r->len == 0)
199 return &UString::Rep::empty;
200
201 UString::Rep *result = *identifierTable().add(r).first;
202 if (result == r)
203 r->isIdentifier = true;
204 return result;
205}
206
207void Identifier::remove(UString::Rep *r)
208{
209 identifierTable().remove(r);
210}
211
212// Global constants for property name strings.
213
214#if !AVOID_STATIC_CONSTRUCTORS
215 // Define an Identifier in the normal way.
216 #define DEFINE_GLOBAL(name, string) extern const Identifier name(string);
217#else
218 // Define an Identifier-sized array of pointers to avoid static initialization.
219 // Use an array of pointers instead of an array of char in case there is some alignment issue.
220 #define DEFINE_GLOBAL(name, string) \
221 void * name[(sizeof(Identifier) + sizeof(void *) - 1) / sizeof(void *)];
222#endif
223
224const char * const nullCString = 0;
225
226DEFINE_GLOBAL(nullIdentifier, nullCString)
227DEFINE_GLOBAL(specialPrototypePropertyName, "__proto__")
228
229#define DEFINE_PROPERTY_NAME_GLOBAL(name) DEFINE_GLOBAL(name ## PropertyName, #name)
230KJS_IDENTIFIER_EACH_PROPERTY_NAME_GLOBAL(DEFINE_PROPERTY_NAME_GLOBAL)
231
232void Identifier::init()
233{
234#if AVOID_STATIC_CONSTRUCTORS
235 static bool initialized;
236 if (!initialized) {
237 // Use placement new to initialize the globals.
238
239 new (&nullIdentifier) Identifier(nullCString);
240 new (&specialPrototypePropertyName) Identifier("__proto__");
241
242 #define PLACEMENT_NEW_PROPERTY_NAME_GLOBAL(name) new(&name ## PropertyName) Identifier(#name);
243 KJS_IDENTIFIER_EACH_PROPERTY_NAME_GLOBAL(PLACEMENT_NEW_PROPERTY_NAME_GLOBAL)
244
245 initialized = true;
246 }
247#endif
248}
249
250} // namespace KJS
Note: See TracBrowser for help on using the repository browser.