diff options
author | hans@chromium.org <hans@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-02 11:13:18 +0000 |
---|---|---|
committer | hans@chromium.org <hans@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-02 11:13:18 +0000 |
commit | 77931ff3077f06bbf4dd5c47ba59c7b4e1d28b02 (patch) | |
tree | f5c8ce66b649844130f4683528ab8a0b61879307 | |
parent | 5c6b568ce633302f64e45991648176b757b7134c (diff) | |
download | chromium_src-77931ff3077f06bbf4dd5c47ba59c7b4e1d28b02.zip chromium_src-77931ff3077f06bbf4dd5c47ba59c7b4e1d28b02.tar.gz chromium_src-77931ff3077f06bbf4dd5c47ba59c7b4e1d28b02.tar.bz2 |
IndexedDB: Cursor pre-fetching.
Add support for cursor pre-fetching.
BUG=88651
TEST=browser_tests
Review URL: http://codereview.chromium.org/8662017
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@112675 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/test/data/indexeddb/common.js | 5 | ||||
-rw-r--r-- | chrome/test/data/indexeddb/cursor_prefetch.html | 11 | ||||
-rw-r--r-- | chrome/test/data/indexeddb/cursor_prefetch.js | 394 | ||||
-rw-r--r-- | content/browser/in_process_webkit/indexed_db_browsertest.cc | 4 | ||||
-rw-r--r-- | content/browser/in_process_webkit/indexed_db_callbacks.cc | 25 | ||||
-rw-r--r-- | content/browser/in_process_webkit/indexed_db_callbacks.h | 4 | ||||
-rw-r--r-- | content/browser/in_process_webkit/indexed_db_dispatcher_host.cc | 28 | ||||
-rw-r--r-- | content/browser/in_process_webkit/indexed_db_dispatcher_host.h | 6 | ||||
-rw-r--r-- | content/common/indexed_db_messages.h | 19 | ||||
-rw-r--r-- | content/renderer/indexed_db_dispatcher.cc | 67 | ||||
-rw-r--r-- | content/renderer/indexed_db_dispatcher.h | 18 | ||||
-rw-r--r-- | content/renderer/renderer_webidbcursor_impl.cc | 100 | ||||
-rw-r--r-- | content/renderer/renderer_webidbcursor_impl.h | 31 |
13 files changed, 710 insertions, 2 deletions
diff --git a/chrome/test/data/indexeddb/common.js b/chrome/test/data/indexeddb/common.js index 5470fec..69665d0 100644 --- a/chrome/test/data/indexeddb/common.js +++ b/chrome/test/data/indexeddb/common.js @@ -4,7 +4,10 @@ function debug(message) { - document.getElementById('status').innerHTML += '<br/>' + message; + var span = document.createElement("span"); + span.appendChild(document.createTextNode(message)); + span.appendChild(document.createElement("br")); + document.getElementById('status').appendChild(span); } function done(message) diff --git a/chrome/test/data/indexeddb/cursor_prefetch.html b/chrome/test/data/indexeddb/cursor_prefetch.html new file mode 100644 index 0000000..d0728ee --- /dev/null +++ b/chrome/test/data/indexeddb/cursor_prefetch.html @@ -0,0 +1,11 @@ +<html> + <head> + <title>IndexedDB cursor prefetch test</title> + <script type="text/javascript" src="common.js"></script> + <script type="text/javascript" src="cursor_prefetch.js"></script> + </head> + <body onLoad="test()"> + <div id="status">Starting...</div> + </body> +</html> + diff --git a/chrome/test/data/indexeddb/cursor_prefetch.js b/chrome/test/data/indexeddb/cursor_prefetch.js new file mode 100644 index 0000000..927461f --- /dev/null +++ b/chrome/test/data/indexeddb/cursor_prefetch.js @@ -0,0 +1,394 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// These constants should match the ones in renderer_webidbcursor_impl.h +// to make sure the test hits the right code paths. +var kPrefetchThreshold = 2; +var kMinPrefetchAmount = 5; + +var kNumberOfItems = 200; + +function test() { + indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB; + IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction + IDBCursor = window.IDBCursor || window.webkitIDBCursor; + IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange; + + request = indexedDB.open('cursor-prefetch'); + request.onsuccess = openSuccess; + request.onerror = unexpectedErrorCallback; +} + +function openSuccess() { + window.db = event.target.result; + + request = db.setVersion('new version'); + request.onsuccess = setVersionSuccess; + request.onerror = unexpectedErrorCallback; +} + +function setVersionSuccess() { + debug("setVersionSuccess():"); + window.trans = event.target.result; + shouldBeTrue("trans !== null"); + trans.onabort = unexpectedAbortCallback; + trans.oncomplete = fillObjectStore; + + deleteAllObjectStores(db); + var store = db.createObjectStore('store'); + store.createIndex('index', ''); +} + +function fillObjectStore() { + debug("fillObjectStore()"); + var trans = db.transaction(['store'], IDBTransaction.READ_WRITE); + trans.onabort = unexpectedAbortCallback; + trans.oncomplete = firstTest; + + var store = trans.objectStore('store'); + debug("Storing " + kNumberOfItems + " object in the object store."); + for (var i = 0; i < kNumberOfItems; ++i) { + var req = store.put(i, i); + req.onerror = unexpectedErrorCallback; + } + + // Let the transaction finish. +} + +function firstTest() { + debug("firstTest()"); + + // Test iterating straight through the object store. + + var trans = db.transaction(['store'], IDBTransaction.READ_WRITE); + trans.onabort = unexpectedAbortCallback; + trans.oncomplete = secondTest; + + var store = trans.objectStore('store'); + var cursorReq = store.openCursor(); + cursorReq.onerror = unexpectedErrorCallback; + + count = 0; + cursorReq.onsuccess = function() { + cursor = event.target.result; + if (cursor === null) { + shouldBe("count", "kNumberOfItems"); + return; // Let the transaction finish. + } + + if (cursor.key !== count) + shouldBe("cursor.key", "count"); + if (cursor.value !== count) + shouldBe("cursor.value", "count"); + + ++count; + + cursor.continue(); + } +} + +function secondTest() { + debug("secondTest()"); + + // Test iterating through the object store, intermixed with + // continue calls to specific keys. + + var trans = db.transaction(['store'], IDBTransaction.READ_WRITE); + trans.onabort = unexpectedAbortCallback; + trans.oncomplete = thirdTest; + + var store = trans.objectStore('store'); + var cursorReq = store.openCursor(); + cursorReq.onerror = unexpectedErrorCallback; + + var jumpTable = [{from: 5, to: 17}, + {from: 25, to: 30}, + {from: 31, to: 35}, + {from: 70, to: 80}, + {from: 98, to: 99}]; + + count = 0; + expectedKey = 0; + + cursorReq.onsuccess = function() { + cursor = event.target.result; + if (cursor === null) { + debug("Finished iterating after " + count + " steps."); + return; // Let the transaction finish. + } + + if (cursor.key !== expectedKey) + shouldBe("cursor.key", "expectedKey"); + if (cursor.value !== expectedKey) + shouldBe("cursor.value", "expectedKey"); + + ++count; + + for (var i = 0; i < jumpTable.length; ++i) { + if (jumpTable[i].from === cursor.key) { + expectedKey = jumpTable[i].to; + debug("Jumping from "+ cursor.key + " to " + expectedKey); + cursor.continue(expectedKey); + return; + } + } + + ++expectedKey; + cursor.continue(); + } +} + +function thirdTest() { + debug("thirdTest()"); + + // Test iterating straight through the object store in reverse. + + var trans = db.transaction(['store'], IDBTransaction.READ_WRITE); + trans.onabort = unexpectedAbortCallback; + trans.oncomplete = fourthTest; + + var store = trans.objectStore('store'); + var cursorReq = store.openCursor( + IDBKeyRange.upperBound(kNumberOfItems-1), IDBCursor.PREV); + cursorReq.onerror = unexpectedErrorCallback; + + count = 0; + cursorReq.onsuccess = function() { + cursor = event.target.result; + if (cursor === null) { + shouldBe("count", "kNumberOfItems"); + return; // Let the transaction finish. + } + + expectedKey = kNumberOfItems - count - 1; + + if (cursor.key !== expectedKey) + shouldBe("cursor.key", "expectedKey"); + if (cursor.value !== expectedKey) + shouldBe("cursor.value", "expectedKey"); + + ++count; + + cursor.continue(); + } +} + +function fourthTest() { + debug("fourthTest()"); + + // Test iterating, and then stopping before reaching the end. + // Make sure transaction terminates anyway. + + var trans = db.transaction(['store'], IDBTransaction.READ_WRITE); + trans.onabort = unexpectedAbortCallback; + trans.oncomplete = function() { + debug("fourthTest() transaction completed"); + fifthTest(); + } + + var store = trans.objectStore('store'); + var cursorReq = store.openCursor(); + cursorReq.onerror = unexpectedErrorCallback; + + count = 0; + cursorReq.onsuccess = function() { + cursor = event.target.result; + + if (cursor.key !== count) + shouldBe("cursor.key", "count"); + if (cursor.value !== count) + shouldBe("cursor.value", "count"); + + ++count; + + if (count === 25) { + // Schedule some other request. + var otherReq = store.get(42); + otherReq.onerror = unexpectedErrorCallback; + otherReq.onsuccess = function() { + if (count === 25) { + debug("Other request fired before continue, as expected."); + } else { + debug("Other request fired out-of-order!"); + fail(); + } + } + + cursor.continue(); + return; + } + + if (count === 30) { + // Do a continue first, then another request. + cursor.continue(); + + var otherReq = store.get(42); + otherReq.onerror = unexpectedErrorCallback; + otherReq.onsuccess = function() { + if (count === 31) { + debug("Other request fired right after continue as expected."); + } else { + debug("Other request didn't fire right after continue as expected."); + fail(); + } + } + + return; + } + + if (count === 75) { + return; // Sudden stop. + } + + cursor.continue(); + } +} + +function fifthTest() { + debug("fifthTest()"); + + // Test iterating over the pre-fetch threshold, but make sure the + // cursor is positioned so that it is actually at the last element + // in the range when pre-fetch fires, and make sure a null cursor + // is the result as expected. + + var trans = db.transaction(['store'], IDBTransaction.READ_WRITE); + trans.onabort = unexpectedAbortCallback; + trans.oncomplete = sixthTest; + + var store = trans.objectStore('store'); + + var startKey = kNumberOfItems - 1 - kPrefetchThreshold; + var cursorReq = store.openCursor(IDBKeyRange.lowerBound(startKey)); + cursorReq.onerror = unexpectedErrorCallback; + + count = 0; + cursorReq.onsuccess = function() { + cursor = event.target.result; + + if (cursor === null) { + debug("cursor is null"); + shouldBe("count", "kPrefetchThreshold + 1"); + return; + } + + debug("count: " + count); + ++count; + cursor.continue(); + } +} + +function sixthTest() { + debug("sixthTest()"); + + // Test stepping two cursors simultaneously. First cursor1 steps + // for a while, then cursor2, then back to cursor1, etc. + + var trans = db.transaction(['store'], IDBTransaction.READ_WRITE); + trans.onabort = unexpectedAbortCallback; + trans.oncomplete = seventhTest; + var store = trans.objectStore('store'); + + cursor1 = null; + cursor2 = null; + + count1 = 0; + count2 = 0; + + var cursor1func = function() { + var cursor = event.target.result; + if (cursor === null) { + shouldBe("count1", "kNumberOfItems"); + cursor2.continue(); + return; + } + + if (cursor1 === null) { + cursor1 = cursor; + } + + if (cursor1.key !== count1) + shouldBe("cursor1.key", "count1"); + if (cursor1.value !== count1) + shouldBe("cursor1.value", "count1"); + + ++count1; + + if (count1 % 20 === 0) { + if (cursor2 !== null) { + cursor2.continue(); + } else { + var req = store.openCursor(); + req.onerror = unexpectedErrorCallback; + req.onsuccess = cursor2func; + } + } else { + cursor1.continue(); + } + } + + var cursor2func = function() { + var cursor = event.target.result; + if (cursor === null) { + shouldBe("count2", "kNumberOfItems"); + return; + } + + if (cursor2 === null) { + cursor2 = cursor; + } + + if (cursor2.key !== count2) + shouldBe("cursor2.key", "count2"); + if (cursor2.value !== count2) + shouldBe("cursor2.value", "count2"); + + ++count2; + + if (count2 % 20 === 0) { + cursor1.continue(); + } else { + cursor2.continue(); + } + } + + var req = store.openCursor(); + req.onerror = unexpectedErrorCallback; + req.onsuccess = cursor1func; +} + +function seventhTest() { + debug("seventhTest()"); + + // Test iterating straight through an index. + + var trans = db.transaction(['store'], IDBTransaction.READ_WRITE); + trans.onabort = unexpectedAbortCallback; + trans.oncomplete = done; + + var store = trans.objectStore('store'); + var index = store.index('index'); + + var cursorReq = index.openCursor(); + cursorReq.onerror = unexpectedErrorCallback; + count = 0; + + cursorReq.onsuccess = function() { + cursor = event.target.result; + if (cursor === null) { + shouldBe("count", "kNumberOfItems"); + return; + } + + if (cursor.key !== count) + shouldBe("cursor.key", "count"); + if (cursor.primaryKey !== count) + shouldBe("cursor.primaryKey", "count"); + if (cursor.value !== count) + shouldBe("cursor.value", "count"); + + ++count; + cursor.continue(); + } +} diff --git a/content/browser/in_process_webkit/indexed_db_browsertest.cc b/content/browser/in_process_webkit/indexed_db_browsertest.cc index c849a18..759efda 100644 --- a/content/browser/in_process_webkit/indexed_db_browsertest.cc +++ b/content/browser/in_process_webkit/indexed_db_browsertest.cc @@ -69,6 +69,10 @@ IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTestIncognito) { true /* incognito */); } +IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorPrefetch) { + SimpleTest(testUrl(FilePath(FILE_PATH_LITERAL("cursor_prefetch.html")))); +} + // Flaky: http://crbug.com/70773 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DISABLED_IndexTest) { SimpleTest(testUrl(FilePath(FILE_PATH_LITERAL("index_test.html")))); diff --git a/content/browser/in_process_webkit/indexed_db_callbacks.cc b/content/browser/in_process_webkit/indexed_db_callbacks.cc index 70e79ef..969a430 100644 --- a/content/browser/in_process_webkit/indexed_db_callbacks.cc +++ b/content/browser/in_process_webkit/indexed_db_callbacks.cc @@ -59,6 +59,31 @@ void IndexedDBCallbacks<WebKit::WebIDBCursor>::onSuccessWithContinuation() { content::SerializedScriptValue(idb_cursor->value()))); } +void IndexedDBCallbacks<WebKit::WebIDBCursor>::onSuccessWithPrefetch( + const WebKit::WebVector<WebKit::WebIDBKey>& keys, + const WebKit::WebVector<WebKit::WebIDBKey>& primaryKeys, + const WebKit::WebVector<WebKit::WebSerializedScriptValue>& values) { + DCHECK(cursor_id_ != -1); + + std::vector<IndexedDBKey> msgKeys; + std::vector<IndexedDBKey> msgPrimaryKeys; + std::vector<content::SerializedScriptValue> msgValues; + + for (size_t i = 0; i < keys.size(); ++i) { + msgKeys.push_back(IndexedDBKey(keys[i])); + msgPrimaryKeys.push_back(IndexedDBKey(primaryKeys[i])); + msgValues.push_back(content::SerializedScriptValue(values[i])); + } + + dispatcher_host()->Send( + new IndexedDBMsg_CallbacksSuccessCursorPrefetch( + response_id(), + cursor_id_, + msgKeys, + msgPrimaryKeys, + msgValues)); +} + void IndexedDBCallbacks<WebKit::WebIDBKey>::onSuccess( const WebKit::WebIDBKey& value) { dispatcher_host()->Send( diff --git a/content/browser/in_process_webkit/indexed_db_callbacks.h b/content/browser/in_process_webkit/indexed_db_callbacks.h index 085bfa4..de9edaf 100644 --- a/content/browser/in_process_webkit/indexed_db_callbacks.h +++ b/content/browser/in_process_webkit/indexed_db_callbacks.h @@ -93,6 +93,10 @@ class IndexedDBCallbacks<WebKit::WebIDBCursor> virtual void onSuccess(WebKit::WebIDBCursor* idb_object); virtual void onSuccess(const WebKit::WebSerializedScriptValue& value); virtual void onSuccessWithContinuation(); + virtual void onSuccessWithPrefetch( + const WebKit::WebVector<WebKit::WebIDBKey>& keys, + const WebKit::WebVector<WebKit::WebIDBKey>& primaryKeys, + const WebKit::WebVector<WebKit::WebSerializedScriptValue>& values); private: // The id of the cursor this callback concerns, or -1 if the cursor diff --git a/content/browser/in_process_webkit/indexed_db_dispatcher_host.cc b/content/browser/in_process_webkit/indexed_db_dispatcher_host.cc index e4abb51..f521667 100644 --- a/content/browser/in_process_webkit/indexed_db_dispatcher_host.cc +++ b/content/browser/in_process_webkit/indexed_db_dispatcher_host.cc @@ -886,6 +886,8 @@ bool IndexedDBDispatcherHost::CursorDispatcherHost::OnMessageReceived( IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorDirection, OnDirection) IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorUpdate, OnUpdate) IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorContinue, OnContinue) + IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetch, OnPrefetch) + IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetchReset, OnPrefetchReset) IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorDelete, OnDelete) IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorDestroyed, OnDestroyed) IPC_MESSAGE_UNHANDLED(handled = false) @@ -967,6 +969,32 @@ void IndexedDBDispatcherHost::CursorDispatcherHost::OnContinue( cursor_id), *ec); } +void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetch( + int32 cursor_id, + int32 response_id, + int n, + WebKit::WebExceptionCode* ec) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT)); + WebIDBCursor* idb_cursor = parent_->GetOrTerminateProcess(&map_, cursor_id); + if (!idb_cursor) + return; + + *ec = 0; + idb_cursor->prefetchContinue( + n, new IndexedDBCallbacks<WebIDBCursor>(parent_, response_id, + cursor_id), *ec); +} + +void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetchReset( + int32 cursor_id, int used_prefetches, int unused_prefetches) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT)); + WebIDBCursor* idb_cursor = parent_->GetOrTerminateProcess(&map_, cursor_id); + if (!idb_cursor) + return; + + idb_cursor->prefetchReset(used_prefetches, unused_prefetches); +} + void IndexedDBDispatcherHost::CursorDispatcherHost::OnDelete( int32 cursor_id, int32 response_id, diff --git a/content/browser/in_process_webkit/indexed_db_dispatcher_host.h b/content/browser/in_process_webkit/indexed_db_dispatcher_host.h index 4ee03b9..88bff96 100644 --- a/content/browser/in_process_webkit/indexed_db_dispatcher_host.h +++ b/content/browser/in_process_webkit/indexed_db_dispatcher_host.h @@ -238,6 +238,12 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter { int32 response_id, const IndexedDBKey& key, WebKit::WebExceptionCode* ec); + void OnPrefetch(int32 idb_cursor_id, + int32 response_id, + int n, + WebKit::WebExceptionCode* ec); + void OnPrefetchReset(int32 idb_cursor_id, int used_prefetches, + int unused_prefetches); void OnDelete(int32 idb_object_store_id, int32 response_id, WebKit::WebExceptionCode* ec); diff --git a/content/common/indexed_db_messages.h b/content/common/indexed_db_messages.h index 118106e..31a0974 100644 --- a/content/common/indexed_db_messages.h +++ b/content/common/indexed_db_messages.h @@ -147,6 +147,12 @@ IPC_MESSAGE_CONTROL5(IndexedDBMsg_CallbacksSuccessCursorContinue, IndexedDBKey /* key */, IndexedDBKey /* primary key */, content::SerializedScriptValue /* script_value */) +IPC_MESSAGE_CONTROL5(IndexedDBMsg_CallbacksSuccessCursorPrefetch, + int32 /* response_id */, + int32 /* cursor_id */, + std::vector<IndexedDBKey> /* keys */, + std::vector<IndexedDBKey> /* primary keys */, + std::vector<content::SerializedScriptValue> /* values */) IPC_MESSAGE_CONTROL2(IndexedDBMsg_CallbacksSuccessIDBDatabase, int32 /* response_id */, int32 /* idb_database_id */) @@ -200,6 +206,19 @@ IPC_SYNC_MESSAGE_CONTROL3_1(IndexedDBHostMsg_CursorContinue, IndexedDBKey, /* key */ WebKit::WebExceptionCode /* ec */) +// WebIDBCursor::prefetchContinue() message. +IPC_SYNC_MESSAGE_CONTROL3_1(IndexedDBHostMsg_CursorPrefetch, + int32, /* idb_cursor_id */ + int32, /* response_id */ + int32, /* n */ + WebKit::WebExceptionCode /* ec */) + +// WebIDBCursor::prefetchReset() message. +IPC_SYNC_MESSAGE_CONTROL3_0(IndexedDBHostMsg_CursorPrefetchReset, + int32, /* idb_cursor_id */ + int32, /* used_prefetches */ + int32 /* used_prefetches */) + // WebIDBCursor::remove() message. IPC_SYNC_MESSAGE_CONTROL2_1(IndexedDBHostMsg_CursorDelete, int32, /* idb_cursor_id */ diff --git a/content/renderer/indexed_db_dispatcher.cc b/content/renderer/indexed_db_dispatcher.cc index 8b3ddda..2308e8b 100644 --- a/content/renderer/indexed_db_dispatcher.cc +++ b/content/renderer/indexed_db_dispatcher.cc @@ -44,6 +44,8 @@ bool IndexedDBDispatcher::OnMessageReceived(const IPC::Message& msg) { OnSuccessOpenCursor) IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessCursorContinue, OnSuccessCursorContinue) + IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessCursorPrefetch, + OnSuccessCursorPrefetch) IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessIDBDatabase, OnSuccessIDBDatabase) IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessIndexedDBKey, @@ -74,6 +76,7 @@ void IndexedDBDispatcher::RequestIDBCursorUpdate( WebIDBCallbacks* callbacks_ptr, int32 idb_cursor_id, WebExceptionCode* ec) { + ResetCursorPrefetchCaches(); scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); int32 response_id = pending_callbacks_.Add(callbacks.release()); @@ -88,6 +91,9 @@ void IndexedDBDispatcher::RequestIDBCursorContinue( WebIDBCallbacks* callbacks_ptr, int32 idb_cursor_id, WebExceptionCode* ec) { + // Reset all cursor prefetch caches except for this cursor. + ResetCursorPrefetchCaches(idb_cursor_id); + scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); int32 response_id = pending_callbacks_.Add(callbacks.release()); @@ -97,10 +103,32 @@ void IndexedDBDispatcher::RequestIDBCursorContinue( pending_callbacks_.Remove(response_id); } +void IndexedDBDispatcher::RequestIDBCursorPrefetch( + int n, + WebIDBCallbacks* callbacks_ptr, + int32 idb_cursor_id, + WebExceptionCode* ec) { + scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); + + int32 response_id = pending_callbacks_.Add(callbacks.release()); + RenderThreadImpl::current()->Send( + new IndexedDBHostMsg_CursorPrefetch(idb_cursor_id, response_id, n, ec)); + if (*ec) + pending_callbacks_.Remove(response_id); +} + +void IndexedDBDispatcher::RequestIDBCursorPrefetchReset( + int used_prefetches, int unused_prefetches, int32 idb_cursor_id) { + RenderThreadImpl::current()->Send( + new IndexedDBHostMsg_CursorPrefetchReset(idb_cursor_id, used_prefetches, + unused_prefetches)); +} + void IndexedDBDispatcher::RequestIDBCursorDelete( WebIDBCallbacks* callbacks_ptr, int32 idb_cursor_id, WebExceptionCode* ec) { + ResetCursorPrefetchCaches(); scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); int32 response_id = pending_callbacks_.Add(callbacks.release()); @@ -114,6 +142,7 @@ void IndexedDBDispatcher::RequestIDBFactoryOpen( WebIDBCallbacks* callbacks_ptr, const string16& origin, WebFrame* web_frame) { + ResetCursorPrefetchCaches(); scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); if (!web_frame) @@ -133,6 +162,7 @@ void IndexedDBDispatcher::RequestIDBFactoryGetDatabaseNames( WebIDBCallbacks* callbacks_ptr, const string16& origin, WebFrame* web_frame) { + ResetCursorPrefetchCaches(); scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); if (!web_frame) @@ -152,6 +182,7 @@ void IndexedDBDispatcher::RequestIDBFactoryDeleteDatabase( WebIDBCallbacks* callbacks_ptr, const string16& origin, WebFrame* web_frame) { + ResetCursorPrefetchCaches(); scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); if (!web_frame) @@ -168,6 +199,7 @@ void IndexedDBDispatcher::RequestIDBFactoryDeleteDatabase( } void IndexedDBDispatcher::RequestIDBDatabaseClose(int32 idb_database_id) { + ResetCursorPrefetchCaches(); Send(new IndexedDBHostMsg_DatabaseClose(idb_database_id)); pending_database_callbacks_.Remove(idb_database_id); } @@ -175,6 +207,7 @@ void IndexedDBDispatcher::RequestIDBDatabaseClose(int32 idb_database_id) { void IndexedDBDispatcher::RequestIDBDatabaseOpen( WebIDBDatabaseCallbacks* callbacks_ptr, int32 idb_database_id) { + ResetCursorPrefetchCaches(); scoped_ptr<WebIDBDatabaseCallbacks> callbacks(callbacks_ptr); int32 response_id = pending_database_callbacks_.Add(callbacks.release()); @@ -186,6 +219,7 @@ void IndexedDBDispatcher::RequestIDBDatabaseSetVersion( WebIDBCallbacks* callbacks_ptr, int32 idb_database_id, WebExceptionCode* ec) { + ResetCursorPrefetchCaches(); scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); int32 response_id = pending_callbacks_.Add(callbacks.release()); @@ -202,6 +236,7 @@ void IndexedDBDispatcher::RequestIDBIndexOpenObjectCursor( int32 idb_index_id, const WebIDBTransaction& transaction, WebExceptionCode* ec) { + ResetCursorPrefetchCaches(); scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); IndexedDBHostMsg_IndexOpenCursor_Params params; params.response_id = pending_callbacks_.Add(callbacks.release()); @@ -224,6 +259,7 @@ void IndexedDBDispatcher::RequestIDBIndexOpenKeyCursor( int32 idb_index_id, const WebIDBTransaction& transaction, WebExceptionCode* ec) { + ResetCursorPrefetchCaches(); scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); IndexedDBHostMsg_IndexOpenCursor_Params params; params.response_id = pending_callbacks_.Add(callbacks.release()); @@ -247,6 +283,7 @@ void IndexedDBDispatcher::RequestIDBIndexGetObject( int32 idb_index_id, const WebIDBTransaction& transaction, WebExceptionCode* ec) { + ResetCursorPrefetchCaches(); scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); int32 response_id = pending_callbacks_.Add(callbacks.release()); Send(new IndexedDBHostMsg_IndexGetObject(idb_index_id, response_id, key, @@ -261,6 +298,7 @@ void IndexedDBDispatcher::RequestIDBIndexGetKey( int32 idb_index_id, const WebIDBTransaction& transaction, WebExceptionCode* ec) { + ResetCursorPrefetchCaches(); scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); int32 response_id = pending_callbacks_.Add(callbacks.release()); Send(new IndexedDBHostMsg_IndexGetKey( @@ -276,6 +314,7 @@ void IndexedDBDispatcher::RequestIDBObjectStoreGet( int32 idb_object_store_id, const WebIDBTransaction& transaction, WebExceptionCode* ec) { + ResetCursorPrefetchCaches(); scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); int32 response_id = pending_callbacks_.Add(callbacks.release()); @@ -294,6 +333,7 @@ void IndexedDBDispatcher::RequestIDBObjectStorePut( int32 idb_object_store_id, const WebIDBTransaction& transaction, WebExceptionCode* ec) { + ResetCursorPrefetchCaches(); scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); IndexedDBHostMsg_ObjectStorePut_Params params; params.idb_object_store_id = idb_object_store_id; @@ -313,6 +353,7 @@ void IndexedDBDispatcher::RequestIDBObjectStoreDelete( int32 idb_object_store_id, const WebIDBTransaction& transaction, WebExceptionCode* ec) { + ResetCursorPrefetchCaches(); scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); int32 response_id = pending_callbacks_.Add(callbacks.release()); @@ -327,6 +368,7 @@ void IndexedDBDispatcher::RequestIDBObjectStoreClear( int32 idb_object_store_id, const WebIDBTransaction& transaction, WebExceptionCode* ec) { + ResetCursorPrefetchCaches(); scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); int32 response_id = pending_callbacks_.Add(callbacks.release()); @@ -343,6 +385,7 @@ void IndexedDBDispatcher::RequestIDBObjectStoreOpenCursor( int32 idb_object_store_id, const WebIDBTransaction& transaction, WebExceptionCode* ec) { + ResetCursorPrefetchCaches(); scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); IndexedDBHostMsg_ObjectStoreOpenCursor_Params params; params.response_id = pending_callbacks_.Add(callbacks.release()); @@ -458,6 +501,21 @@ void IndexedDBDispatcher::OnSuccessCursorContinue( pending_callbacks_.Remove(response_id); } +void IndexedDBDispatcher::OnSuccessCursorPrefetch( + int32 response_id, + int32 cursor_id, + const std::vector<IndexedDBKey>& keys, + const std::vector<IndexedDBKey>& primary_keys, + const std::vector<content::SerializedScriptValue>& values) { + RendererWebIDBCursorImpl* cursor = cursors_[cursor_id]; + DCHECK(cursor); + cursor->SetPrefetchData(keys, primary_keys, values); + + WebIDBCallbacks* callbacks = pending_callbacks_.Lookup(response_id); + cursor->CachedContinue(callbacks); + pending_callbacks_.Remove(response_id); +} + void IndexedDBDispatcher::OnBlocked(int32 response_id) { WebIDBCallbacks* callbacks = pending_callbacks_.Lookup(response_id); callbacks->onBlocked(); @@ -500,3 +558,12 @@ void IndexedDBDispatcher::OnVersionChange(int32 database_id, return; callbacks->onVersionChange(newVersion); } + +void IndexedDBDispatcher::ResetCursorPrefetchCaches(int32 exception_cursor_id) { + typedef std::map<int32, RendererWebIDBCursorImpl*>::iterator Iterator; + for (Iterator i = cursors_.begin(); i != cursors_.end(); ++i) { + if (i->first == exception_cursor_id) + continue; + i->second->ResetPrefetchCache(); + } +} diff --git a/content/renderer/indexed_db_dispatcher.h b/content/renderer/indexed_db_dispatcher.h index d2b3f1a..6f7deef 100644 --- a/content/renderer/indexed_db_dispatcher.h +++ b/content/renderer/indexed_db_dispatcher.h @@ -70,6 +70,15 @@ class IndexedDBDispatcher : public IPC::Channel::Listener { int32 idb_cursor_id, WebKit::WebExceptionCode* ec); + void RequestIDBCursorPrefetch( + int n, + WebKit::WebIDBCallbacks* callbacks_ptr, + int32 idb_cursor_id, + WebKit::WebExceptionCode* ec); + + void RequestIDBCursorPrefetchReset(int used_prefetches, int unused_prefetches, + int32 idb_cursor_id); + void RequestIDBCursorDelete( WebKit::WebIDBCallbacks* callbacks_ptr, int32 idb_cursor_id, @@ -174,6 +183,12 @@ class IndexedDBDispatcher : public IPC::Channel::Listener { const IndexedDBKey& key, const IndexedDBKey& primary_key, const content::SerializedScriptValue& value); + void OnSuccessCursorPrefetch( + int32 response_id, + int32 cursor_id, + const std::vector<IndexedDBKey>& keys, + const std::vector<IndexedDBKey>& primary_keys, + const std::vector<content::SerializedScriptValue>& values); void OnSuccessStringList(int32 response_id, const std::vector<string16>& value); void OnSuccessSerializedScriptValue( @@ -185,6 +200,9 @@ class IndexedDBDispatcher : public IPC::Channel::Listener { void OnComplete(int32 transaction_id); void OnVersionChange(int32 database_id, const string16& newVersion); + // Reset cursor prefetch caches for all cursors except exception_cursor_id. + void ResetCursorPrefetchCaches(int32 exception_cursor_id = -1); + // Careful! WebIDBCallbacks wraps non-threadsafe data types. It must be // destroyed and used on the same thread it was created on. IDMap<WebKit::WebIDBCallbacks, IDMapOwnPointer> pending_callbacks_; diff --git a/content/renderer/renderer_webidbcursor_impl.cc b/content/renderer/renderer_webidbcursor_impl.cc index 5ba7820..58113ad 100644 --- a/content/renderer/renderer_webidbcursor_impl.cc +++ b/content/renderer/renderer_webidbcursor_impl.cc @@ -14,7 +14,11 @@ using WebKit::WebIDBKey; using WebKit::WebSerializedScriptValue; RendererWebIDBCursorImpl::RendererWebIDBCursorImpl(int32 idb_cursor_id) - : idb_cursor_id_(idb_cursor_id) { + : idb_cursor_id_(idb_cursor_id), + continue_count_(0), + used_prefetches_(0), + pending_onsuccess_callbacks_(0), + prefetch_amount_(kMinPrefetchAmount) { } RendererWebIDBCursorImpl::~RendererWebIDBCursorImpl() { @@ -62,6 +66,34 @@ void RendererWebIDBCursorImpl::continueFunction(const WebIDBKey& key, WebExceptionCode& ec) { IndexedDBDispatcher* dispatcher = RenderThreadImpl::current()->indexed_db_dispatcher(); + + if (key.type() == WebIDBKey::InvalidType) { + // No key, so this would qualify for a prefetch. + ++continue_count_; + + if (!prefetch_keys_.empty()) { + // We have a prefetch cache, so serve the result from that. + CachedContinue(callbacks); + return; + } + + if (continue_count_ > kPrefetchContinueThreshold) { + // Request pre-fetch. + dispatcher->RequestIDBCursorPrefetch(prefetch_amount_, callbacks, + idb_cursor_id_, &ec); + + // Increase prefetch_amount_ exponentially. + prefetch_amount_ *= 2; + if (prefetch_amount_ > kMaxPrefetchAmount) + prefetch_amount_ = kMaxPrefetchAmount; + + return; + } + } else { + // Key argument supplied. We couldn't prefetch this. + ResetPrefetchCache(); + } + dispatcher->RequestIDBCursorContinue(IndexedDBKey(key), callbacks, idb_cursor_id_, &ec); } @@ -73,6 +105,20 @@ void RendererWebIDBCursorImpl::deleteFunction(WebIDBCallbacks* callbacks, dispatcher->RequestIDBCursorDelete(callbacks, idb_cursor_id_, &ec); } +void RendererWebIDBCursorImpl::postSuccessHandlerCallback() +{ + pending_onsuccess_callbacks_--; + + // If the onsuccess callback called continue() on the cursor again, + // and that continue was served by the prefetch cache, then + // pending_onsuccess_callbacks_ would be incremented. + // If not, it means the callback did something else, or nothing at all, + // in which case we need to reset the cache. + + if (pending_onsuccess_callbacks_ == 0) + ResetPrefetchCache(); +} + void RendererWebIDBCursorImpl::SetKeyAndValue( const IndexedDBKey& key, const IndexedDBKey& primary_key, @@ -81,3 +127,55 @@ void RendererWebIDBCursorImpl::SetKeyAndValue( primary_key_ = primary_key; value_ = value; } + +void RendererWebIDBCursorImpl::SetPrefetchData( + const std::vector<IndexedDBKey>& keys, + const std::vector<IndexedDBKey>& primary_keys, + const std::vector<content::SerializedScriptValue>& values) { + prefetch_keys_.assign(keys.begin(), keys.end()); + prefetch_primary_keys_.assign(primary_keys.begin(), primary_keys.end()); + prefetch_values_.assign(values.begin(), values.end()); + + used_prefetches_ = 0; + pending_onsuccess_callbacks_ = 0; +} + +void RendererWebIDBCursorImpl::CachedContinue( + WebKit::WebIDBCallbacks* callbacks) { + DCHECK(prefetch_keys_.size() > 0); + DCHECK(prefetch_primary_keys_.size() == prefetch_keys_.size()); + DCHECK(prefetch_values_.size() == prefetch_keys_.size()); + + key_ = prefetch_keys_.front(); + primary_key_ = prefetch_primary_keys_.front(); + value_ = prefetch_values_.front(); + + prefetch_keys_.pop_front(); + prefetch_primary_keys_.pop_front(); + prefetch_values_.pop_front(); + used_prefetches_++; + + pending_onsuccess_callbacks_++; + callbacks->onSuccessWithContinuation(); +} + +void RendererWebIDBCursorImpl::ResetPrefetchCache() { + continue_count_ = 0; + prefetch_amount_ = kMinPrefetchAmount; + + if (!prefetch_keys_.size()) { + // No prefetch cache, so no need to reset the cursor in the back-end. + return; + } + + IndexedDBDispatcher* dispatcher = + RenderThreadImpl::current()->indexed_db_dispatcher(); + dispatcher->RequestIDBCursorPrefetchReset(used_prefetches_, + prefetch_keys_.size(), + idb_cursor_id_); + prefetch_keys_.clear(); + prefetch_primary_keys_.clear(); + prefetch_values_.clear(); + + pending_onsuccess_callbacks_ = 0; +} diff --git a/content/renderer/renderer_webidbcursor_impl.h b/content/renderer/renderer_webidbcursor_impl.h index fbca6b2..b033990 100644 --- a/content/renderer/renderer_webidbcursor_impl.h +++ b/content/renderer/renderer_webidbcursor_impl.h @@ -5,6 +5,8 @@ #ifndef CONTENT_RENDERER_RENDERER_WEBIDBCURSOR_IMPL_H_ #define CONTENT_RENDERER_RENDERER_WEBIDBCURSOR_IMPL_H_ +#include <deque> + #include "base/basictypes.h" #include "content/common/indexed_db_key.h" #include "content/public/common/serialized_script_value.h" @@ -30,15 +32,44 @@ class RendererWebIDBCursorImpl : public WebKit::WebIDBCursor { WebKit::WebExceptionCode& ec); virtual void deleteFunction(WebKit::WebIDBCallbacks* callback, WebKit::WebExceptionCode& ec); + virtual void postSuccessHandlerCallback(); void SetKeyAndValue(const IndexedDBKey& key, const IndexedDBKey& primary_key, const content::SerializedScriptValue& value); + void SetPrefetchData( + const std::vector<IndexedDBKey>& keys, + const std::vector<IndexedDBKey>& primary_keys, + const std::vector<content::SerializedScriptValue>& values); + + void CachedContinue(WebKit::WebIDBCallbacks* callbacks); + void ResetPrefetchCache(); private: int32 idb_cursor_id_; IndexedDBKey key_; IndexedDBKey primary_key_; content::SerializedScriptValue value_; + + // Prefetch cache. + std::deque<IndexedDBKey> prefetch_keys_; + std::deque<IndexedDBKey> prefetch_primary_keys_; + std::deque<content::SerializedScriptValue> prefetch_values_; + + // Number of continue calls that would qualify for a pre-fetch. + int continue_count_; + + // Number of items used from the last prefetch. + int used_prefetches_; + + // Number of onsuccess handlers we are waiting for. + int pending_onsuccess_callbacks_; + + // Number of items to request in next prefetch. + int prefetch_amount_; + + enum { kPrefetchContinueThreshold = 2 }; + enum { kMinPrefetchAmount = 5 }; + enum { kMaxPrefetchAmount = 100 }; }; #endif // CONTENT_RENDERER_RENDERER_WEBIDBCURSOR_IMPL_H_ |