|
| 1 | +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| 2 | + * vim: set ts=8 sts=4 et sw=4 tw=99: |
| 3 | + * This Source Code Form is subject to the terms of the Mozilla Public |
| 4 | + * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 5 | + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| 6 | + |
| 7 | +/* JS::Anchor implementation. */ |
| 8 | + |
| 9 | +#ifndef js_Anchor_h |
| 10 | +#define js_Anchor_h |
| 11 | + |
| 12 | +#include "mozilla/Attributes.h" |
| 13 | + |
| 14 | +#include "js/TypeDecls.h" |
| 15 | + |
| 16 | +namespace JS { |
| 17 | + |
| 18 | +/* |
| 19 | + * Protecting non-Value, non-JSObject *, non-JSString * values from collection |
| 20 | + * |
| 21 | + * Most of the time, the garbage collector's conservative stack scanner works |
| 22 | + * behind the scenes, finding all live values and protecting them from being |
| 23 | + * collected. However, when JSAPI client code obtains a pointer to data the |
| 24 | + * scanner does not know about, owned by an object the scanner does know about, |
| 25 | + * Care Must Be Taken. |
| 26 | + * |
| 27 | + * The scanner recognizes only a select set of types: pointers to JSObjects and |
| 28 | + * similar things (JSFunctions, and so on), pointers to JSStrings, and Values. |
| 29 | + * So while the scanner finds all live |JSString| pointers, it does not notice |
| 30 | + * |jschar| pointers. |
| 31 | + * |
| 32 | + * So suppose we have: |
| 33 | + * |
| 34 | + * void f(JSString *str) { |
| 35 | + * const jschar *ch = JS_GetStringCharsZ(str); |
| 36 | + * ... do stuff with ch, but no uses of str ...; |
| 37 | + * } |
| 38 | + * |
| 39 | + * After the call to |JS_GetStringCharsZ|, there are no further uses of |
| 40 | + * |str|, which means that the compiler is within its rights to not store |
| 41 | + * it anywhere. But because the stack scanner will not notice |ch|, there |
| 42 | + * is no longer any live value in this frame that would keep the string |
| 43 | + * alive. If |str| is the last reference to that |JSString|, and the |
| 44 | + * collector runs while we are using |ch|, the string's array of |jschar|s |
| 45 | + * may be freed out from under us. |
| 46 | + * |
| 47 | + * Note that there is only an issue when 1) we extract a thing X the scanner |
| 48 | + * doesn't recognize from 2) a thing Y the scanner does recognize, and 3) if Y |
| 49 | + * gets garbage-collected, then X gets freed. If we have code like this: |
| 50 | + * |
| 51 | + * void g(JSObject *obj) { |
| 52 | + * JS::Value x; |
| 53 | + * JS_GetProperty(obj, "x", &x); |
| 54 | + * ... do stuff with x ... |
| 55 | + * } |
| 56 | + * |
| 57 | + * there's no problem, because the value we've extracted, x, is a Value, a |
| 58 | + * type that the conservative scanner recognizes. |
| 59 | + * |
| 60 | + * Conservative GC frees us from the obligation to explicitly root the types it |
| 61 | + * knows about, but when we work with derived values like |ch|, we must root |
| 62 | + * their owners, as the derived value alone won't keep them alive. |
| 63 | + * |
| 64 | + * A JS::Anchor is a kind of GC root that allows us to keep the owners of |
| 65 | + * derived values like |ch| alive throughout the Anchor's lifetime. We could |
| 66 | + * fix the above code as follows: |
| 67 | + * |
| 68 | + * void f(JSString *str) { |
| 69 | + * JS::Anchor<JSString *> a_str(str); |
| 70 | + * const jschar *ch = JS_GetStringCharsZ(str); |
| 71 | + * ... do stuff with ch, but no uses of str ...; |
| 72 | + * } |
| 73 | + * |
| 74 | + * This simply ensures that |str| will be live until |a_str| goes out of scope. |
| 75 | + * As long as we don't retain a pointer to the string's characters for longer |
| 76 | + * than that, we have avoided all garbage collection hazards. |
| 77 | + */ |
| 78 | +template<typename T> class AnchorPermitted; |
| 79 | +template<> class AnchorPermitted<JSObject *> { }; |
| 80 | +template<> class AnchorPermitted<const JSObject *> { }; |
| 81 | +template<> class AnchorPermitted<JSFunction *> { }; |
| 82 | +template<> class AnchorPermitted<const JSFunction *> { }; |
| 83 | +template<> class AnchorPermitted<JSString *> { }; |
| 84 | +template<> class AnchorPermitted<const JSString *> { }; |
| 85 | +template<> class AnchorPermitted<Value> { }; |
| 86 | +template<> class AnchorPermitted<const JSScript *> { }; |
| 87 | +template<> class AnchorPermitted<JSScript *> { }; |
| 88 | + |
| 89 | +template<typename T> |
| 90 | +class Anchor : AnchorPermitted<T> |
| 91 | +{ |
| 92 | + public: |
| 93 | + Anchor() { } |
| 94 | + explicit Anchor(T t) { hold = t; } |
| 95 | + inline ~Anchor(); |
| 96 | + |
| 97 | + private: |
| 98 | + T hold; |
| 99 | + |
| 100 | + /* |
| 101 | + * Rooting analysis considers use of operator= to be a use of an anchor. |
| 102 | + * For simplicity, Anchor is treated as if it contained a GC thing, from |
| 103 | + * construction. Thus if we had |
| 104 | + * |
| 105 | + * void operator=(const T &t) { hold = t; } |
| 106 | + * |
| 107 | + * and this code |
| 108 | + * |
| 109 | + * JS::Anchor<JSString*> anchor; |
| 110 | + * stuff that could GC, producing |str|; |
| 111 | + * anchor = str; |
| 112 | + * |
| 113 | + * the last line would be seen as a hazard, because the final = would "use" |
| 114 | + * |anchor| that is a GC thing -- which could have been moved around by the |
| 115 | + * GC. The workaround is to structure your code so that JS::Anchor is |
| 116 | + * always constructed, living for however long the corresponding value must |
| 117 | + * live. |
| 118 | + */ |
| 119 | + void operator=(const T &t) MOZ_DELETE; |
| 120 | + |
| 121 | + Anchor(const Anchor &other) MOZ_DELETE; |
| 122 | + void operator=(const Anchor &other) MOZ_DELETE; |
| 123 | +}; |
| 124 | + |
| 125 | +template<typename T> |
| 126 | +inline Anchor<T>::~Anchor() |
| 127 | +{ |
| 128 | +#ifdef __GNUC__ |
| 129 | + /* |
| 130 | + * No code is generated for this. But because this is marked 'volatile', G++ will |
| 131 | + * assume it has important side-effects, and won't delete it. (G++ never looks at |
| 132 | + * the actual text and notices it's empty.) And because we have passed |hold| to |
| 133 | + * it, GCC will keep |hold| alive until this point. |
| 134 | + * |
| 135 | + * The "memory" clobber operand ensures that G++ will not move prior memory |
| 136 | + * accesses after the asm --- it's a barrier. Unfortunately, it also means that |
| 137 | + * G++ will assume that all memory has changed after the asm, as it would for a |
| 138 | + * call to an unknown function. I don't know of a way to avoid that consequence. |
| 139 | + */ |
| 140 | + asm volatile("":: "g" (hold) : "memory"); |
| 141 | +#else |
| 142 | + /* |
| 143 | + * An adequate portable substitute, for non-structure types. |
| 144 | + * |
| 145 | + * The compiler promises that, by the end of an expression statement, the |
| 146 | + * last-stored value to a volatile object is the same as it would be in an |
| 147 | + * unoptimized, direct implementation (the "abstract machine" whose behavior the |
| 148 | + * language spec describes). However, the compiler is still free to reorder |
| 149 | + * non-volatile accesses across this store --- which is what we must prevent. So |
| 150 | + * assigning the held value to a volatile variable, as we do here, is not enough. |
| 151 | + * |
| 152 | + * In our case, however, garbage collection only occurs at function calls, so it |
| 153 | + * is sufficient to ensure that the destructor's store isn't moved earlier across |
| 154 | + * any function calls that could collect. It is hard to imagine the compiler |
| 155 | + * analyzing the program so thoroughly that it could prove that such motion was |
| 156 | + * safe. In practice, compilers treat calls to the collector as opaque operations |
| 157 | + * --- in particular, as operations which could access volatile variables, across |
| 158 | + * which this destructor must not be moved. |
| 159 | + * |
| 160 | + * ("Objection, your honor! *Alleged* killer whale!") |
| 161 | + * |
| 162 | + * The disadvantage of this approach is that it does generate code for the store. |
| 163 | + * We do need to use Anchors in some cases where cycles are tight. |
| 164 | + * |
| 165 | + * Note that there is a Anchor<Value>::~Anchor() specialization in Value.h. |
| 166 | + */ |
| 167 | + volatile T sink; |
| 168 | + sink = hold; |
| 169 | +#endif /* defined(__GNUC__) */ |
| 170 | +} |
| 171 | + |
| 172 | +} // namespace JS |
| 173 | + |
| 174 | +#endif /* js_Anchor_h */ |
0 commit comments