Title: [122291] trunk/Source/WebCore
Revision
122291
Author
[email protected]
Date
2012-07-10 21:24:22 -0700 (Tue, 10 Jul 2012)

Log Message

IndexedDB: Ensure transaction abort events are deterministic in multiprocess ports
https://bugs.webkit.org/show_bug.cgi?id=90412

Reviewed by Tony Chang.

In multi-process ports (e.g. Chromium), transaction aborts triggered on the front-end could
be initiated while a "success" event was in-flight from the back end. This would lead to
apparently flaky behavior when requests would sometimes report success and sometimes report
an error. Address this by having front-end triggered aborts do the abort steps immediately,
then send the async abort request to the back end.

No new tests - behavior in single process ports (and DRT) covered by existing
tests. Will enable currently disabled Chromium tests to be enabled (crbug.com/83226).

* Modules/indexeddb/IDBRequest.cpp:
(WebCore::IDBRequest::IDBRequest): Initialize a new m_requestAborted flag, used to prevent
dispatching if an in-flight request comes in after the abort.
(WebCore::IDBRequest::abort): Set flag to prevent double dispatching.
(WebCore::IDBRequest::onError): Handle aborted-then-received-event case.
(WebCore::IDBRequest::onSuccess): Ditto.
(WebCore::IDBRequest::onSuccessWithContinuation): Ditto.
(WebCore::IDBRequest::dispatchEvent): On uncaught error, trigger abort on transaction front-end.
* Modules/indexeddb/IDBRequest.h:
(IDBRequest):
* Modules/indexeddb/IDBTransaction.cpp:
(WebCore::IDBTransaction::abort): Do abort steps locally first, then notify back-end.
(WebCore::IDBTransaction::onAbort): If abort wasn't triggered locally, clean up is still necessary.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (122290 => 122291)


--- trunk/Source/WebCore/ChangeLog	2012-07-11 03:11:30 UTC (rev 122290)
+++ trunk/Source/WebCore/ChangeLog	2012-07-11 04:24:22 UTC (rev 122291)
@@ -1,3 +1,33 @@
+2012-07-10  Joshua Bell  <[email protected]>
+
+        IndexedDB: Ensure transaction abort events are deterministic in multiprocess ports
+        https://bugs.webkit.org/show_bug.cgi?id=90412
+
+        Reviewed by Tony Chang.
+
+        In multi-process ports (e.g. Chromium), transaction aborts triggered on the front-end could 
+        be initiated while a "success" event was in-flight from the back end. This would lead to 
+        apparently flaky behavior when requests would sometimes report success and sometimes report
+        an error. Address this by having front-end triggered aborts do the abort steps immediately,
+        then send the async abort request to the back end.
+
+        No new tests - behavior in single process ports (and DRT) covered by existing
+        tests. Will enable currently disabled Chromium tests to be enabled (crbug.com/83226).
+
+        * Modules/indexeddb/IDBRequest.cpp:
+        (WebCore::IDBRequest::IDBRequest): Initialize a new m_requestAborted flag, used to prevent
+        dispatching if an in-flight request comes in after the abort.
+        (WebCore::IDBRequest::abort): Set flag to prevent double dispatching.
+        (WebCore::IDBRequest::onError): Handle aborted-then-received-event case.
+        (WebCore::IDBRequest::onSuccess): Ditto.
+        (WebCore::IDBRequest::onSuccessWithContinuation): Ditto.
+        (WebCore::IDBRequest::dispatchEvent): On uncaught error, trigger abort on transaction front-end.
+        * Modules/indexeddb/IDBRequest.h:
+        (IDBRequest):
+        * Modules/indexeddb/IDBTransaction.cpp:
+        (WebCore::IDBTransaction::abort): Do abort steps locally first, then notify back-end.
+        (WebCore::IDBTransaction::onAbort): If abort wasn't triggered locally, clean up is still necessary.
+
 2012-07-10  Julien Chaffraix  <[email protected]>
 
         REGRESSION(r112113): absolutely positioned INPUT boxes with a table cell containing block have a 0px height

Modified: trunk/Source/WebCore/Modules/indexeddb/IDBRequest.cpp (122290 => 122291)


--- trunk/Source/WebCore/Modules/indexeddb/IDBRequest.cpp	2012-07-11 03:11:30 UTC (rev 122290)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBRequest.cpp	2012-07-11 04:24:22 UTC (rev 122291)
@@ -57,6 +57,7 @@
     , m_source(source)
     , m_transaction(transaction)
     , m_readyState(PENDING)
+    , m_requestAborted(false)
     , m_requestFinished(false)
     , m_cursorFinished(false)
     , m_contextStopped(false)
@@ -166,6 +167,7 @@
 
 void IDBRequest::abort()
 {
+    ASSERT(!m_requestAborted);
     if (m_contextStopped || !scriptExecutionContext())
         return;
 
@@ -186,6 +188,7 @@
     m_errorMessage = String();
     m_result.clear();
     onError(IDBDatabaseError::create(IDBDatabaseException::IDB_ABORT_ERR, "The transaction was aborted, so the request cannot be fulfilled."));
+    m_requestAborted = true;
 }
 
 void IDBRequest::setCursorDetails(IDBCursorBackendInterface::CursorType cursorType, IDBCursor::Direction direction)
@@ -210,6 +213,8 @@
 
 void IDBRequest::onError(PassRefPtr<IDBDatabaseError> error)
 {
+    if (m_requestAborted)
+        return;
     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result);
     m_errorCode = error->code();
     ASSERT(!m_error);
