Bug 462525 - username truncation code is unnecessarily duplicated in nsLoginManagerP...
[wine-gecko.git] / dom / src / threads / nsDOMWorkerTimeout.cpp
blobd04f561ae794b42eb8b6bd6b038d36c5b048b851
1 /* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is worker threads.
17 * The Initial Developer of the Original Code is
18 * Mozilla Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2008
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Ben Turner <bent.mozilla@gmail.com> (Original Author)
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsDOMWorkerTimeout.h"
41 // Interfaces
42 #include "nsIJSContextStack.h"
43 #include "nsIJSRuntimeService.h"
44 #include "nsITimer.h"
45 #include "nsIXPConnect.h"
47 // Other includes
48 #include "nsContentUtils.h"
49 #include "nsJSUtils.h"
50 #include "pratom.h"
52 // DOMWorker includes
53 #include "nsDOMThreadService.h"
55 #define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
57 #define CONSTRUCTOR_ENSURE_TRUE(_cond, _rv) \
58 PR_BEGIN_MACRO \
59 if (NS_UNLIKELY(!(_cond))) { \
60 NS_WARNING("CONSTRUCTOR_ENSURE_TRUE(" #_cond ") failed"); \
61 (_rv) = NS_ERROR_FAILURE; \
62 return; \
63 } \
64 PR_END_MACRO
66 #define SUSPEND_SPINLOCK_COUNT 5000
68 static const char* kSetIntervalStr = "setInterval";
69 static const char* kSetTimeoutStr = "setTimeout";
71 nsDOMWorkerTimeout::FunctionCallback::FunctionCallback(PRUint32 aArgc,
72 jsval* aArgv,
73 nsresult* aRv)
74 : mCallback(nsnull),
75 mCallbackArgs(nsnull),
76 mCallbackArgsLength(0)
78 MOZ_COUNT_CTOR(nsDOMWorkerTimeout::FunctionCallback);
80 JSRuntime* rt;
81 *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
82 NS_ENSURE_SUCCESS(*aRv,);
84 PRBool success = JS_AddNamedRootRT(rt, &mCallback,
85 "nsDOMWorkerTimeout Callback Object");
86 CONSTRUCTOR_ENSURE_TRUE(success, *aRv);
88 mCallback = aArgv[0];
90 // We want enough space for an extra lateness arg.
91 mCallbackArgsLength = aArgc > 2 ? aArgc - 1 : 1;
93 mCallbackArgs = new jsval[mCallbackArgsLength];
94 if (NS_UNLIKELY(!mCallbackArgs)) {
95 // Reset this!
96 mCallbackArgsLength = 0;
98 NS_ERROR("Out of memory!");
99 *aRv = NS_ERROR_OUT_OF_MEMORY;
100 return;
103 for (PRUint32 i = 0; i < mCallbackArgsLength - 1; i++) {
104 mCallbackArgs[i] = aArgv[i + 2];
105 success = JS_AddNamedRootRT(rt, &mCallbackArgs[i],
106 "nsDOMWorkerTimeout Callback Arg");
107 if (NS_UNLIKELY(!success)) {
108 // Set this to i so that the destructor only unroots the right number of
109 // values.
110 mCallbackArgsLength = i;
112 NS_WARNING("Failed to add root!");
113 *aRv = NS_ERROR_FAILURE;
114 return;
118 // Take care of the last arg.
119 mCallbackArgs[mCallbackArgsLength - 1] = 0;
120 success = JS_AddNamedRootRT(rt, &mCallbackArgs[mCallbackArgsLength - 1],
121 "nsDOMWorkerTimeout Callback Final Arg");
122 if (NS_UNLIKELY(!success)) {
123 // Decrement this so that the destructor only unroots the right number of
124 // values.
125 mCallbackArgsLength -= 1;
127 NS_WARNING("Failed to add root!");
128 *aRv = NS_ERROR_FAILURE;
129 return;
132 *aRv = NS_OK;
135 nsDOMWorkerTimeout::FunctionCallback::~FunctionCallback()
137 MOZ_COUNT_DTOR(nsDOMWorkerTimeout::FunctionCallback);
139 if (mCallback) {
140 JSRuntime* rt;
141 nsresult rv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
143 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Can't unroot callback objects!");
145 if (NS_SUCCEEDED(rv)) {
146 for (PRUint32 i = 0; i < mCallbackArgsLength; i++) {
147 JS_RemoveRootRT(rt, &mCallbackArgs[i]);
149 JS_RemoveRootRT(rt, &mCallback);
153 delete [] mCallbackArgs;
156 nsresult
157 nsDOMWorkerTimeout::FunctionCallback::Run(nsDOMWorkerTimeout* aTimeout,
158 JSContext* aCx)
160 PRInt32 lateness = PR_MAX(0, PRInt32(PR_Now() - aTimeout->mTargetTime)) /
161 (PRTime)PR_USEC_PER_MSEC;
162 mCallbackArgs[mCallbackArgsLength - 1] = INT_TO_JSVAL(lateness);
164 JSObject* global = JS_GetGlobalObject(aCx);
165 NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
167 jsval rval;
168 PRBool success =
169 JS_CallFunctionValue(aCx, global, mCallback, mCallbackArgsLength,
170 mCallbackArgs, &rval);
171 NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
173 return NS_OK;
176 nsDOMWorkerTimeout::ExpressionCallback::ExpressionCallback(PRUint32 aArgc,
177 jsval* aArgv,
178 JSContext* aCx,
179 nsresult* aRv)
180 : mExpression(nsnull),
181 mLineNumber(0)
183 MOZ_COUNT_CTOR(nsDOMWorkerTimeout::ExpressionCallback);
185 JSString* expr = JS_ValueToString(aCx, aArgv[0]);
186 *aRv = expr ? NS_OK : NS_ERROR_FAILURE;
187 NS_ENSURE_SUCCESS(*aRv,);
189 JSRuntime* rt;
190 *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
191 NS_ENSURE_SUCCESS(*aRv,);
193 PRBool success = JS_AddNamedRootRT(rt, &mExpression,
194 "nsDOMWorkerTimeout Expression");
195 CONSTRUCTOR_ENSURE_TRUE(success, *aRv);
197 mExpression = expr;
199 // Get the calling location.
200 const char* fileName;
201 PRUint32 lineNumber;
202 if (nsJSUtils::GetCallingLocation(aCx, &fileName, &lineNumber, nsnull)) {
203 CopyUTF8toUTF16(nsDependentCString(fileName), mFileName);
204 mLineNumber = lineNumber;
207 *aRv = NS_OK;
210 nsDOMWorkerTimeout::ExpressionCallback::~ExpressionCallback()
212 MOZ_COUNT_DTOR(nsDOMWorkerTimeout::ExpressionCallback);
214 if (mExpression) {
215 JSRuntime* rt;
216 nsresult rv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
218 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Can't unroot callback objects!");
220 if (NS_SUCCEEDED(rv)) {
221 JS_RemoveRootRT(rt, &mExpression);
226 nsresult
227 nsDOMWorkerTimeout::ExpressionCallback::Run(nsDOMWorkerTimeout* aTimeout,
228 JSContext* aCx)
230 NS_ERROR("Not yet implemented!");
231 return NS_ERROR_NOT_IMPLEMENTED;
234 nsDOMWorkerTimeout::nsDOMWorkerTimeout(nsDOMWorkerThread* aWorker,
235 PRUint32 aId)
236 : mWorker(aWorker),
237 mInterval(0),
238 mIsInterval(PR_FALSE),
239 mId(aId),
240 mSuspendSpinlock(0),
241 mIsSuspended(PR_FALSE),
242 mSuspendInterval(0)
243 #ifdef DEBUG
244 , mFiredOrCanceled(PR_FALSE)
245 #endif
247 MOZ_COUNT_CTOR(nsDOMWorkerTimeout);
248 NS_ASSERTION(mWorker, "Need a worker here!");
251 nsDOMWorkerTimeout::~nsDOMWorkerTimeout()
253 MOZ_COUNT_DTOR(nsDOMWorkerTimeout);
255 // If we have a timer then we assume we added ourselves to the thread's list.
256 if (mTimer) {
257 NS_ASSERTION(mFiredOrCanceled || mWorker->IsCanceled(),
258 "Timeout should have fired or been canceled!");
260 mWorker->RemoveTimeout(this);
264 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerTimeout, nsITimerCallback)
266 nsresult
267 nsDOMWorkerTimeout::Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv,
268 PRBool aIsInterval)
270 NS_ASSERTION(aCx, "Null pointer!");
271 NS_ASSERTION(aArgv, "Null pointer!");
273 JSAutoRequest ar(aCx);
275 if (!aArgc) {
276 JS_ReportError(aCx, "Function %s requires at least 1 parameter",
277 aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
278 return NS_ERROR_INVALID_ARG;
281 PRUint32 interval;
282 if (aArgc > 1) {
283 if (!JS_ValueToECMAUint32(aCx, aArgv[1], (uint32*)&interval)) {
284 JS_ReportError(aCx, "Second argument to %s must be a millisecond value",
285 aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
286 return NS_ERROR_INVALID_ARG;
289 else {
290 // If no interval was specified, treat this like a timeout, to avoid
291 // setting an interval of 0 milliseconds.
292 aIsInterval = PR_FALSE;
295 mInterval = interval;
297 mTargetTime = PR_Now() + interval * (PRTime)PR_USEC_PER_MSEC;
299 nsresult rv;
300 switch (JS_TypeOfValue(aCx, aArgv[0])) {
301 case JSTYPE_FUNCTION:
302 mCallback = new FunctionCallback(aArgc, aArgv, &rv);
303 NS_ENSURE_TRUE(mCallback, NS_ERROR_OUT_OF_MEMORY);
304 NS_ENSURE_SUCCESS(rv, rv);
306 break;
308 case JSTYPE_STRING:
309 case JSTYPE_OBJECT:
310 mCallback = new ExpressionCallback(aArgc, aArgv, aCx, &rv);
311 NS_ENSURE_TRUE(mCallback, NS_ERROR_OUT_OF_MEMORY);
312 NS_ENSURE_SUCCESS(rv, rv);
313 break;
315 default:
316 JS_ReportError(aCx, "useless %s call (missing quotes around argument?)",
317 aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
319 // Return an error that nsGlobalWindow can recognize and turn into NS_OK.
320 return NS_ERROR_INVALID_ARG;
323 PRInt32 type;
324 if (aIsInterval) {
325 type = nsITimer::TYPE_REPEATING_SLACK;
327 else {
328 type = nsITimer::TYPE_ONE_SHOT;
330 mIsInterval = aIsInterval;
332 nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
333 NS_ENSURE_SUCCESS(rv, rv);
335 nsIEventTarget* target =
336 static_cast<nsIEventTarget*>(nsDOMThreadService::get());
338 rv = timer->SetTarget(target);
339 NS_ENSURE_SUCCESS(rv, rv);
341 rv = timer->InitWithCallback(this, interval, type);
342 NS_ENSURE_SUCCESS(rv, rv);
344 mTimer.swap(timer);
346 if (!mWorker->AddTimeout(this)) {
347 // Must have been canceled.
348 mTimer->Cancel();
349 mTimer = nsnull;
350 return NS_ERROR_ABORT;
353 return NS_OK;
356 nsresult
357 nsDOMWorkerTimeout::Run()
359 NS_ENSURE_TRUE(mCallback, NS_ERROR_NOT_INITIALIZED);
360 LOG(("Worker [0x%p] running timeout [0x%p] with id %u",
361 static_cast<void*>(mWorker.get()), static_cast<void*>(this), mId));
363 #ifdef DEBUG
364 mFiredOrCanceled = PR_TRUE;
365 #endif
367 JSContext* cx;
368 nsresult rv =
369 nsDOMThreadService::ThreadJSContextStack()->GetSafeJSContext(&cx);
370 NS_ENSURE_SUCCESS(rv, rv);
372 JSAutoRequest ar(cx);
374 rv = mCallback->Run(this, cx);
376 // Make sure any pending exceptions are converted to errors for the pool.
377 JS_ReportPendingException(cx);
379 if (mIsInterval) {
380 mTargetTime = PR_Now() + mInterval * (PRTime)PR_USEC_PER_MSEC;
383 return rv;
386 void
387 nsDOMWorkerTimeout::Cancel()
389 NS_ASSERTION(mTimer, "Impossible to get here without a timer!");
391 LOG(("Worker [0x%p] canceling timeout [0x%p] with id %u",
392 static_cast<void*>(mWorker.get()), static_cast<void*>(this), mId));
394 #ifdef DEBUG
395 mFiredOrCanceled = PR_TRUE;
396 #endif
399 AutoSpinlock lock(this);
401 if (IsSuspendedNoLock()) {
402 mIsSuspended = PR_FALSE;
403 // This should kill us when all is said and done.
404 mSuspendedRef = nsnull;
408 // This call to Cancel should kill us.
409 mTimer->Cancel();
412 void
413 nsDOMWorkerTimeout::Suspend(PRTime aNow)
415 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
416 NS_ASSERTION(mTimer, "Impossible to get here without a timer!");
418 AutoSpinlock lock(this);
420 if (!mIsSuspended) {
421 mIsSuspended = PR_TRUE;
422 mSuspendedRef = this;
425 mTimer->Cancel();
427 mSuspendInterval = PR_MAX(0, PRInt32(mTargetTime - aNow)) /
428 (PRTime)PR_USEC_PER_MSEC;
430 LOG(("Worker [0x%p] suspending timeout [0x%p] with id %u (interval = %u)",
431 static_cast<void*>(mWorker.get()), static_cast<void*>(this), mId,
432 mSuspendInterval));
435 void
436 nsDOMWorkerTimeout::Resume(PRTime aNow)
438 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
439 NS_ASSERTION(mTimer, "Impossible to get here without a timer!");
441 LOG(("Worker [0x%p] resuming timeout [0x%p] with id %u",
442 static_cast<void*>(mWorker.get()), static_cast<void*>(this), mId));
444 AutoSpinlock lock(this);
446 NS_ASSERTION(IsSuspendedNoLock(), "Should be suspended!");
448 mTargetTime = aNow + mSuspendInterval * (PRTime)PR_USEC_PER_MSEC;
450 #ifdef DEBUG
451 nsresult rv =
452 #endif
453 mTimer->InitWithCallback(this, mSuspendInterval, nsITimer::TYPE_ONE_SHOT);
454 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to init timer!");
457 void
458 nsDOMWorkerTimeout::AcquireSpinlock()
460 PRUint32 loopCount = 0;
461 while (PR_AtomicSet(&mSuspendSpinlock, 1) == 1) {
462 if (++loopCount > SUSPEND_SPINLOCK_COUNT) {
463 LOG(("AcquireSpinlock taking too long (looped %u times), yielding.",
464 loopCount));
465 loopCount = 0;
466 PR_Sleep(PR_INTERVAL_NO_WAIT);
469 #ifdef PR_LOGGING
470 if (loopCount) {
471 LOG(("AcquireSpinlock needed %u loops", loopCount));
473 #endif
476 void
477 nsDOMWorkerTimeout::ReleaseSpinlock()
479 #ifdef DEBUG
480 PRInt32 suspended =
481 #endif
482 PR_AtomicSet(&mSuspendSpinlock, 0);
483 NS_ASSERTION(suspended == 1, "Huh?!");
486 NS_IMETHODIMP
487 nsDOMWorkerTimeout::Notify(nsITimer* aTimer)
489 // Should be on the timer thread.
490 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
491 NS_ASSERTION(aTimer == mTimer, "Wrong timer?!");
493 PRUint32 type;
494 nsresult rv = aTimer->GetType(&type);
495 NS_ENSURE_SUCCESS(rv, rv);
497 // We only care about one-shot timers here because that may be the one that
498 // we set from Resume().
499 if (type == nsITimer::TYPE_ONE_SHOT) {
500 AutoSpinlock lock(this);
501 if (mIsSuspended) {
502 if (mIsInterval) {
503 //LOG(("Timeout [0x%p] resuming normal interval (%u) with id %u",
504 //static_cast<void*>(this), mInterval, mId));
506 // This is the first fire since we resumed. Set our interval back to the
507 // real interval.
508 mTargetTime = PR_Now() + mInterval * (PRTime)PR_USEC_PER_MSEC;
510 rv = aTimer->InitWithCallback(this, mInterval,
511 nsITimer::TYPE_REPEATING_SLACK);
512 NS_ENSURE_SUCCESS(rv, rv);
515 mIsSuspended = PR_FALSE;
516 mSuspendedRef = nsnull;
520 nsDOMThreadService::get()->TimeoutReady(this);
521 return NS_OK;