Remove UTF8 BOM marker from last commit.
[wine-gecko.git] / storage / src / mozStorageEvents.cpp
bloba0a52eb58c48f44e278d35967665e87ee7315b67
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=2 sts=2
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Mozilla Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 2008
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsThreadUtils.h"
41 #include "nsAutoPtr.h"
42 #include "nsAutoLock.h"
43 #include "nsCOMArray.h"
45 #include "mozIStorageStatementCallback.h"
46 #include "mozIStoragePendingStatement.h"
47 #include "mozStorageStatement.h"
48 #include "mozStorageResultSet.h"
49 #include "mozStorageRow.h"
50 #include "mozStorageBackground.h"
51 #include "mozStorageError.h"
52 #include "mozStorageEvents.h"
54 ////////////////////////////////////////////////////////////////////////////////
55 //// Asynchronous Statement Execution
57 /**
58 * Enum used to describe the state of execution.
60 enum ExecutionState {
61 PENDING = -1
62 , COMPLETED = mozIStorageStatementCallback::REASON_FINISHED
63 , CANCELED = mozIStorageStatementCallback::REASON_CANCELED
64 , ERROR = mozIStorageStatementCallback::REASON_ERROR
67 /**
68 * Interface used to cancel pending events.
70 class iCancelable : public nsISupports
72 public:
73 /**
74 * Tells an event to cancel itself.
76 virtual void cancel() = 0;
79 /**
80 * Interface used to notify of event completion.
82 class iCompletionNotifier : public nsISupports
84 public:
85 /**
86 * Called when an event is completed and no longer needs to be tracked.
88 * @param aEvent
89 * The event that has finished.
91 virtual void completed(iCancelable *aEvent) = 0;
94 /**
95 * Notifies a callback with a result set.
97 class CallbackResultNotifier : public nsIRunnable
98 , public iCancelable
100 public:
101 NS_DECL_ISUPPORTS
103 CallbackResultNotifier(mozIStorageStatementCallback *aCallback,
104 mozIStorageResultSet *aResults,
105 iCompletionNotifier *aNotifier) :
106 mCallback(aCallback)
107 , mResults(aResults)
108 , mCompletionNotifier(aNotifier)
109 , mCanceled(PR_FALSE)
113 NS_IMETHOD Run()
115 if (!mCanceled)
116 (void)mCallback->HandleResult(mResults);
118 // Notify owner AsyncExecute that we have completed
119 mCompletionNotifier->completed(this);
120 // It is likely that the completion notifier holds a reference to us as
121 // well, so we release our reference to it here to avoid cycles.
122 mCompletionNotifier = nsnull;
123 return NS_OK;
126 virtual void cancel()
128 // Atomically set our status so we know to not run.
129 PR_AtomicSet(&mCanceled, PR_TRUE);
131 private:
132 CallbackResultNotifier() { }
134 mozIStorageStatementCallback *mCallback;
135 nsCOMPtr<mozIStorageResultSet> mResults;
136 nsRefPtr<iCompletionNotifier> mCompletionNotifier;
137 PRInt32 mCanceled;
139 NS_IMPL_THREADSAFE_ISUPPORTS1(
140 CallbackResultNotifier,
141 nsIRunnable
145 * Notifies the calling thread that an error has occurred.
147 class ErrorNotifier : public nsIRunnable
148 , public iCancelable
150 public:
151 NS_DECL_ISUPPORTS
153 ErrorNotifier(mozIStorageStatementCallback *aCallback,
154 mozIStorageError *aErrorObj,
155 iCompletionNotifier *aCompletionNotifier) :
156 mCallback(aCallback)
157 , mErrorObj(aErrorObj)
158 , mCanceled(PR_FALSE)
159 , mCompletionNotifier(aCompletionNotifier)
163 NS_IMETHOD Run()
165 if (!mCanceled)
166 (void)mCallback->HandleError(mErrorObj);
168 mCompletionNotifier->completed(this);
169 // It is likely that the completion notifier holds a reference to us as
170 // well, so we release our reference to it here to avoid cycles.
171 mCompletionNotifier = nsnull;
172 return NS_OK;
175 virtual void cancel()
177 // Atomically set our status so we know to not run.
178 PR_AtomicSet(&mCanceled, PR_TRUE);
181 static inline iCancelable *Dispatch(nsIThread *aCallingThread,
182 mozIStorageStatementCallback *aCallback,
183 iCompletionNotifier *aCompletionNotifier,
184 int aResult,
185 const char *aMessage)
187 nsCOMPtr<mozIStorageError> errorObj(new mozStorageError(aResult, aMessage));
188 if (!errorObj)
189 return nsnull;
191 ErrorNotifier *notifier =
192 new ErrorNotifier(aCallback, errorObj, aCompletionNotifier);
193 (void)aCallingThread->Dispatch(notifier, NS_DISPATCH_NORMAL);
194 return notifier;
196 private:
197 ErrorNotifier() { }
199 mozIStorageStatementCallback *mCallback;
200 nsCOMPtr<mozIStorageError> mErrorObj;
201 PRInt32 mCanceled;
202 nsRefPtr<iCompletionNotifier> mCompletionNotifier;
204 NS_IMPL_THREADSAFE_ISUPPORTS1(
205 ErrorNotifier,
206 nsIRunnable
210 * Notifies the calling thread that the statement has finished executing.
212 class CompletionNotifier : public nsIRunnable
213 , public iCancelable
215 public:
216 NS_DECL_ISUPPORTS
219 * This takes ownership of the callback and statement. Both are released
220 * on the thread this is dispatched to (which should always be the calling
221 * thread).
223 CompletionNotifier(mozIStorageStatementCallback *aCallback,
224 ExecutionState aReason,
225 mozIStorageStatement *aStatement,
226 iCompletionNotifier *aCompletionNotifier) :
227 mCallback(aCallback)
228 , mReason(aReason)
229 , mStatement(aStatement)
230 , mCompletionNotifier(aCompletionNotifier)
234 NS_IMETHOD Run()
236 NS_RELEASE(mStatement);
237 if (mCallback) {
238 (void)mCallback->HandleCompletion(mReason);
239 NS_RELEASE(mCallback);
242 mCompletionNotifier->completed(this);
243 // It is likely that the completion notifier holds a reference to us as
244 // well, so we release our reference to it here to avoid cycles.
245 mCompletionNotifier = nsnull;
246 return NS_OK;
249 virtual void cancel()
251 // Update our reason so the completion notifier knows what is up.
252 mReason = CANCELED;
255 private:
256 CompletionNotifier() { }
258 mozIStorageStatementCallback *mCallback;
259 ExecutionState mReason;
260 mozIStorageStatement *mStatement;
261 nsRefPtr<iCompletionNotifier> mCompletionNotifier;
263 NS_IMPL_THREADSAFE_ISUPPORTS1(
264 CompletionNotifier,
265 nsIRunnable
269 * Executes a statement asynchronously in the background.
271 class AsyncExecute : public nsIRunnable
272 , public mozIStoragePendingStatement
273 , public iCompletionNotifier
275 public:
276 NS_DECL_ISUPPORTS
279 * This takes ownership of both the statement and the callback.
281 AsyncExecute(mozStorageStatement *aStatement,
282 mozIStorageStatementCallback *aCallback) :
283 mStatement(aStatement)
284 , mCallback(aCallback)
285 , mCallingThread(do_GetCurrentThread())
286 , mState(PENDING)
287 , mStateMutex(nsAutoLock::NewLock("AsyncExecute::mStateMutex"))
288 , mPendingEventsMutex(nsAutoLock::NewLock("AsyncExecute::mPendingEventsMutex"))
292 nsresult initialize()
294 NS_ENSURE_TRUE(mStateMutex, NS_ERROR_OUT_OF_MEMORY);
295 NS_ENSURE_TRUE(mPendingEventsMutex, NS_ERROR_OUT_OF_MEMORY);
296 NS_ADDREF(mStatement);
297 NS_IF_ADDREF(mCallback);
298 return NS_OK;
301 NS_IMETHOD Run()
303 // do not run if we have been canceled
305 nsAutoLock mutex(mStateMutex);
306 if (mState == CANCELED)
307 return Complete();
310 // Execute the statement, giving the callback results
311 // XXX better chunking of results?
312 nsresult rv;
313 while (PR_TRUE) {
314 PRBool hasResults;
315 rv = mStatement->ExecuteStep(&hasResults);
316 // Break out if we have no more results
317 if (NS_SUCCEEDED(rv) && !hasResults)
318 break;
320 // Some errors are not fatal, but we still need to report them
321 if (NS_FAILED(rv)) {
322 // Get the real result code
323 sqlite3 *db = sqlite3_db_handle(mStatement->NativeStatement());
324 int err = sqlite3_errcode(db);
325 if (err == SQLITE_BUSY) {
326 // Yield, and try again
327 PR_Sleep(PR_INTERVAL_NO_WAIT);
328 continue;
331 // Set error state
333 nsAutoLock mutex(mStateMutex);
334 mState = ERROR;
337 // Notify
338 iCancelable *cancelable = ErrorNotifier::Dispatch(
339 mCallingThread, mCallback, this, err, sqlite3_errmsg(db)
341 if (cancelable) {
342 nsAutoLock mutex(mPendingEventsMutex);
343 (void)mPendingEvents.AppendObject(cancelable);
346 // And complete
347 return Complete();
350 // Check to see if we have been canceled
352 nsAutoLock mutex(mStateMutex);
353 if (mState == CANCELED)
354 return Complete();
357 // If we do not have a callback, but are getting results, we should stop
358 // now since all this work isn't going to accomplish anything
359 if (!mCallback) {
360 nsAutoLock mutex(mStateMutex);
361 mState = COMPLETED;
362 return Complete();
365 // Build result object
366 nsRefPtr<mozStorageResultSet> results(new mozStorageResultSet());
367 if (!results)
368 break;
370 nsRefPtr<mozStorageRow> row(new mozStorageRow());
371 if (!row)
372 break;
374 rv = row->initialize(mStatement->NativeStatement());
375 if (NS_FAILED(rv))
376 break;
378 rv = results->add(row);
379 if (NS_FAILED(rv))
380 break;
382 // Notify caller
383 nsRefPtr<CallbackResultNotifier> notifier =
384 new CallbackResultNotifier(mCallback, results, this);
385 if (!notifier)
386 break;
388 nsresult status = mCallingThread->Dispatch(notifier, NS_DISPATCH_NORMAL);
389 if (NS_SUCCEEDED(status)) {
390 nsAutoLock mutex(mPendingEventsMutex);
391 (void)mPendingEvents.AppendObject(notifier);
394 if (NS_FAILED(rv)) {
395 // This is a fatal error :(
397 // Update state
399 nsAutoLock mutex(mStateMutex);
400 mState = ERROR;
403 // Notify
404 iCancelable *cancelable = ErrorNotifier::Dispatch(
405 mCallingThread, mCallback, this, mozIStorageError::ERROR, ""
407 if (cancelable) {
408 nsAutoLock mutex(mPendingEventsMutex);
409 (void)mPendingEvents.AppendObject(cancelable);
413 // No more results, so update state if needed
415 nsAutoLock mutex(mStateMutex);
416 if (mState == PENDING)
417 mState = COMPLETED;
419 // Notify about completion
420 return Complete();
424 static PRBool cancelEnumerator(iCancelable *aCancelable, void *)
426 (void)aCancelable->cancel();
427 return PR_TRUE;
430 NS_IMETHOD Cancel()
432 // Check and update our state
434 nsAutoLock mutex(mStateMutex);
435 NS_ENSURE_TRUE(mState == PENDING || mState == COMPLETED,
436 NS_ERROR_UNEXPECTED);
437 mState = CANCELED;
440 // Cancel all our pending events on the calling thread
442 nsAutoLock mutex(mPendingEventsMutex);
443 (void)mPendingEvents.EnumerateForwards(&AsyncExecute::cancelEnumerator,
444 nsnull);
445 mPendingEvents.Clear();
448 return NS_OK;
451 virtual void completed(iCancelable *aCancelable)
453 nsAutoLock mutex(mPendingEventsMutex);
454 (void)mPendingEvents.RemoveObject(aCancelable);
457 private:
458 AsyncExecute() { }
460 ~AsyncExecute()
462 NS_ASSERTION(mPendingEvents.Count() == 0, "Still pending events!");
463 nsAutoLock::DestroyLock(mStateMutex);
464 nsAutoLock::DestroyLock(mPendingEventsMutex);
468 * Notifies callback about completion, and does any necessary cleanup.
469 * @note: When calling this function, mStateMutex must be held.
471 nsresult Complete()
473 // Reset the statement
474 (void)mStatement->Reset();
476 // Notify about completion
477 NS_ASSERTION(mState != PENDING,
478 "Still in a pending state when calling Complete!");
479 nsRefPtr<CompletionNotifier> completionEvent =
480 new CompletionNotifier(mCallback, mState, mStatement, this);
481 nsresult rv = mCallingThread->Dispatch(completionEvent, NS_DISPATCH_NORMAL);
482 if (NS_SUCCEEDED(rv)) {
483 nsAutoLock mutex(mPendingEventsMutex);
484 (void)mPendingEvents.AppendObject(completionEvent);
487 // We no longer own mCallback or mStatement (the CompletionNotifier takes
488 // ownership), so null them out
489 mCallback = nsnull;
490 mStatement = nsnull;
491 return NS_OK;
494 mozStorageStatement *mStatement;
495 mozIStorageStatementCallback *mCallback;
496 nsCOMPtr<nsIThread> mCallingThread;
499 * Indicates the state the object is currently in.
501 ExecutionState mState;
504 * Mutex to protect mState.
506 PRLock *mStateMutex;
509 * Stores a list of pending events that have not yet completed on the
510 * calling thread.
512 nsCOMArray<iCancelable> mPendingEvents;
515 * Mutex to protect mPendingEvents.
517 PRLock *mPendingEventsMutex;
519 NS_IMPL_THREADSAFE_ISUPPORTS2(
520 AsyncExecute,
521 nsIRunnable,
522 mozIStoragePendingStatement
525 nsresult
526 NS_executeAsync(mozStorageStatement *aStatement,
527 mozIStorageStatementCallback *aCallback,
528 mozIStoragePendingStatement **_stmt)
530 // Create our event to run in the background
531 nsRefPtr<AsyncExecute> event(new AsyncExecute(aStatement, aCallback));
532 NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
534 nsresult rv = event->initialize();
535 NS_ENSURE_SUCCESS(rv, rv);
537 // Dispatch it to the background
538 nsIEventTarget *target = mozStorageBackground::getService()->target();
539 rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
540 NS_ENSURE_SUCCESS(rv, rv);
542 // Return it as the pending statement object
543 NS_ADDREF(*_stmt = event);
544 return NS_OK;