зеркало из https://github.com/mozilla/pjs.git
Bug 364776: New operation counter API to replace branch callback. r,a=brendan
This commit is contained in:
Родитель
2caf4e3d3c
Коммит
c575726029
|
@ -904,8 +904,8 @@ JS_EndRequest(JSContext *cx)
|
|||
* If js_DropObjectMap returns null, we held the last ref to scope.
|
||||
* The waiting thread(s) must have been killed, after which the GC
|
||||
* collected the object that held this scope. Unlikely, because it
|
||||
* requires that the GC ran (e.g., from a branch callback) during
|
||||
* this request, but possible.
|
||||
* requires that the GC ran (e.g., from an operation callback)
|
||||
* during this request, but possible.
|
||||
*/
|
||||
if (js_DropObjectMap(cx, &scope->map, NULL)) {
|
||||
js_InitLock(&scope->lock);
|
||||
|
@ -4929,13 +4929,79 @@ JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc,
|
|||
return ok;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback,
|
||||
uint32 operationLimit)
|
||||
{
|
||||
JS_ASSERT(callback);
|
||||
JS_ASSERT(operationLimit <= JS_MAX_OPERATION_LIMIT);
|
||||
JS_ASSERT(operationLimit > 0);
|
||||
|
||||
cx->operationCount = (int32) operationLimit;
|
||||
cx->operationLimit = operationLimit;
|
||||
cx->operationCallbackIsSet = 1;
|
||||
cx->operationCallback = callback;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_ClearOperationCallback(JSContext *cx)
|
||||
{
|
||||
cx->operationCount = (int32) JS_MAX_OPERATION_LIMIT;
|
||||
cx->operationLimit = JS_MAX_OPERATION_LIMIT;
|
||||
cx->operationCallbackIsSet = 0;
|
||||
cx->operationCallback = NULL;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSOperationCallback)
|
||||
JS_GetOperationCallback(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(cx->operationCallbackIsSet || !cx->operationCallback);
|
||||
return cx->operationCallback;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(uint32)
|
||||
JS_GetOperationLimit(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(cx->operationCallbackIsSet);
|
||||
return cx->operationLimit;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_SetOperationLimit(JSContext *cx, uint32 operationLimit)
|
||||
{
|
||||
JS_ASSERT(operationLimit <= JS_MAX_OPERATION_LIMIT);
|
||||
JS_ASSERT(operationLimit > 0);
|
||||
JS_ASSERT(cx->operationCallbackIsSet);
|
||||
|
||||
cx->operationLimit = operationLimit;
|
||||
if (cx->operationCount > (int32) operationLimit)
|
||||
cx->operationCount = (int32) operationLimit;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBranchCallback)
|
||||
JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb)
|
||||
{
|
||||
JSBranchCallback oldcb;
|
||||
|
||||
oldcb = cx->branchCallback;
|
||||
cx->branchCallback = cb;
|
||||
if (cx->operationCallbackIsSet) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,
|
||||
"JS API usage error: call to JS_SetOperationCallback is followed by\n"
|
||||
"invocation of deprecated JS_SetBranchCallback\n");
|
||||
JS_ASSERT(0);
|
||||
#endif
|
||||
cx->operationCallbackIsSet = 0;
|
||||
oldcb = NULL;
|
||||
} else {
|
||||
oldcb = (JSBranchCallback) cx->operationCallback;
|
||||
}
|
||||
if (cb) {
|
||||
cx->operationCount = JSOW_SCRIPT_JUMP;
|
||||
cx->operationLimit = JSOW_SCRIPT_JUMP;
|
||||
cx->operationCallback = (JSOperationCallback) cb;
|
||||
} else {
|
||||
JS_ClearOperationCallback(cx);
|
||||
}
|
||||
return oldcb;
|
||||
}
|
||||
|
||||
|
|
|
@ -563,7 +563,10 @@ JS_StringToVersion(const char *string);
|
|||
JS_SetBranchCallback may be
|
||||
called with a null script
|
||||
parameter, by native code
|
||||
that loops intensively */
|
||||
that loops intensively.
|
||||
Deprecated, use
|
||||
JS_SetOperationCallback
|
||||
instead */
|
||||
#define JSOPTION_DONT_REPORT_UNCAUGHT \
|
||||
JS_BIT(8) /* When returning from the
|
||||
outermost API call, prevent
|
||||
|
@ -2096,6 +2099,56 @@ extern JS_PUBLIC_API(JSBool)
|
|||
JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc,
|
||||
jsval *argv, jsval *rval);
|
||||
|
||||
/*
|
||||
* The maximum value of the operation limit to pass to JS_SetOperationCallback
|
||||
* and JS_SetOperationLimit.
|
||||
*/
|
||||
#define JS_MAX_OPERATION_LIMIT ((uint32) 0x7FFFFFFF)
|
||||
|
||||
/*
|
||||
* Set the operation callback that the engine calls periodically after
|
||||
* the internal operation count reaches the specified limit.
|
||||
*
|
||||
* When operationLimit is 4096, the callback will be called at least after
|
||||
* each backward jump in the interpreter. To minimize the overhead of the
|
||||
* callback invocation we suggest at least 100*4096 as a value for
|
||||
* operationLimit.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback,
|
||||
uint32 operationLimit);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_ClearOperationCallback(JSContext *cx);
|
||||
|
||||
extern JS_PUBLIC_API(JSOperationCallback)
|
||||
JS_GetOperationCallback(JSContext *cx);
|
||||
|
||||
/*
|
||||
* Get the operation limit associated with the operation callback. This API
|
||||
* function may be called only when the result of JS_GetOperationCallback(cx)
|
||||
* is not null.
|
||||
*/
|
||||
extern JS_PUBLIC_API(uint32)
|
||||
JS_GetOperationLimit(JSContext *cx);
|
||||
|
||||
/*
|
||||
* Change the operation limit associated with the operation callback. This API
|
||||
* function may be called only when the result of JS_GetOperationCallback(cx)
|
||||
* is not null.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_SetOperationLimit(JSContext *cx, uint32 operationLimit);
|
||||
|
||||
/*
|
||||
* Note well: JS_SetBranchCallback is deprecated. It is similar to
|
||||
*
|
||||
* JS_SetOperationCallback(cx, callback, 4096, NULL);
|
||||
*
|
||||
* except that the callback will not be called from a long-running native
|
||||
* function when JSOPTION_NATIVE_BRANCH_CALLBACK is not set and the top-most
|
||||
* frame is native.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSBranchCallback)
|
||||
JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb);
|
||||
|
||||
|
@ -2256,12 +2309,12 @@ JS_PUBLIC_API(JSBool)
|
|||
JS_CStringsAreUTF8(void);
|
||||
|
||||
/*
|
||||
* Update the value to be returned by JS_CStringsAreUTF8(). Once set,
|
||||
* it can not be changed. Must be called before the first call to
|
||||
* Update the value to be returned by JS_CStringsAreUTF8(). Once set, it
|
||||
* can never be changed. This API must be called before the first call to
|
||||
* JS_NewRuntime.
|
||||
*/
|
||||
JS_PUBLIC_API(void)
|
||||
JS_SetCStringsAreUTF8();
|
||||
JS_SetCStringsAreUTF8(void);
|
||||
|
||||
/*
|
||||
* Character encoding support.
|
||||
|
|
|
@ -1250,9 +1250,10 @@ array_sort(JSContext *cx, uintN argc, jsval *vp)
|
|||
goto out;
|
||||
if (!all_strings) {
|
||||
/*
|
||||
* Do not call the branch callback in the following moving loop
|
||||
* to make it fast and unroot the cached results of toString
|
||||
* invocations before the callback has a chance to run the GC.
|
||||
* We want to make the following loop fast and to unroot the
|
||||
* cached results of toString invocations before the operation
|
||||
* callback has a chance to run the GC. For this reason we do
|
||||
* not call JS_CHECK_OPERATION_LIMIT in the loop.
|
||||
*/
|
||||
i = 0;
|
||||
do {
|
||||
|
|
|
@ -232,6 +232,7 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
|||
memset(cx, 0, sizeof *cx);
|
||||
|
||||
cx->runtime = rt;
|
||||
JS_ClearOperationCallback(cx);
|
||||
cx->debugHooks = &rt->globalDebugHooks;
|
||||
#if JS_STACK_GROWTH_DIRECTION > 0
|
||||
cx->stackLimit = (jsuword)-1;
|
||||
|
@ -1309,10 +1310,26 @@ js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
|
|||
}
|
||||
|
||||
JSBool
|
||||
js_ResetOperationCounter(JSContext *cx)
|
||||
js_ResetOperationCount(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(cx->operationCounter & JSOW_BRANCH_CALLBACK);
|
||||
JSScript *script;
|
||||
|
||||
cx->operationCounter = 0;
|
||||
return !cx->branchCallback || cx->branchCallback(cx, NULL);
|
||||
JS_ASSERT(cx->operationCount <= 0);
|
||||
JS_ASSERT(cx->operationLimit > 0);
|
||||
|
||||
cx->operationCount = (int32) cx->operationLimit;
|
||||
if (cx->operationCallbackIsSet)
|
||||
return cx->operationCallback(cx);
|
||||
|
||||
if (cx->operationCallback) {
|
||||
/*
|
||||
* Invoke the deprecated branch callback. It may be called only when
|
||||
* the top-most frame is scripted or JSOPTION_NATIVE_BRANCH_CALLBACK
|
||||
* is set.
|
||||
*/
|
||||
script = cx->fp ? cx->fp->script : NULL;
|
||||
if (script || JS_HAS_OPTION(cx, JSOPTION_NATIVE_BRANCH_CALLBACK))
|
||||
return ((JSBranchCallback) cx->operationCallback)(cx, script);
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
|
|
@ -637,8 +637,11 @@ struct JSContext {
|
|||
/* JSRuntime contextList linkage. */
|
||||
JSCList links;
|
||||
|
||||
/* Counter of operations for branch callback calls. */
|
||||
uint32 operationCounter;
|
||||
/*
|
||||
* Operation count. It is declared early in the structure as a frequently
|
||||
* accessed field.
|
||||
*/
|
||||
int32 operationCount;
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
/*
|
||||
|
@ -732,10 +735,18 @@ struct JSContext {
|
|||
void *tracefp;
|
||||
#endif
|
||||
|
||||
/* Per-context optional user callbacks. */
|
||||
JSBranchCallback branchCallback;
|
||||
/* Per-context optional error reporter. */
|
||||
JSErrorReporter errorReporter;
|
||||
|
||||
/*
|
||||
* Flag indicating that the operation callback is set. When the flag is 0
|
||||
* but operationCallback is not null, operationCallback stores the branch
|
||||
* callback.
|
||||
*/
|
||||
uint32 operationCallbackIsSet : 1;
|
||||
uint32 operationLimit : 31;
|
||||
JSOperationCallback operationCallback;
|
||||
|
||||
/* Interpreter activation count. */
|
||||
uintN interpLevel;
|
||||
|
||||
|
@ -843,9 +854,6 @@ class JSAutoTempValueRooter
|
|||
#define JS_HAS_XML_OPTION(cx) ((cx)->version & JSVERSION_HAS_XML || \
|
||||
JSVERSION_NUMBER(cx) >= JSVERSION_1_6)
|
||||
|
||||
#define JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) \
|
||||
JS_HAS_OPTION(cx, JSOPTION_NATIVE_BRANCH_CALLBACK)
|
||||
|
||||
/*
|
||||
* Initialize a library-wide thread private data index, and remember that it
|
||||
* has already been done, so that it happens only once ever. Returns true on
|
||||
|
@ -1026,6 +1034,39 @@ extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
|
|||
# define JS_CHECK_STACK_SIZE(cx, lval) ((jsuword)&(lval) > (cx)->stackLimit)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Update the operation counter according to the given weight and call the
|
||||
* operation callback when we reach the operation limit. To make this
|
||||
* frequently executed macro faster we decrease the counter from
|
||||
* JSContext.operationLimit and compare against zero to check the limit.
|
||||
*
|
||||
* This macro can run the full GC. Return true if it is OK to continue and
|
||||
* false otherwise.
|
||||
*/
|
||||
#define JS_CHECK_OPERATION_LIMIT(cx, weight) \
|
||||
(JS_CHECK_OPERATION_WEIGHT(weight), \
|
||||
(((cx)->operationCount -= (weight)) > 0 || js_ResetOperationCount(cx)))
|
||||
|
||||
/*
|
||||
* A version of JS_CHECK_OPERATION_LIMIT that just updates the operation count
|
||||
* without calling the operation callback or any other API. This macro resets
|
||||
* the count to 0 when it becomes negative to prevent a wrap-around when the
|
||||
* macro is called repeatably.
|
||||
*/
|
||||
#define JS_COUNT_OPERATION(cx, weight) \
|
||||
((void)(JS_CHECK_OPERATION_WEIGHT(weight), \
|
||||
(cx)->operationCount = ((cx)->operationCount > 0) \
|
||||
? (cx)->operationCount - (weight) \
|
||||
: 0))
|
||||
|
||||
/*
|
||||
* The implementation of the above macros assumes that subtracting weights
|
||||
* twice from a positive number does not wrap-around INT32_MIN.
|
||||
*/
|
||||
#define JS_CHECK_OPERATION_WEIGHT(weight) \
|
||||
(JS_ASSERT((uint32) (weight) > 0), \
|
||||
JS_ASSERT((uint32) (weight) < JS_BIT(30)))
|
||||
|
||||
/* Relative operations weights. */
|
||||
#define JSOW_JUMP 1
|
||||
#define JSOW_ALLOCATION 100
|
||||
|
@ -1034,43 +1075,15 @@ extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
|
|||
#define JSOW_SET_PROPERTY 20
|
||||
#define JSOW_NEW_PROPERTY 200
|
||||
#define JSOW_DELETE_PROPERTY 30
|
||||
|
||||
#define JSOW_BRANCH_CALLBACK JS_BIT(12)
|
||||
#define JSOW_ENTER_SHARP 4096
|
||||
#define JSOW_SCRIPT_JUMP 4096
|
||||
|
||||
/*
|
||||
* The implementation of JS_COUNT_OPERATION macro below assumes that
|
||||
* JSOW_BRANCH_CALLBACK is a power of two to ensures that an unsigned int
|
||||
* overflow does not bring the counter below JSOW_BRANCH_CALLBACK limit.
|
||||
*/
|
||||
JS_STATIC_ASSERT((JSOW_BRANCH_CALLBACK & (JSOW_BRANCH_CALLBACK - 1)) == 0);
|
||||
|
||||
/*
|
||||
* Update the operation counter according the specified weight. This macro
|
||||
* does not call the branch callback or any API.
|
||||
*/
|
||||
#define JS_COUNT_OPERATION(cx, weight) \
|
||||
((void)(JS_ASSERT((weight) > 0), \
|
||||
JS_ASSERT((weight) <= JSOW_BRANCH_CALLBACK), \
|
||||
(cx)->operationCounter = (((cx)->operationCounter + (weight)) | \
|
||||
(~(JSOW_BRANCH_CALLBACK - 1) & \
|
||||
(cx)->operationCounter))))
|
||||
|
||||
/*
|
||||
* Update the operation counter and call the branch callback when it reaches
|
||||
* JSOW_BRANCH_CALLBACK limit. This macro can run the full GC. Return true if
|
||||
* it is OK to continue and false otherwise.
|
||||
*/
|
||||
#define JS_CHECK_OPERATION_LIMIT(cx, weight) \
|
||||
(JS_COUNT_OPERATION(cx, weight), \
|
||||
((cx)->operationCounter < JSOW_BRANCH_CALLBACK || \
|
||||
js_ResetOperationCounter(cx)))
|
||||
|
||||
/*
|
||||
* Reset the operation counter and call branch callback assuming that the
|
||||
* Reset the operation count and call the operation callback assuming that the
|
||||
* operation limit is reached.
|
||||
*/
|
||||
extern JSBool
|
||||
js_ResetOperationCounter(JSContext *cx);
|
||||
js_ResetOperationCount(JSContext *cx);
|
||||
|
||||
JS_END_EXTERN_C
|
||||
|
||||
|
|
|
@ -268,7 +268,8 @@ typedef struct JSGCStats {
|
|||
#endif
|
||||
uint32 alloc; /* number of allocation attempts */
|
||||
uint32 retry; /* allocation attempt retries after running the GC */
|
||||
uint32 retryhalt; /* allocation retries halted by the branch callback */
|
||||
uint32 retryhalt; /* allocation retries halted by the operation
|
||||
callback */
|
||||
uint32 fail; /* allocation failures */
|
||||
uint32 finalfail; /* finalizer calls allocator failures */
|
||||
uint32 lockborn; /* things born locked */
|
||||
|
|
|
@ -1983,9 +1983,10 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
|
|||
*/
|
||||
#define CHECK_BRANCH(len) \
|
||||
JS_BEGIN_MACRO \
|
||||
if (len <= 0 && cx->branchCallback) { \
|
||||
if (len <= 0 && (cx->operationCount -= JSOW_SCRIPT_JUMP) <= 0) { \
|
||||
SAVE_SP_AND_PC(fp); \
|
||||
if (!(ok = cx->branchCallback(cx, script))) \
|
||||
ok = js_ResetOperationCount(cx); \
|
||||
if (!ok) \
|
||||
goto out; \
|
||||
} \
|
||||
JS_END_MACRO
|
||||
|
@ -2323,9 +2324,6 @@ interrupt:
|
|||
depth = (jsint) script->depth;
|
||||
atoms = script->atomMap.vector;
|
||||
pc = fp->pc;
|
||||
#if !JS_THREADED_INTERP
|
||||
endpc = script->code + script->length;
|
||||
#endif
|
||||
|
||||
/* Store the generating pc for the return value. */
|
||||
vp[-depth] = (jsval)pc;
|
||||
|
@ -3773,9 +3771,6 @@ interrupt:
|
|||
/* Push the frame and set interpreter registers. */
|
||||
cx->fp = fp = &newifp->frame;
|
||||
pc = script->code;
|
||||
#if !JS_THREADED_INTERP
|
||||
endpc = pc + script->length;
|
||||
#endif
|
||||
inlineCallCount++;
|
||||
JS_RUNTIME_METER(rt, inlineCalls);
|
||||
|
||||
|
|
|
@ -920,7 +920,7 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
|
|||
}
|
||||
|
||||
/*
|
||||
* An error, silent termination by branch callback or an exception.
|
||||
* An error, silent termination by operation callback or an exception.
|
||||
* Propagate the condition to the caller.
|
||||
*/
|
||||
return JS_FALSE;
|
||||
|
|
|
@ -558,11 +558,8 @@ js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
|
|||
char buf[20];
|
||||
size_t len;
|
||||
|
||||
if (JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) &&
|
||||
cx->branchCallback &&
|
||||
!cx->branchCallback(cx, NULL)) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_ENTER_SHARP))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set to null in case we return an early error. */
|
||||
*sp = NULL;
|
||||
|
|
|
@ -641,6 +641,12 @@ typedef JSBool
|
|||
typedef void
|
||||
(* JS_DLL_CALLBACK JSTraceDataOp)(JSTracer *trc, void *data);
|
||||
|
||||
typedef JSBool
|
||||
(* JS_DLL_CALLBACK JSOperationCallback)(JSContext *cx);
|
||||
|
||||
/*
|
||||
* Deprecated form of JSOperationCallback.
|
||||
*/
|
||||
typedef JSBool
|
||||
(* JS_DLL_CALLBACK JSBranchCallback)(JSContext *cx, JSScript *script);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче