author | Jeff Walden <jwalden@mit.edu> |
Fri, 01 Feb 2013 15:31:00 -0800 | |
changeset 120943 | 7f1ecab23f6f67254c5fb9ea5c1c0384cbeedaf4 |
parent 120942 | dff079686e0bc98784a55b760474e1399856c325 |
child 120944 | f13e3cac0c1b058fa6880a50f618e288941dda85 |
push id | 22497 |
push user | jwalden@mit.edu |
push date | Tue, 05 Feb 2013 23:38:16 +0000 |
treeherder | mozilla-inbound@43a5c9890c22 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jorendorff |
bugs | 837773 |
milestone | 21.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
new file mode 100644 --- /dev/null +++ b/js/public/Anchor.h @@ -0,0 +1,163 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99 ft=cpp: + * + * 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/. */ + +/* JS::Anchor implementation. */ + +#ifndef js_Anchor_h___ +#define js_Anchor_h___ + +#include "mozilla/Attributes.h" + +class JSFunction; +class JSObject; +class JSScript; +class JSString; + +namespace JS { class Value; } + +namespace JS { + +/* + * Protecting non-Value, non-JSObject *, non-JSString * values from collection + * + * Most of the time, the garbage collector's conservative stack scanner works + * behind the scenes, finding all live values and protecting them from being + * collected. However, when JSAPI client code obtains a pointer to data the + * scanner does not know about, owned by an object the scanner does know about, + * Care Must Be Taken. + * + * The scanner recognizes only a select set of types: pointers to JSObjects and + * similar things (JSFunctions, and so on), pointers to JSStrings, and Values. + * So while the scanner finds all live |JSString| pointers, it does not notice + * |jschar| pointers. + * + * So suppose we have: + * + * void f(JSString *str) { + * const jschar *ch = JS_GetStringCharsZ(str); + * ... do stuff with ch, but no uses of str ...; + * } + * + * After the call to |JS_GetStringCharsZ|, there are no further uses of + * |str|, which means that the compiler is within its rights to not store + * it anywhere. But because the stack scanner will not notice |ch|, there + * is no longer any live value in this frame that would keep the string + * alive. If |str| is the last reference to that |JSString|, and the + * collector runs while we are using |ch|, the string's array of |jschar|s + * may be freed out from under us. + * + * Note that there is only an issue when 1) we extract a thing X the scanner + * doesn't recognize from 2) a thing Y the scanner does recognize, and 3) if Y + * gets garbage-collected, then X gets freed. If we have code like this: + * + * void g(JSObject *obj) { + * JS::Value x; + * JS_GetProperty(obj, "x", &x); + * ... do stuff with x ... + * } + * + * there's no problem, because the value we've extracted, x, is a Value, a + * type that the conservative scanner recognizes. + * + * Conservative GC frees us from the obligation to explicitly root the types it + * knows about, but when we work with derived values like |ch|, we must root + * their owners, as the derived value alone won't keep them alive. + * + * A JS::Anchor is a kind of GC root that allows us to keep the owners of + * derived values like |ch| alive throughout the Anchor's lifetime. We could + * fix the above code as follows: + * + * void f(JSString *str) { + * JS::Anchor<JSString *> a_str(str); + * const jschar *ch = JS_GetStringCharsZ(str); + * ... do stuff with ch, but no uses of str ...; + * } + * + * This simply ensures that |str| will be live until |a_str| goes out of scope. + * As long as we don't retain a pointer to the string's characters for longer + * than that, we have avoided all garbage collection hazards. + */ +template<typename T> class AnchorPermitted; +template<> class AnchorPermitted<JSObject *> { }; +template<> class AnchorPermitted<const JSObject *> { }; +template<> class AnchorPermitted<JSFunction *> { }; +template<> class AnchorPermitted<const JSFunction *> { }; +template<> class AnchorPermitted<JSString *> { }; +template<> class AnchorPermitted<const JSString *> { }; +template<> class AnchorPermitted<Value> { }; +template<> class AnchorPermitted<const JSScript *> { }; +template<> class AnchorPermitted<JSScript *> { }; + +template<typename T> +class Anchor : AnchorPermitted<T> +{ + public: + Anchor() { } + explicit Anchor(T t) { hold = t; } + inline ~Anchor(); + T &get() { return hold; } + const T &get() const { return hold; } + void set(const T &t) { hold = t; } + void operator=(const T &t) { hold = t; } + void clear() { hold = 0; } + + private: + T hold; + Anchor(const Anchor &other) MOZ_DELETE; + void operator=(const Anchor &other) MOZ_DELETE; +}; + +template<typename T> +inline Anchor<T>::~Anchor() +{ +#ifdef __GNUC__ + /* + * No code is generated for this. But because this is marked 'volatile', G++ will + * assume it has important side-effects, and won't delete it. (G++ never looks at + * the actual text and notices it's empty.) And because we have passed |hold| to + * it, GCC will keep |hold| alive until this point. + * + * The "memory" clobber operand ensures that G++ will not move prior memory + * accesses after the asm --- it's a barrier. Unfortunately, it also means that + * G++ will assume that all memory has changed after the asm, as it would for a + * call to an unknown function. I don't know of a way to avoid that consequence. + */ + asm volatile("":: "g" (hold) : "memory"); +#else + /* + * An adequate portable substitute, for non-structure types. + * + * The compiler promises that, by the end of an expression statement, the + * last-stored value to a volatile object is the same as it would be in an + * unoptimized, direct implementation (the "abstract machine" whose behavior the + * language spec describes). However, the compiler is still free to reorder + * non-volatile accesses across this store --- which is what we must prevent. So + * assigning the held value to a volatile variable, as we do here, is not enough. + * + * In our case, however, garbage collection only occurs at function calls, so it + * is sufficient to ensure that the destructor's store isn't moved earlier across + * any function calls that could collect. It is hard to imagine the compiler + * analyzing the program so thoroughly that it could prove that such motion was + * safe. In practice, compilers treat calls to the collector as opaque operations + * --- in particular, as operations which could access volatile variables, across + * which this destructor must not be moved. + * + * ("Objection, your honor! *Alleged* killer whale!") + * + * The disadvantage of this approach is that it does generate code for the store. + * We do need to use Anchors in some cases where cycles are tight. + * + * Note that there is a Anchor<Value>::~Anchor() specialization in Value.h. + */ + volatile T sink; + sink = hold; +#endif /* defined(__GNUC__) */ +} + +} // namespace JS + +#endif /* js_Anchor_h___ */
--- a/js/public/Value.h +++ b/js/public/Value.h @@ -5,16 +5,17 @@ * 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/. */ /* JS::Value implementation. */ #ifndef js_Value_h___ #define js_Value_h___ +#include "js/Anchor.h" #include "js/Utility.h" /* * Try to get jsvals 64-bit aligned. We could almost assert that all values are * aligned, but MSVC and GCC occasionally break alignment. */ #if defined(__GNUC__) || defined(__xlc__) || defined(__xlC__) # define JSVAL_ALIGNMENT __attribute__((aligned (8))) @@ -831,9 +832,33 @@ JS_CANONICALIZE_NAN(double d) return d; } #ifdef __cplusplus static jsval_layout JSVAL_TO_IMPL(JS::Value); static JS::Value IMPL_TO_JSVAL(jsval_layout); #endif +namespace JS { + +#ifndef __GNUC__ +/* + * The default assignment operator for |struct C| has the signature: + * + * C& C::operator=(const C&) + * + * And in particular requires implicit conversion of |this| to type |C| for the + * return value. But |volatile C| cannot thus be converted to |C|, so just + * doing |sink = hold| as in the non-specialized version would fail to compile. + * Do the assignment on asBits instead, since I don't think we want to give + * jsval_layout an assignment operator returning |volatile jsval_layout|. + */ +template<> +inline Anchor<Value>::~Anchor() +{ + volatile uint64_t bits; + bits = JSVAL_TO_IMPL(hold).asBits; +} +#endif + +} // namespace JS + #endif /* js_Value_h___ */
--- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -218,20 +218,21 @@ VPATH += \ $(NULL) EXPORTS_NAMESPACES += js # If you add a header here, add it to js/src/jsapi-tests/testIntTypesABI.cpp so # that we ensure we don't over-expose our internal integer typedefs. Note that # LegacyIntTypes.h below is deliberately exempted from this requirement. EXPORTS_js = \ + Anchor.h \ CharacterEncoding.h \ + GCAPI.h \ HashTable.h \ HeapAPI.h \ - GCAPI.h \ LegacyIntTypes.h \ MemoryMetrics.h \ TemplateLib.h \ Utility.h \ Value.h \ Vector.h \ $(NULL)
--- a/js/src/jsapi-tests/testIntTypesABI.cpp +++ b/js/src/jsapi-tests/testIntTypesABI.cpp @@ -12,16 +12,19 @@ */ #include "js-config.h" #include "jsapi.h" #include "jsclass.h" #include "jscpucfg.h" #include "jspubtd.h" #include "jstypes.h" +#include "js/Anchor.h" +#include "js/CharacterEncoding.h" +#include "js/GCAPI.h" #include "js/HashTable.h" #include "js/HeapAPI.h" #include "js/MemoryMetrics.h" #include "js/TemplateLib.h" #include "js/Utility.h" #include "js/Value.h" #include "js/Vector.h"
--- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -22,16 +22,17 @@ #include <stdio.h> #include "js-config.h" #include "jsalloc.h" #include "jspubtd.h" #include "jsutil.h" #include "gc/Root.h" +#include "js/Anchor.h" #include "js/CharacterEncoding.h" #include "js/HashTable.h" #include "js/Utility.h" #include "js/Value.h" #include "js/Vector.h" /************************************************************************/ @@ -52,156 +53,16 @@ class StableCharPtr : public CharPtr { StableCharPtr(const mozilla::RangedPtr<const jschar> &s) : CharPtr(s) {} StableCharPtr(const jschar *s, size_t len) : CharPtr(s, len) {} StableCharPtr(const jschar *pos, const jschar *start, size_t len) : CharPtr(pos, start, len) {} }; /* - * Protecting non-jsval, non-JSObject *, non-JSString * values from collection - * - * Most of the time, the garbage collector's conservative stack scanner works - * behind the scenes, finding all live values and protecting them from being - * collected. However, when JSAPI client code obtains a pointer to data the - * scanner does not know about, owned by an object the scanner does know about, - * Care Must Be Taken. - * - * The scanner recognizes only a select set of types: pointers to JSObjects and - * similar things (JSFunctions, and so on), pointers to JSStrings, and jsvals. - * So while the scanner finds all live |JSString| pointers, it does not notice - * |jschar| pointers. - * - * So suppose we have: - * - * void f(JSString *str) { - * const jschar *ch = JS_GetStringCharsZ(str); - * ... do stuff with ch, but no uses of str ...; - * } - * - * After the call to |JS_GetStringCharsZ|, there are no further uses of - * |str|, which means that the compiler is within its rights to not store - * it anywhere. But because the stack scanner will not notice |ch|, there - * is no longer any live value in this frame that would keep the string - * alive. If |str| is the last reference to that |JSString|, and the - * collector runs while we are using |ch|, the string's array of |jschar|s - * may be freed out from under us. - * - * Note that there is only an issue when 1) we extract a thing X the scanner - * doesn't recognize from 2) a thing Y the scanner does recognize, and 3) if Y - * gets garbage-collected, then X gets freed. If we have code like this: - * - * void g(JSObject *obj) { - * jsval x; - * JS_GetProperty(obj, "x", &x); - * ... do stuff with x ... - * } - * - * there's no problem, because the value we've extracted, x, is a jsval, a - * type that the conservative scanner recognizes. - * - * Conservative GC frees us from the obligation to explicitly root the types it - * knows about, but when we work with derived values like |ch|, we must root - * their owners, as the derived value alone won't keep them alive. - * - * A JS::Anchor is a kind of GC root that allows us to keep the owners of - * derived values like |ch| alive throughout the Anchor's lifetime. We could - * fix the above code as follows: - * - * void f(JSString *str) { - * JS::Anchor<JSString *> a_str(str); - * const jschar *ch = JS_GetStringCharsZ(str); - * ... do stuff with ch, but no uses of str ...; - * } - * - * This simply ensures that |str| will be live until |a_str| goes out of scope. - * As long as we don't retain a pointer to the string's characters for longer - * than that, we have avoided all garbage collection hazards. - */ -template<typename T> class AnchorPermitted; -template<> class AnchorPermitted<JSObject *> { }; -template<> class AnchorPermitted<const JSObject *> { }; -template<> class AnchorPermitted<JSFunction *> { }; -template<> class AnchorPermitted<const JSFunction *> { }; -template<> class AnchorPermitted<JSString *> { }; -template<> class AnchorPermitted<const JSString *> { }; -template<> class AnchorPermitted<Value> { }; -template<> class AnchorPermitted<const JSScript *> { }; -template<> class AnchorPermitted<JSScript *> { }; - -template<typename T> -class Anchor: AnchorPermitted<T> -{ - public: - Anchor() { } - explicit Anchor(T t) { hold = t; } - inline ~Anchor(); - T &get() { return hold; } - const T &get() const { return hold; } - void set(const T &t) { hold = t; } - void operator=(const T &t) { hold = t; } - void clear() { hold = 0; } - private: - T hold; - Anchor(const Anchor &) MOZ_DELETE; - const Anchor &operator=(const Anchor &) MOZ_DELETE; -}; - -#ifdef __GNUC__ -template<typename T> -inline Anchor<T>::~Anchor() -{ - /* - * No code is generated for this. But because this is marked 'volatile', G++ will - * assume it has important side-effects, and won't delete it. (G++ never looks at - * the actual text and notices it's empty.) And because we have passed |hold| to - * it, GCC will keep |hold| alive until this point. - * - * The "memory" clobber operand ensures that G++ will not move prior memory - * accesses after the asm --- it's a barrier. Unfortunately, it also means that - * G++ will assume that all memory has changed after the asm, as it would for a - * call to an unknown function. I don't know of a way to avoid that consequence. - */ - asm volatile("":: "g" (hold) : "memory"); -} -#else -template<typename T> -inline Anchor<T>::~Anchor() -{ - /* - * An adequate portable substitute, for non-structure types. - * - * The compiler promises that, by the end of an expression statement, the - * last-stored value to a volatile object is the same as it would be in an - * unoptimized, direct implementation (the "abstract machine" whose behavior the - * language spec describes). However, the compiler is still free to reorder - * non-volatile accesses across this store --- which is what we must prevent. So - * assigning the held value to a volatile variable, as we do here, is not enough. - * - * In our case, however, garbage collection only occurs at function calls, so it - * is sufficient to ensure that the destructor's store isn't moved earlier across - * any function calls that could collect. It is hard to imagine the compiler - * analyzing the program so thoroughly that it could prove that such motion was - * safe. In practice, compilers treat calls to the collector as opaque operations - * --- in particular, as operations which could access volatile variables, across - * which this destructor must not be moved. - * - * ("Objection, your honor! *Alleged* killer whale!") - * - * The disadvantage of this approach is that it does generate code for the store. - * We do need to use Anchors in some cases where cycles are tight. - * - * NB: there is a Anchor<Value>::~Anchor() specialization below. - */ - volatile T sink; - sink = hold; -} -#endif /* defined(__GNUC__) */ - -/* * JS::Value is the C++ interface for a single JavaScript Engine value. * A few general notes on JS::Value: * * - JS::Value has setX() and isX() members for X in * * { Int32, Double, String, Boolean, Undefined, Null, Object, Magic } * * JS::Value also contains toX() for each of the non-singleton types. @@ -972,38 +833,16 @@ class RootedBase<JS::Value> : public Mut }; } /* namespace js */ /************************************************************************/ namespace JS { -#ifndef __GNUC__ - -/* - * The default assignment operator for |struct C| has the signature: - * - * C& C::operator=(const C&) - * - * And in particular requires implicit conversion of |this| to type |C| for the - * return value. But |volatile C| cannot thus be converted to |C|, so just - * doing |sink = hold| as in the non-specialized version would fail to compile. - * Do the assignment on asBits instead, since I don't think we want to give - * jsval_layout an assignment operator returning |volatile jsval_layout|. - */ -template<> -inline Anchor<Value>::~Anchor() -{ - volatile uint64_t bits; - bits = JSVAL_TO_IMPL(hold).asBits; -} - -#endif - #if defined JS_THREADSAFE && defined DEBUG class JS_PUBLIC_API(AutoCheckRequestDepth) { JSContext *cx; public: AutoCheckRequestDepth(JSContext *cx); ~AutoCheckRequestDepth();