diff options
-rw-r--r-- | chrome/renderer/render_thread.h | 6 | ||||
-rw-r--r-- | content/common/web_database_observer_impl.cc | 19 | ||||
-rw-r--r-- | content/common/web_database_observer_impl.h | 9 | ||||
-rw-r--r-- | webkit/database/database_connections.cc | 54 | ||||
-rw-r--r-- | webkit/database/database_connections.h | 36 | ||||
-rw-r--r-- | webkit/database/database_connections_unittest.cc | 79 | ||||
-rw-r--r-- | webkit/support/simple_database_system.cc | 257 | ||||
-rw-r--r-- | webkit/support/simple_database_system.h | 73 | ||||
-rw-r--r-- | webkit/tools/test_shell/test_shell.gypi | 1 |
9 files changed, 393 insertions, 141 deletions
diff --git a/chrome/renderer/render_thread.h b/chrome/renderer/render_thread.h index c2e78e6..2d9f051 100644 --- a/chrome/renderer/render_thread.h +++ b/chrome/renderer/render_thread.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -329,13 +329,15 @@ class RenderThread : public RenderThreadBase, scoped_ptr<RendererHistogramSnapshots> histogram_snapshots_; scoped_ptr<RendererWebKitClientImpl> webkit_client_; scoped_ptr<WebKit::WebStorageEventDispatcher> dom_storage_event_dispatcher_; - scoped_ptr<WebDatabaseObserverImpl> web_database_observer_impl_; scoped_ptr<SpellCheck> spellchecker_; // Used on the renderer and IPC threads. scoped_refptr<DBMessageFilter> db_message_filter_; scoped_refptr<CookieMessageFilter> cookie_message_filter_; + // Used on multiple script execution context threads. + scoped_ptr<WebDatabaseObserverImpl> web_database_observer_impl_; + #if defined(OS_POSIX) scoped_refptr<IPC::ChannelProxy::MessageFilter> suicide_on_channel_error_filter_; diff --git a/content/common/web_database_observer_impl.cc b/content/common/web_database_observer_impl.cc index 37c3e2c..be3c8ca 100644 --- a/content/common/web_database_observer_impl.cc +++ b/content/common/web_database_observer_impl.cc @@ -4,8 +4,6 @@ #include "content/common/web_database_observer_impl.h" -#include "base/auto_reset.h" -#include "base/message_loop.h" #include "base/string16.h" #include "content/common/database_messages.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDatabase.h" @@ -14,17 +12,20 @@ WebDatabaseObserverImpl::WebDatabaseObserverImpl( IPC::Message::Sender* sender) : sender_(sender), - waiting_for_dbs_to_close_(false) { + open_connections_(new webkit_database::DatabaseConnectionsWrapper) { +} + +WebDatabaseObserverImpl::~WebDatabaseObserverImpl() { } void WebDatabaseObserverImpl::databaseOpened( const WebKit::WebDatabase& database) { string16 origin_identifier = database.securityOrigin().databaseIdentifier(); string16 database_name = database.name(); + open_connections_->AddOpenConnection(origin_identifier, database_name); sender_->Send(new DatabaseHostMsg_Opened( origin_identifier, database_name, database.displayName(), database.estimatedSize())); - database_connections_.AddConnection(origin_identifier, database_name); } void WebDatabaseObserverImpl::databaseModified( @@ -39,15 +40,9 @@ void WebDatabaseObserverImpl::databaseClosed( string16 database_name = database.name(); sender_->Send(new DatabaseHostMsg_Closed( origin_identifier, database_name)); - database_connections_.RemoveConnection(origin_identifier, database_name); - if (waiting_for_dbs_to_close_ && database_connections_.IsEmpty()) - MessageLoop::current()->Quit(); + open_connections_->RemoveOpenConnection(origin_identifier, database_name); } void WebDatabaseObserverImpl::WaitForAllDatabasesToClose() { - if (!database_connections_.IsEmpty()) { - AutoReset<bool> waiting_for_dbs_auto_reset(&waiting_for_dbs_to_close_, true); - MessageLoop::ScopedNestableTaskAllower nestable(MessageLoop::current()); - MessageLoop::current()->Run(); - } + open_connections_->WaitForAllDatabasesToClose(); } diff --git a/content/common/web_database_observer_impl.h b/content/common/web_database_observer_impl.h index 442a7af..a79ee6f 100644 --- a/content/common/web_database_observer_impl.h +++ b/content/common/web_database_observer_impl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -6,6 +6,7 @@ #define CONTENT_COMMON_WEB_DATABASE_OBSERVER_IMPL_H_ #pragma once +#include "base/memory/ref_counted.h" #include "ipc/ipc_message.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDatabaseObserver.h" #include "webkit/database/database_connections.h" @@ -13,7 +14,8 @@ class WebDatabaseObserverImpl : public WebKit::WebDatabaseObserver { public: explicit WebDatabaseObserverImpl(IPC::Message::Sender* sender); - virtual ~WebDatabaseObserverImpl() {} + virtual ~WebDatabaseObserverImpl(); + virtual void databaseOpened(const WebKit::WebDatabase& database); virtual void databaseModified(const WebKit::WebDatabase& database); virtual void databaseClosed(const WebKit::WebDatabase& database); @@ -22,8 +24,7 @@ class WebDatabaseObserverImpl : public WebKit::WebDatabaseObserver { private: IPC::Message::Sender* sender_; - bool waiting_for_dbs_to_close_; - webkit_database::DatabaseConnections database_connections_; + scoped_refptr<webkit_database::DatabaseConnectionsWrapper> open_connections_; }; #endif // CONTENT_COMMON_WEB_DATABASE_OBSERVER_IMPL_H_ diff --git a/webkit/database/database_connections.cc b/webkit/database/database_connections.cc index 05efb1e..612f73b4 100644 --- a/webkit/database/database_connections.cc +++ b/webkit/database/database_connections.cc @@ -1,10 +1,13 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. #include "webkit/database/database_connections.h" +#include "base/auto_reset.h" #include "base/logging.h" +#include "base/message_loop.h" +#include "base/message_loop_proxy.h" namespace webkit_database { @@ -84,4 +87,53 @@ void DatabaseConnections::RemoveConnectionsHelper( } } +DatabaseConnectionsWrapper::DatabaseConnectionsWrapper() + : waiting_for_dbs_to_close_(false), + main_thread_(base::MessageLoopProxy::CreateForCurrentThread()) { +} + +DatabaseConnectionsWrapper::~DatabaseConnectionsWrapper() { +} + +void DatabaseConnectionsWrapper::WaitForAllDatabasesToClose() { + // We assume that new databases won't be open while we're waiting. + DCHECK(main_thread_->BelongsToCurrentThread()); + if (HasOpenConnections()) { + AutoReset<bool> auto_reset(&waiting_for_dbs_to_close_, true); + MessageLoop::ScopedNestableTaskAllower nestable(MessageLoop::current()); + MessageLoop::current()->Run(); + } +} + +bool DatabaseConnectionsWrapper::HasOpenConnections() { + DCHECK(main_thread_->BelongsToCurrentThread()); + base::AutoLock auto_lock(open_connections_lock_); + return !open_connections_.IsEmpty(); +} + +void DatabaseConnectionsWrapper::AddOpenConnection( + const string16& origin_identifier, + const string16& database_name) { + // We add to the collection immediately on any thread. + base::AutoLock auto_lock(open_connections_lock_); + open_connections_.AddConnection(origin_identifier, database_name); +} + +void DatabaseConnectionsWrapper::RemoveOpenConnection( + const string16& origin_identifier, + const string16& database_name) { + // But only remove from the collection on the main thread + // so we can handle the waiting_for_dbs_to_close_ case. + if (!main_thread_->BelongsToCurrentThread()) { + main_thread_->PostTask(FROM_HERE, NewRunnableMethod( + this, &DatabaseConnectionsWrapper::RemoveOpenConnection, + origin_identifier, database_name)); + return; + } + base::AutoLock auto_lock(open_connections_lock_); + open_connections_.RemoveConnection(origin_identifier, database_name); + if (waiting_for_dbs_to_close_ && open_connections_.IsEmpty()) + MessageLoop::current()->Quit(); +} + } // namespace webkit_database diff --git a/webkit/database/database_connections.h b/webkit/database/database_connections.h index 433bb3f..6a594b2 100644 --- a/webkit/database/database_connections.h +++ b/webkit/database/database_connections.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -8,7 +8,13 @@ #include <map> #include <vector> +#include "base/memory/ref_counted.h" #include "base/string16.h" +#include "base/synchronization/lock.h" + +namespace base { +class MessageLoopProxy; +} namespace webkit_database { @@ -40,6 +46,34 @@ class DatabaseConnections { int num_connections); }; +// A wrapper class that provides thread-safety and the +// ability to wait until all connections have closed. +// Intended for use in renderer processes. +class DatabaseConnectionsWrapper + : public base::RefCountedThreadSafe<DatabaseConnectionsWrapper> { + public: + DatabaseConnectionsWrapper(); + + // The Wait and Has methods should only be called on the + // main thread (the thread on which the wrapper is constructed). + void WaitForAllDatabasesToClose(); + bool HasOpenConnections(); + + // Add and Remove may be called on any thread. + void AddOpenConnection(const string16& origin_identifier, + const string16& database_name); + void RemoveOpenConnection(const string16& origin_identifier, + const string16& database_name); + private: + ~DatabaseConnectionsWrapper(); + friend class base::RefCountedThreadSafe<DatabaseConnectionsWrapper>; + + bool waiting_for_dbs_to_close_; + base::Lock open_connections_lock_; + DatabaseConnections open_connections_; + scoped_refptr<base::MessageLoopProxy> main_thread_; +}; + } // namespace webkit_database #endif // WEBKIT_DATABASE_DATABASE_CONNECTIONS_H_ diff --git a/webkit/database/database_connections_unittest.cc b/webkit/database/database_connections_unittest.cc new file mode 100644 index 0000000..1b3ef34 --- /dev/null +++ b/webkit/database/database_connections_unittest.cc @@ -0,0 +1,79 @@ +// 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. + +#include "base/message_loop.h" +#include "base/task.h" +#include "base/threading/thread.h" +#include "base/utf_string_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/database/database_connections.h" + +namespace webkit_database { + +namespace { + +void RemoveConnectionTask( + const string16& origin_id, const string16& database_name, + scoped_refptr<DatabaseConnectionsWrapper> obj, + bool* did_task_execute) { + *did_task_execute = true; + obj->RemoveOpenConnection(origin_id, database_name); +} + +void ScheduleRemoveConnectionTask( + base::Thread* thread, const string16& origin_id, + const string16& database_name, + scoped_refptr<DatabaseConnectionsWrapper> obj, + bool* did_task_execute) { + thread->message_loop()->PostTask(FROM_HERE, + NewRunnableFunction(RemoveConnectionTask, + origin_id, database_name, obj, did_task_execute)); +} + +} // anonymous namespace + +TEST(DatabaseConnectionsTest, DatabaseConnectionsWrapperTest) { + string16 kOriginId(ASCIIToUTF16("origin_id")); + string16 kName(ASCIIToUTF16("database_name")); + + scoped_refptr<DatabaseConnectionsWrapper> obj( + new DatabaseConnectionsWrapper); + EXPECT_FALSE(obj->HasOpenConnections()); + obj->AddOpenConnection(kOriginId, kName); + EXPECT_TRUE(obj->HasOpenConnections()); + obj->AddOpenConnection(kOriginId, kName); + EXPECT_TRUE(obj->HasOpenConnections()); + obj->RemoveOpenConnection(kOriginId, kName); + EXPECT_TRUE(obj->HasOpenConnections()); + obj->RemoveOpenConnection(kOriginId, kName); + EXPECT_FALSE(obj->HasOpenConnections()); + obj->WaitForAllDatabasesToClose(); // should return immediately + + // Test WaitForAllDatabasesToClose with the last connection + // being removed on the current thread. + obj->AddOpenConnection(kOriginId, kName); + bool did_task_execute = false; + MessageLoop::current()->PostTask(FROM_HERE, + NewRunnableFunction(&RemoveConnectionTask, + kOriginId, kName, obj, &did_task_execute)); + obj->WaitForAllDatabasesToClose(); // should return after the task executes + EXPECT_TRUE(did_task_execute); + EXPECT_FALSE(obj->HasOpenConnections()); + + // Test WaitForAllDatabasesToClose with the last connection + // being removed on another thread. + obj->AddOpenConnection(kOriginId, kName); + base::Thread thread("WrapperTestThread"); + thread.Start(); + did_task_execute = false; + MessageLoop::current()->PostTask(FROM_HERE, + NewRunnableFunction(ScheduleRemoveConnectionTask, + &thread, kOriginId, kName, + obj, &did_task_execute)); + obj->WaitForAllDatabasesToClose(); // should return after the task executes + EXPECT_TRUE(did_task_execute); + EXPECT_FALSE(obj->HasOpenConnections()); +} + +} // namespace webkit_database diff --git a/webkit/support/simple_database_system.cc b/webkit/support/simple_database_system.cc index 6c00d6c..62bd03e 100644 --- a/webkit/support/simple_database_system.cc +++ b/webkit/support/simple_database_system.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -7,6 +7,8 @@ #include "base/auto_reset.h" #include "base/file_util.h" #include "base/message_loop.h" +#include "base/message_loop_proxy.h" +#include "base/synchronization/waitable_event.h" #include "base/threading/platform_thread.h" #include "base/utf_string_conversions.h" #include "third_party/sqlite/sqlite3.h" @@ -27,91 +29,138 @@ SimpleDatabaseSystem* SimpleDatabaseSystem::GetInstance() { } SimpleDatabaseSystem::SimpleDatabaseSystem() - : waiting_for_dbs_to_close_(false) { + : db_thread_("SimpleDBThread"), + open_connections_(new webkit_database::DatabaseConnectionsWrapper) { + DCHECK(!instance_); + instance_ = this; CHECK(temp_dir_.CreateUniqueTempDir()); db_tracker_ = new DatabaseTracker(temp_dir_.path(), false, NULL); db_tracker_->AddObserver(this); - DCHECK(!instance_); - instance_ = this; + db_thread_.Start(); + db_thread_proxy_ = db_thread_.message_loop_proxy(); } SimpleDatabaseSystem::~SimpleDatabaseSystem() { - db_tracker_->RemoveObserver(this); + base::WaitableEvent done_event(false, false); + db_thread_proxy_->PostTask(FROM_HERE, + NewRunnableMethod(this, &SimpleDatabaseSystem::ThreadCleanup, + &done_event)); + done_event.Wait(); instance_ = NULL; } +void SimpleDatabaseSystem::databaseOpened(const WebKit::WebDatabase& database) { + string16 origin_identifier = database.securityOrigin().databaseIdentifier(); + string16 database_name = database.name(); + open_connections_->AddOpenConnection(origin_identifier, database_name); + db_thread_proxy_->PostTask(FROM_HERE, + NewRunnableMethod(this, &SimpleDatabaseSystem::DatabaseOpened, + origin_identifier, + database_name, database.displayName(), + database.estimatedSize())); +} + +void SimpleDatabaseSystem::databaseModified( + const WebKit::WebDatabase& database) { + db_thread_proxy_->PostTask(FROM_HERE, + NewRunnableMethod(this, &SimpleDatabaseSystem::DatabaseModified, + database.securityOrigin().databaseIdentifier(), + database.name())); +} + +void SimpleDatabaseSystem::databaseClosed(const WebKit::WebDatabase& database) { + string16 origin_identifier = database.securityOrigin().databaseIdentifier(); + string16 database_name = database.name(); + db_thread_proxy_->PostTask(FROM_HERE, + NewRunnableMethod(this, &SimpleDatabaseSystem::DatabaseClosed, + origin_identifier, database_name)); +} + base::PlatformFile SimpleDatabaseSystem::OpenFile( const string16& vfs_file_name, int desired_flags) { - base::PlatformFile file_handle = base::kInvalidPlatformFileValue; - FilePath file_name = GetFullFilePathForVfsFile(vfs_file_name); - if (file_name.empty()) { - VfsBackend::OpenTempFileInDirectory( - db_tracker_->DatabaseDirectory(), desired_flags, &file_handle); - } else { - VfsBackend::OpenFile(file_name, desired_flags, &file_handle); - } - - return file_handle; + base::PlatformFile result = base::kInvalidPlatformFileValue; + base::WaitableEvent done_event(false, false); + db_thread_proxy_->PostTask(FROM_HERE, + NewRunnableMethod(this, &SimpleDatabaseSystem::VfsOpenFile, + vfs_file_name, desired_flags, + &result, &done_event)); + done_event.Wait(); + return result; } int SimpleDatabaseSystem::DeleteFile( const string16& vfs_file_name, bool sync_dir) { - // We try to delete the file multiple times, because that's what the default - // VFS does (apparently deleting a file can sometimes fail on Windows). - // We sleep for 10ms between retries for the same reason. - const int kNumDeleteRetries = 3; - int num_retries = 0; - int error_code = SQLITE_OK; - FilePath file_name = GetFullFilePathForVfsFile(vfs_file_name); - do { - error_code = VfsBackend::DeleteFile(file_name, sync_dir); - } while ((++num_retries < kNumDeleteRetries) && - (error_code == SQLITE_IOERR_DELETE) && - (base::PlatformThread::Sleep(10), 1)); + int result = SQLITE_OK; + base::WaitableEvent done_event(false, false); + db_thread_proxy_->PostTask(FROM_HERE, + NewRunnableMethod(this, &SimpleDatabaseSystem::VfsDeleteFile, + vfs_file_name, sync_dir, + &result, &done_event)); + done_event.Wait(); + return result; +} - return error_code; +uint32 SimpleDatabaseSystem::GetFileAttributes(const string16& vfs_file_name) { + uint32 result = 0; + base::WaitableEvent done_event(false, false); + db_thread_proxy_->PostTask(FROM_HERE, + NewRunnableMethod(this, &SimpleDatabaseSystem::VfsGetFileAttributes, + vfs_file_name, &result, &done_event)); + done_event.Wait(); + return result; } -long SimpleDatabaseSystem::GetFileAttributes(const string16& vfs_file_name) { - return VfsBackend::GetFileAttributes( - GetFullFilePathForVfsFile(vfs_file_name)); +int64 SimpleDatabaseSystem::GetFileSize(const string16& vfs_file_name) { + int64 result = 0; + base::WaitableEvent done_event(false, false); + db_thread_proxy_->PostTask(FROM_HERE, + NewRunnableMethod(this, &SimpleDatabaseSystem::VfsGetFileSize, + vfs_file_name, &result, &done_event)); + done_event.Wait(); + return result; +} + +void SimpleDatabaseSystem::ClearAllDatabases() { + open_connections_->WaitForAllDatabasesToClose(); + db_thread_proxy_->PostTask(FROM_HERE, + NewRunnableMethod(this, &SimpleDatabaseSystem::ResetTracker)); } -long long SimpleDatabaseSystem::GetFileSize(const string16& vfs_file_name) { - return VfsBackend::GetFileSize(GetFullFilePathForVfsFile(vfs_file_name)); +void SimpleDatabaseSystem::SetDatabaseQuota(int64 quota) { + if (!db_thread_proxy_->BelongsToCurrentThread()) { + db_thread_proxy_->PostTask(FROM_HERE, + NewRunnableMethod(this, &SimpleDatabaseSystem::SetDatabaseQuota, + quota)); + return; + } + db_tracker_->SetDefaultQuota(quota); } void SimpleDatabaseSystem::DatabaseOpened(const string16& origin_identifier, const string16& database_name, const string16& description, int64 estimated_size) { + DCHECK(db_thread_proxy_->BelongsToCurrentThread()); int64 database_size = 0; int64 space_available = 0; - database_connections_.AddConnection(origin_identifier, database_name); - db_tracker_->DatabaseOpened(origin_identifier, database_name, description, - estimated_size, &database_size, &space_available); - SetFullFilePathsForVfsFile(origin_identifier, database_name); - + db_tracker_->DatabaseOpened( + origin_identifier, database_name, description, + estimated_size, &database_size, &space_available); OnDatabaseSizeChanged(origin_identifier, database_name, database_size, space_available); } void SimpleDatabaseSystem::DatabaseModified(const string16& origin_identifier, const string16& database_name) { - DCHECK(database_connections_.IsDatabaseOpened( - origin_identifier, database_name)); + DCHECK(db_thread_proxy_->BelongsToCurrentThread()); db_tracker_->DatabaseModified(origin_identifier, database_name); } void SimpleDatabaseSystem::DatabaseClosed(const string16& origin_identifier, const string16& database_name) { - DCHECK(database_connections_.IsDatabaseOpened( - origin_identifier, database_name)); + DCHECK(db_thread_proxy_->BelongsToCurrentThread()); db_tracker_->DatabaseClosed(origin_identifier, database_name); - database_connections_.RemoveConnection(origin_identifier, database_name); - - if (waiting_for_dbs_to_close_ && database_connections_.IsEmpty()) - MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + open_connections_->RemoveOpenConnection(origin_identifier, database_name); } void SimpleDatabaseSystem::OnDatabaseSizeChanged( @@ -119,75 +168,95 @@ void SimpleDatabaseSystem::OnDatabaseSizeChanged( const string16& database_name, int64 database_size, int64 space_available) { - if (database_connections_.IsOriginUsed(origin_identifier)) { - WebKit::WebDatabase::updateDatabaseSize( - origin_identifier, database_name, database_size, space_available); - } + DCHECK(db_thread_proxy_->BelongsToCurrentThread()); + // We intentionally call into webkit on our background db_thread_ + // to better emulate what happens in chrome where this method is + // invoked on the background ipc thread. + WebKit::WebDatabase::updateDatabaseSize( + origin_identifier, database_name, database_size, space_available); } void SimpleDatabaseSystem::OnDatabaseScheduledForDeletion( const string16& origin_identifier, const string16& database_name) { + DCHECK(db_thread_proxy_->BelongsToCurrentThread()); + // We intentionally call into webkit on our background db_thread_ + // to better emulate what happens in chrome where this method is + // invoked on the background ipc thread. WebKit::WebDatabase::closeDatabaseImmediately( origin_identifier, database_name); } -void SimpleDatabaseSystem::databaseOpened(const WebKit::WebDatabase& database) { - DatabaseOpened(database.securityOrigin().databaseIdentifier(), - database.name(), database.displayName(), - database.estimatedSize()); -} - -void SimpleDatabaseSystem::databaseModified( - const WebKit::WebDatabase& database) { - DatabaseModified(database.securityOrigin().databaseIdentifier(), - database.name()); -} - -void SimpleDatabaseSystem::databaseClosed(const WebKit::WebDatabase& database) { - DatabaseClosed(database.securityOrigin().databaseIdentifier(), - database.name()); +void SimpleDatabaseSystem::VfsOpenFile( + const string16& vfs_file_name, int desired_flags, + base::PlatformFile* file_handle, base::WaitableEvent* done_event ) { + DCHECK(db_thread_proxy_->BelongsToCurrentThread()); + FilePath file_name = GetFullFilePathForVfsFile(vfs_file_name); + if (file_name.empty()) { + VfsBackend::OpenTempFileInDirectory( + db_tracker_->DatabaseDirectory(), desired_flags, file_handle); + } else { + VfsBackend::OpenFile(file_name, desired_flags, file_handle); + } + done_event->Signal(); } -void SimpleDatabaseSystem::ClearAllDatabases() { - // Wait for all databases to be closed. - if (!database_connections_.IsEmpty()) { - AutoReset<bool> waiting_for_dbs_auto_reset( - &waiting_for_dbs_to_close_, true); - MessageLoop::ScopedNestableTaskAllower nestable(MessageLoop::current()); - MessageLoop::current()->Run(); - } +void SimpleDatabaseSystem::VfsDeleteFile( + const string16& vfs_file_name, bool sync_dir, + int* result, base::WaitableEvent* done_event) { + DCHECK(db_thread_proxy_->BelongsToCurrentThread()); + // We try to delete the file multiple times, because that's what the default + // VFS does (apparently deleting a file can sometimes fail on Windows). + // We sleep for 10ms between retries for the same reason. + const int kNumDeleteRetries = 3; + int num_retries = 0; + *result = SQLITE_OK; + FilePath file_name = GetFullFilePathForVfsFile(vfs_file_name); + do { + *result = VfsBackend::DeleteFile(file_name, sync_dir); + } while ((++num_retries < kNumDeleteRetries) && + (*result == SQLITE_IOERR_DELETE) && + (base::PlatformThread::Sleep(10), 1)); - db_tracker_->CloseTrackerDatabaseAndClearCaches(); - file_util::Delete(db_tracker_->DatabaseDirectory(), true); - file_names_.clear(); + done_event->Signal(); } -void SimpleDatabaseSystem::SetDatabaseQuota(int64 quota) { - db_tracker_->SetDefaultQuota(quota); +void SimpleDatabaseSystem::VfsGetFileAttributes( + const string16& vfs_file_name, + uint32* result, base::WaitableEvent* done_event) { + DCHECK(db_thread_proxy_->BelongsToCurrentThread()); + *result = VfsBackend::GetFileAttributes( + GetFullFilePathForVfsFile(vfs_file_name)); + done_event->Signal(); } -void SimpleDatabaseSystem::SetFullFilePathsForVfsFile( - const string16& origin_identifier, - const string16& database_name) { - string16 vfs_file_name = origin_identifier + ASCIIToUTF16("/") + - database_name + ASCIIToUTF16("#"); - FilePath file_name = - DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_, vfs_file_name); - - base::AutoLock file_names_auto_lock(file_names_lock_); - file_names_[vfs_file_name] = file_name; - DCHECK(file_name.Extension().empty()); - file_names_[vfs_file_name + ASCIIToUTF16("-journal")] = - file_name.InsertBeforeExtensionASCII("-journal"); +void SimpleDatabaseSystem::VfsGetFileSize( + const string16& vfs_file_name, + int64* result, base::WaitableEvent* done_event) { + DCHECK(db_thread_proxy_->BelongsToCurrentThread()); + *result = VfsBackend::GetFileSize(GetFullFilePathForVfsFile(vfs_file_name)); + done_event->Signal(); } FilePath SimpleDatabaseSystem::GetFullFilePathForVfsFile( const string16& vfs_file_name) { + DCHECK(db_thread_proxy_->BelongsToCurrentThread()); if (vfs_file_name.empty()) // temp file, used for vacuuming return FilePath(); + return DatabaseUtil::GetFullFilePathForVfsFile( + db_tracker_.get(), vfs_file_name); +} + +void SimpleDatabaseSystem::ResetTracker() { + DCHECK(db_thread_proxy_->BelongsToCurrentThread()); + db_tracker_->CloseTrackerDatabaseAndClearCaches(); + file_util::Delete(db_tracker_->DatabaseDirectory(), true); +} - base::AutoLock file_names_auto_lock(file_names_lock_); - DCHECK(file_names_.find(vfs_file_name) != file_names_.end()); - return file_names_[vfs_file_name]; +void SimpleDatabaseSystem::ThreadCleanup(base::WaitableEvent* done_event) { + ResetTracker(); + db_tracker_->RemoveObserver(this); + db_tracker_ = NULL; + done_event->Signal(); } + diff --git a/webkit/support/simple_database_system.h b/webkit/support/simple_database_system.h index 63cc892..02bb66a 100644 --- a/webkit/support/simple_database_system.h +++ b/webkit/support/simple_database_system.h @@ -12,24 +12,45 @@ #include "base/platform_file.h" #include "base/string16.h" #include "base/synchronization/lock.h" +#include "base/task.h" +#include "base/threading/thread.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDatabaseObserver.h" #include "webkit/database/database_connections.h" #include "webkit/database/database_tracker.h" +namespace base { +class MessageLoopProxy; +class WaitableEvent; +} + class SimpleDatabaseSystem : public webkit_database::DatabaseTracker::Observer, public WebKit::WebDatabaseObserver { public: static SimpleDatabaseSystem* GetInstance(); + SimpleDatabaseSystem(); ~SimpleDatabaseSystem(); - // VFS functions + // WebDatabaseObserver implementation, these are called on the script + // execution context thread on which the database is opened. This may be + // the main thread or background WebWorker threads. + virtual void databaseOpened(const WebKit::WebDatabase& database); + virtual void databaseModified(const WebKit::WebDatabase& database); + virtual void databaseClosed(const WebKit::WebDatabase& database); + + // SQLite VFS related methods, these are called on webcore's + // background database threads via the WebKitClient impl. base::PlatformFile OpenFile(const string16& vfs_file_name, int desired_flags); int DeleteFile(const string16& vfs_file_name, bool sync_dir); - long GetFileAttributes(const string16& vfs_file_name); - long long GetFileSize(const string16& vfs_file_name); + uint32 GetFileAttributes(const string16& vfs_file_name); + int64 GetFileSize(const string16& vfs_file_name); - // database tracker functions + // For use by LayoutTestController, called on the main thread. + void ClearAllDatabases(); + void SetDatabaseQuota(int64 quota); + + private: + // Used by our WebDatabaseObserver impl, only called on the db_thread void DatabaseOpened(const string16& origin_identifier, const string16& database_name, const string16& description, @@ -47,38 +68,36 @@ class SimpleDatabaseSystem : public webkit_database::DatabaseTracker::Observer, virtual void OnDatabaseScheduledForDeletion(const string16& origin_identifier, const string16& database_name); - // WebDatabaseObserver implementation - virtual void databaseOpened(const WebKit::WebDatabase& database); - virtual void databaseModified(const WebKit::WebDatabase& database); - virtual void databaseClosed(const WebKit::WebDatabase& database); - - void ClearAllDatabases(); - void SetDatabaseQuota(int64 quota); + // Used by our public SQLite VFS methods, only called on the db_thread. + void VfsOpenFile(const string16& vfs_file_name, int desired_flags, + base::PlatformFile* result, base::WaitableEvent* done_event); + void VfsDeleteFile(const string16& vfs_file_name, bool sync_dir, + int* result, base::WaitableEvent* done_event); + void VfsGetFileAttributes(const string16& vfs_file_name, + uint32* result, base::WaitableEvent* done_event); + void VfsGetFileSize(const string16& vfs_file_name, + int64* result, base::WaitableEvent* done_event); - private: - // The calls that come from the database tracker run on the main thread. - // Therefore, we can only call DatabaseUtil::GetFullFilePathForVfsFile() - // on the main thread. However, the VFS calls run on the DB thread and - // they need to crack VFS file paths. To resolve this problem, we store - // a map of vfs_file_names to file_paths. The map is updated on the main - // thread on each DatabaseOpened() call that comes from the database - // tracker, and is read on the DB thread by each VFS call. - void SetFullFilePathsForVfsFile(const string16& origin_identifier, - const string16& database_name); FilePath GetFullFilePathForVfsFile(const string16& vfs_file_name); - static SimpleDatabaseSystem* instance_; - - bool waiting_for_dbs_to_close_; + void ResetTracker(); + void ThreadCleanup(base::WaitableEvent* done_event); + // Where the tracker database file and per origin database files reside. ScopedTempDir temp_dir_; + // All access to the db_tracker (except for its construction) and + // vfs operations are serialized on a background thread. + base::Thread db_thread_; + scoped_refptr<base::MessageLoopProxy> db_thread_proxy_; scoped_refptr<webkit_database::DatabaseTracker> db_tracker_; - base::Lock file_names_lock_; - base::hash_map<string16, FilePath> file_names_; + // Data members to support waiting for all connections to be closed. + scoped_refptr<webkit_database::DatabaseConnectionsWrapper> open_connections_; - webkit_database::DatabaseConnections database_connections_; + static SimpleDatabaseSystem* instance_; }; +DISABLE_RUNNABLE_METHOD_REFCOUNT(SimpleDatabaseSystem); + #endif // WEBKIT_SUPPORT_SIMPLE_DATABASE_SYSTEM_H_ diff --git a/webkit/tools/test_shell/test_shell.gypi b/webkit/tools/test_shell/test_shell.gypi index 43fc227..ddde626 100644 --- a/webkit/tools/test_shell/test_shell.gypi +++ b/webkit/tools/test_shell/test_shell.gypi @@ -370,6 +370,7 @@ '../../blob/blob_storage_controller_unittest.cc', '../../blob/blob_url_request_job_unittest.cc', '../../blob/deletable_file_reference_unittest.cc', + '../../database/database_connections_unittest.cc', '../../database/databases_table_unittest.cc', '../../database/database_tracker_unittest.cc', '../../database/database_util_unittest.cc', |