@@ -227,6 +232,8 @@
 void IDBRequest::onSuccess(PassRefPtr<DOMStringList> domStringList)
 {
     IDB_TRACE("IDBRequest::onSuccess(DOMStringList)");
+    if (m_requestAborted)
+        return;
     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
     m_result = IDBAny::create(domStringList);
     enqueueEvent(createSuccessEvent());
@@ -235,6 +242,8 @@
 void IDBRequest::onSuccess(PassRefPtr<IDBCursorBackendInterface> backend)
 {
     IDB_TRACE("IDBRequest::onSuccess(IDBCursor)");
+    if (m_requestAborted)
+        return;
     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
     ASSERT(m_cursorType != IDBCursorBackendInterface::InvalidCursorType);
     RefPtr<IDBCursor> cursor;
@@ -250,6 +259,8 @@
 void IDBRequest::onSuccess(PassRefPtr<IDBDatabaseBackendInterface> backend)
 {
     IDB_TRACE("IDBRequest::onSuccess(IDBDatabase)");
+    if (m_requestAborted)
+        return;
     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
     if (m_contextStopped || !scriptExecutionContext())
         return;
@@ -264,6 +275,8 @@
 void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey)
 {
     IDB_TRACE("IDBRequest::onSuccess(IDBKey)");
+    if (m_requestAborted)
+        return;
     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
     if (idbKey && idbKey->isValid())
         m_result = IDBAny::create(idbKey);
@@ -275,6 +288,8 @@
 void IDBRequest::onSuccess(PassRefPtr<IDBTransactionBackendInterface> prpBackend)
 {
     IDB_TRACE("IDBRequest::onSuccess(IDBTransaction)");
+    if (m_requestAborted)
+        return;
     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
     RefPtr<IDBTransactionBackendInterface> backend = prpBackend;
 
@@ -297,6 +312,8 @@
 void IDBRequest::onSuccess(PassRefPtr<SerializedScriptValue> serializedScriptValue)
 {
     IDB_TRACE("IDBRequest::onSuccess(SerializedScriptValue)");
+    if (m_requestAborted)
+        return;
     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
     m_result = IDBAny::create(serializedScriptValue);
     m_cursor.clear();
@@ -307,6 +324,8 @@
 void IDBRequest::onSuccess(PassRefPtr<SerializedScriptValue> prpSerializedScriptValue, PassRefPtr<IDBKey> prpPrimaryKey, const IDBKeyPath& keyPath)
 {
     LOG_ERROR("CHECKING: onSuccess(value, key, keypath)");
+    if (m_requestAborted)
+        return;
     RefPtr<SerializedScriptValue> serializedScriptValue = prpSerializedScriptValue;
 #ifndef NDEBUG
     // FIXME: Assert until we can actually inject the right value.
@@ -321,6 +340,8 @@
 void IDBRequest::onSuccessWithContinuation()
 {
     IDB_TRACE("IDBRequest::onSuccessWithContinuation");
+    if (m_requestAborted)
+        return;
     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
     ASSERT(m_cursor);
     setResultCursor(m_cursor, m_cursorType);
@@ -421,7 +442,7 @@
         // If an error event and the default wasn't prevented...
         if (dontPreventDefault && event->type() == eventNames().errorEvent) {
             m_transaction->setError(m_error);
-            m_transaction->backend()->abort();
+            m_transaction->abort();
         }
         m_transaction->backend()->didCompleteTaskEvents();
     }

Modified: trunk/Source/WebCore/Modules/indexeddb/IDBRequest.h (122290 => 122291)


--- trunk/Source/WebCore/Modules/indexeddb/IDBRequest.h	2012-07-11 03:11:30 UTC (rev 122290)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBRequest.h	2012-07-11 04:24:22 UTC (rev 122291)
@@ -128,6 +128,7 @@
     RefPtr<IDBTransaction> m_transaction;
 
     ReadyState m_readyState;
+    bool m_requestAborted; // May be aborted by transaction then receive async onsuccess; ignore vs. assert.
     bool m_requestFinished; // Is it possible that we'll fire any more events? If not, we're finished.
     bool m_cursorFinished;
     bool m_contextStopped;

Modified: trunk/Source/WebCore/Modules/indexeddb/IDBTransaction.cpp (122290 => 122291)


--- trunk/Source/WebCore/Modules/indexeddb/IDBTransaction.cpp	2012-07-11 03:11:30 UTC (rev 122290)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBTransaction.cpp	2012-07-11 04:24:22 UTC (rev 122291)
@@ -213,6 +213,13 @@
         return;
     m_state = Finishing;
     m_active = false;
+
+    while (!m_requestList.isEmpty()) {
+        IDBRequest* request = *m_requestList.begin();
+        m_requestList.remove(request);
+        request->abort();
+    }
+
     RefPtr<IDBTransaction> selfRef = this;
     if (m_backend)
         m_backend->abort();
@@ -267,11 +274,16 @@
 void IDBTransaction::onAbort()
 {
     ASSERT(m_state != Finished);
-    m_state = Finishing;
-    while (!m_requestList.isEmpty()) {
-        IDBRequest* request = *m_requestList.begin();
-        m_requestList.remove(request);
-        request->abort();
+
+    if (m_state != Finishing) {
+        // Abort was not triggered by front-end, so outstanding requests must
+        // be aborted now.
+        while (!m_requestList.isEmpty()) {
+            IDBRequest* request = *m_requestList.begin();
+            m_requestList.remove(request);
+            request->abort();
+        }
+        m_state = Finishing;
     }
 
     if (isVersionChange()) {
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to