diff options
author | ivankr@chromium.org <ivankr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-29 14:00:12 +0000 |
---|---|---|
committer | ivankr@chromium.org <ivankr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-29 14:00:12 +0000 |
commit | 95b42e2745a2380a16112a059bd0e842d81f0c0a (patch) | |
tree | 8715285e587b94807bcc70cf135d99bdbec662fb | |
parent | 1b14a45ac508a066cc3c060dd37327c3a13a6fda (diff) | |
download | chromium_src-95b42e2745a2380a16112a059bd0e842d81f0c0a.zip chromium_src-95b42e2745a2380a16112a059bd0e842d81f0c0a.tar.gz chromium_src-95b42e2745a2380a16112a059bd0e842d81f0c0a.tar.bz2 |
[cros] RlzValueStore made protected by a cross-process lock and not persisted over browser lifetime (like on Mac).
*) Moved RecursiveCrossProcessLock out of .mm file to a common _posix file.
*) Added static method to ImportantFileWriter that does blocking write on the current thread.
*) Dedicated RLZ thread gone, replaced back with shutdown-blocking worker pool.
BUG=157348,62328
Review URL: https://chromiumcodereview.appspot.com/11308196
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@170179 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | base/files/important_file_writer.cc | 29 | ||||
-rw-r--r-- | base/files/important_file_writer.h | 5 | ||||
-rw-r--r-- | chrome/browser/rlz/rlz.cc | 53 | ||||
-rw-r--r-- | chrome/browser/rlz/rlz.h | 15 | ||||
-rw-r--r-- | chrome/browser/rlz/rlz_unittest.cc | 42 | ||||
-rw-r--r-- | rlz/chromeos/lib/rlz_value_store_chromeos.cc | 253 | ||||
-rw-r--r-- | rlz/chromeos/lib/rlz_value_store_chromeos.h | 55 | ||||
-rw-r--r-- | rlz/lib/recursive_cross_process_lock_posix.cc | 81 | ||||
-rw-r--r-- | rlz/lib/recursive_cross_process_lock_posix.h | 43 | ||||
-rw-r--r-- | rlz/lib/recursive_lock.cc | 40 | ||||
-rw-r--r-- | rlz/lib/recursive_lock.h | 34 | ||||
-rw-r--r-- | rlz/lib/recursive_lock_unittest.cc | 234 | ||||
-rw-r--r-- | rlz/lib/rlz_lib.cc | 14 | ||||
-rw-r--r-- | rlz/lib/rlz_lib.h | 15 | ||||
-rw-r--r-- | rlz/lib/rlz_lib_test.cc | 7 | ||||
-rw-r--r-- | rlz/lib/rlz_value_store.h | 18 | ||||
-rw-r--r-- | rlz/mac/lib/rlz_value_store_mac.mm | 101 | ||||
-rw-r--r-- | rlz/rlz.gyp | 16 | ||||
-rw-r--r-- | rlz/test/rlz_test_helpers.cc | 28 | ||||
-rw-r--r-- | rlz/test/rlz_test_helpers.h | 15 |
20 files changed, 361 insertions, 737 deletions
diff --git a/base/files/important_file_writer.cc b/base/files/important_file_writer.cc index 536b0fb..351adc2 100644 --- a/base/files/important_file_writer.cc +++ b/base/files/important_file_writer.cc @@ -42,7 +42,11 @@ void LogFailure(const FilePath& path, TempFileFailure failure_code, << " : " << message; } -void WriteToDiskTask(const FilePath& path, const std::string& data) { +} // namespace + +// static +bool ImportantFileWriter::WriteFileAtomically(const FilePath& path, + const std::string& data) { // Write the data to a temp file then rename to avoid data loss if we crash // while writing the file. Ensure that the temp file is on the same volume // as target file, so it can be moved in one step, and that the temp file @@ -50,7 +54,7 @@ void WriteToDiskTask(const FilePath& path, const std::string& data) { FilePath tmp_file_path; if (!file_util::CreateTemporaryFileInDir(path.DirName(), &tmp_file_path)) { LogFailure(path, FAILED_CREATING, "could not create temporary file"); - return; + return false; } int flags = PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE; @@ -58,7 +62,7 @@ void WriteToDiskTask(const FilePath& path, const std::string& data) { CreatePlatformFile(tmp_file_path, flags, NULL, NULL); if (tmp_file == kInvalidPlatformFileValue) { LogFailure(path, FAILED_OPENING, "could not open temporary file"); - return; + return false; } // If this happens in the wild something really bad is going on. @@ -70,24 +74,24 @@ void WriteToDiskTask(const FilePath& path, const std::string& data) { if (!ClosePlatformFile(tmp_file)) { LogFailure(path, FAILED_CLOSING, "failed to close temporary file"); file_util::Delete(tmp_file_path, false); - return; + return false; } if (bytes_written < static_cast<int>(data.length())) { LogFailure(path, FAILED_WRITING, "error writing, bytes_written=" + IntToString(bytes_written)); file_util::Delete(tmp_file_path, false); - return; + return false; } if (!file_util::ReplaceFile(tmp_file_path, path)) { LogFailure(path, FAILED_RENAMING, "could not rename temporary file"); file_util::Delete(tmp_file_path, false); - return; + return false; } -} -} // namespace + return true; +} ImportantFileWriter::ImportantFileWriter( const FilePath& path, base::SequencedTaskRunner* task_runner) @@ -122,14 +126,17 @@ void ImportantFileWriter::WriteNow(const std::string& data) { if (HasPendingWrite()) timer_.Stop(); - if (!task_runner_->PostTask(FROM_HERE, - MakeCriticalClosure(Bind(&WriteToDiskTask, path_, data)))) { + if (!task_runner_->PostTask( + FROM_HERE, + MakeCriticalClosure( + Bind(IgnoreResult(&ImportantFileWriter::WriteFileAtomically), + path_, data)))) { // Posting the task to background message loop is not expected // to fail, but if it does, avoid losing data and just hit the disk // on the current thread. NOTREACHED(); - WriteToDiskTask(path_, data); + WriteFileAtomically(path_, data); } } diff --git a/base/files/important_file_writer.h b/base/files/important_file_writer.h index ae814d5..9bc8f07 100644 --- a/base/files/important_file_writer.h +++ b/base/files/important_file_writer.h @@ -51,6 +51,11 @@ class BASE_EXPORT ImportantFileWriter : public NonThreadSafe { virtual ~DataSerializer() {} }; + // Save |data| to |path| in an atomic manner (see the class comment above). + // Blocks and writes data on the current thread. + static bool WriteFileAtomically(const FilePath& path, + const std::string& data); + // Initialize the writer. // |path| is the name of file to write. // |task_runner| is the SequencedTaskRunner instance where on which we will diff --git a/chrome/browser/rlz/rlz.cc b/chrome/browser/rlz/rlz.cc index 7005e32..6032dd9 100644 --- a/chrome/browser/rlz/rlz.cc +++ b/chrome/browser/rlz/rlz.cc @@ -26,10 +26,6 @@ #include "content/public/browser/notification_service.h" #include "net/http/http_util.h" -#if defined(OS_CHROMEOS) -#include "base/threading/sequenced_worker_pool.h" -#endif - #if defined(OS_WIN) #include "chrome/installer/util/google_update_settings.h" #else @@ -56,8 +52,6 @@ using content::NavigationEntry; namespace { -const char kRlzThreadName[] = "RLZ_thread"; - bool IsBrandOrganic(const std::string& brand) { return brand.empty() || google_util::IsOrganic(brand); } @@ -177,9 +171,6 @@ RLZTracker::RLZTracker() is_google_default_search_(false), is_google_homepage_(false), is_google_in_startpages_(false), - rlz_thread_(kRlzThreadName), - blocking_task_runner_(NULL), - url_request_context_(NULL), already_ran_(false), omnibox_used_(false), homepage_used_(false) { @@ -208,9 +199,6 @@ bool RLZTracker::Init(bool first_run, is_google_homepage_ = is_google_homepage; is_google_in_startpages_ = is_google_in_startpages; - if (!InitWorkers()) - return false; - // A negative delay means that a financial ping should be sent immediately // after a first search is recorded, without waiting for the next restart // of chrome. However, we only want this behaviour on first run. @@ -246,7 +234,7 @@ bool RLZTracker::Init(bool first_run, content::NotificationService::AllSources()); } - url_request_context_ = g_browser_process->system_request_context(); + rlz_lib::SetURLRequestContext(g_browser_process->system_request_context()); ScheduleDelayedInit(delay); return true; @@ -255,35 +243,14 @@ bool RLZTracker::Init(bool first_run, void RLZTracker::ScheduleDelayedInit(int delay) { // The RLZTracker is a singleton object that outlives any runnable tasks // that will be queued up. - blocking_task_runner_->PostDelayedTask( + BrowserThread::GetBlockingPool()->PostDelayedTask( FROM_HERE, base::Bind(&RLZTracker::DelayedInit, base::Unretained(this)), base::TimeDelta::FromMilliseconds(delay)); } -bool RLZTracker::InitWorkers() { - base::Thread::Options options; - options.message_loop_type = MessageLoop::TYPE_IO; - if (!rlz_thread_.StartWithOptions(options)) - return false; - blocking_task_runner_ = rlz_thread_.message_loop_proxy(); - -#if defined(OS_CHROMEOS) - base::SequencedWorkerPool* worker_pool = - content::BrowserThread::GetBlockingPool(); - if (!worker_pool) - return false; - rlz_lib::SetIOTaskRunner( - worker_pool->GetSequencedTaskRunnerWithShutdownBehavior( - worker_pool->GetSequenceToken(), - base::SequencedWorkerPool::BLOCK_SHUTDOWN)); -#endif - return true; -} - void RLZTracker::DelayedInit() { - if (!already_ran_) - rlz_lib::SetURLRequestContext(url_request_context_); + worker_pool_token_ = BrowserThread::GetBlockingPool()->GetSequenceToken(); bool schedule_ping = false; @@ -316,7 +283,8 @@ void RLZTracker::DelayedInit() { } void RLZTracker::ScheduleFinancialPing() { - blocking_task_runner_->PostTask( + BrowserThread::GetBlockingPool()->PostSequencedWorkerTask( + worker_pool_token_, FROM_HERE, base::Bind(&RLZTracker::PingNowImpl, base::Unretained(this))); } @@ -421,12 +389,13 @@ bool RLZTracker::ScheduleRecordProductEvent(rlz_lib::Product product, rlz_lib::Event event_id) { if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) return false; - if (!blocking_task_runner_) { + if (!already_ran_) { LOG(ERROR) << "Attempted recording RLZ event before RLZ init."; return true; } - blocking_task_runner_->PostTask( + BrowserThread::GetBlockingPool()->PostSequencedWorkerTask( + worker_pool_token_, FROM_HERE, base::Bind(base::IgnoreResult(&RLZTracker::RecordProductEvent), product, point, event_id)); @@ -454,7 +423,8 @@ void RLZTracker::RecordFirstSearch(rlz_lib::AccessPoint point) { bool RLZTracker::ScheduleRecordFirstSearch(rlz_lib::AccessPoint point) { if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) return false; - blocking_task_runner_->PostTask( + BrowserThread::GetBlockingPool()->PostSequencedWorkerTask( + worker_pool_token_, FROM_HERE, base::Bind(&RLZTracker::RecordFirstSearch, base::Unretained(this), point)); @@ -522,7 +492,8 @@ bool RLZTracker::ScheduleGetAccessPointRlz(rlz_lib::AccessPoint point) { return false; string16* not_used = NULL; - blocking_task_runner_->PostTask( + BrowserThread::GetBlockingPool()->PostSequencedWorkerTask( + worker_pool_token_, FROM_HERE, base::Bind(base::IgnoreResult(&RLZTracker::GetAccessPointRlz), point, not_used)); diff --git a/chrome/browser/rlz/rlz.h b/chrome/browser/rlz/rlz.h index 6a1d3c2..e41b5a3 100644 --- a/chrome/browser/rlz/rlz.h +++ b/chrome/browser/rlz/rlz.h @@ -15,7 +15,7 @@ #include "base/basictypes.h" #include "base/memory/singleton.h" #include "base/string16.h" -#include "base/threading/thread.h" +#include "base/threading/sequenced_worker_pool.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "rlz/lib/rlz_lib.h" @@ -113,10 +113,6 @@ class RLZTracker : public content::NotificationObserver { bool google_default_homepage, bool is_google_in_startpages); - // Initializes task runners for tasks that access RlzValueStore and perform - // disk I/O. - virtual bool InitWorkers(); - // Implementation called from RecordProductEvent() static method. bool RecordProductEventImpl(rlz_lib::Product product, rlz_lib::AccessPoint point, @@ -169,12 +165,9 @@ class RLZTracker : public content::NotificationObserver { bool is_google_homepage_; bool is_google_in_startpages_; - // Dedicated RLZ thread for accessing RlzValueStore and doing I/O. - base::Thread rlz_thread_; - - // Sequenced task runner used to post blocking tasks that access - // RlzValueStore. - base::SequencedTaskRunner* blocking_task_runner_; + // Unique sequence token so that tasks posted by RLZTracker are executed + // sequentially in the blocking pool. + base::SequencedWorkerPool::SequenceToken worker_pool_token_; // URLRequestContextGetter used by RLZ library. net::URLRequestContextGetter* url_request_context_; diff --git a/chrome/browser/rlz/rlz_unittest.cc b/chrome/browser/rlz/rlz_unittest.cc index b1ccca9..165652f 100644 --- a/chrome/browser/rlz/rlz_unittest.cc +++ b/chrome/browser/rlz/rlz_unittest.cc @@ -28,12 +28,9 @@ #include "base/test/test_reg_util_win.h" #include "base/win/registry.h" #include "rlz/win/lib/rlz_lib.h" // InitializeTempHivesForTesting -#elif defined(OS_MACOSX) || defined(OS_CHROMEOS) +#elif defined(OS_POSIX) #include "rlz/lib/rlz_value_store.h" // SetRlzStoreDirectory #endif -#if defined(OS_CHROMEOS) -#include "rlz/chromeos/lib/rlz_value_store_chromeos.h" -#endif using content::NavigationEntry; using testing::AssertionResult; @@ -119,10 +116,6 @@ class TestRLZTracker : public RLZTracker { } private: - virtual bool InitWorkers() OVERRIDE { - return true; - } - virtual void ScheduleDelayedInit(int delay) OVERRIDE { // If the delay is 0, invoke the delayed init now. Otherwise, // don't schedule anything, it will be manually called during tests. @@ -172,10 +165,6 @@ class TestRLZTracker : public RLZTracker { class RlzLibTest : public testing::Test { public: -#if defined(OS_CHROMEOS) - RlzLibTest(); -#endif - virtual void SetUp() OVERRIDE; virtual void TearDown() OVERRIDE; @@ -197,22 +186,12 @@ class RlzLibTest : public testing::Test { TestRLZTracker tracker_; #if defined(OS_WIN) RegistryOverrideManager override_manager_; -#elif defined(OS_MACOSX) || defined(OS_CHROMEOS) +#elif defined(OS_POSIX) base::ScopedTempDir temp_dir_; scoped_ptr<google_util::BrandForTesting> brand_override_; #endif -#if defined(OS_CHROMEOS) - MessageLoop loop_; - base::Thread io_thread_; -#endif }; -#if defined(OS_CHROMEOS) -RlzLibTest::RlzLibTest() - : io_thread_("RlzLibTest-io") { -} -#endif - void RlzLibTest::SetUp() { testing::Test::SetUp(); @@ -247,19 +226,11 @@ void RlzLibTest::SetUp() { // initialization performed above. override_manager_.OverrideRegistry(HKEY_LOCAL_MACHINE, kRlzTempHklm); override_manager_.OverrideRegistry(HKEY_CURRENT_USER, kRlzTempHkcu); -#elif defined(OS_MACOSX) || defined(OS_CHROMEOS) +#elif defined(OS_POSIX) ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); rlz_lib::testing::SetRlzStoreDirectory(temp_dir_.path()); #endif -#if defined(OS_CHROMEOS) - base::Thread::Options options; - options.message_loop_type = MessageLoop::TYPE_IO; - ASSERT_TRUE(io_thread_.StartWithOptions(options)); - rlz_lib::SetIOTaskRunner(io_thread_.message_loop_proxy()); - rlz_lib::RlzValueStoreChromeOS::ResetForTesting(); -#endif - // Make sure a non-organic brand code is set in the registry or the RLZTracker // is pretty much a no-op. SetMainBrand("TEST"); @@ -267,19 +238,16 @@ void RlzLibTest::SetUp() { } void RlzLibTest::TearDown() { -#if defined(OS_MACOSX) || defined(OS_CHROMEOS) +#if defined(OS_POSIX) rlz_lib::testing::SetRlzStoreDirectory(FilePath()); #endif -#if defined(OS_CHROMEOS) - io_thread_.Stop(); -#endif // defined(OS_CHROMEOS) testing::Test::TearDown(); } void RlzLibTest::SetMainBrand(const char* brand) { #if defined(OS_WIN) SetRegistryBrandValue(google_update::kRegRLZBrandField, brand); -#elif defined(OS_MACOSX) || defined(OS_CHROMEOS) +#elif defined(OS_POSIX) brand_override_.reset(new google_util::BrandForTesting(brand)); #endif std::string check_brand; diff --git a/rlz/chromeos/lib/rlz_value_store_chromeos.cc b/rlz/chromeos/lib/rlz_value_store_chromeos.cc index ddf73e7..bb9c431 100644 --- a/rlz/chromeos/lib/rlz_value_store_chromeos.cc +++ b/rlz/chromeos/lib/rlz_value_store_chromeos.cc @@ -4,16 +4,16 @@ #include "rlz/chromeos/lib/rlz_value_store_chromeos.h" -#include "base/file_path.h" #include "base/file_util.h" +#include "base/files/important_file_writer.h" +#include "base/json/json_file_value_serializer.h" +#include "base/json/json_string_value_serializer.h" #include "base/logging.h" -#include "base/memory/singleton.h" -#include "base/prefs/json_pref_store.h" #include "base/sequenced_task_runner.h" #include "base/string_number_conversions.h" #include "base/values.h" #include "rlz/lib/lib_values.h" -#include "rlz/lib/recursive_lock.h" +#include "rlz/lib/recursive_cross_process_lock_posix.h" #include "rlz/lib/rlz_lib.h" namespace rlz_lib { @@ -36,6 +36,10 @@ const char kNoSupplementaryBrand[] = "_"; // RLZ store filename. const FilePath::CharType kRLZDataFileName[] = FILE_PATH_LITERAL("RLZ Data"); +// RLZ store lock filename +const FilePath::CharType kRLZLockFileName[] = + FILE_PATH_LITERAL("RLZ Data.lock"); + // RLZ store path for testing. FilePath g_testing_rlz_store_path_; @@ -46,6 +50,13 @@ FilePath GetRlzStorePath() { g_testing_rlz_store_path_.Append(kRLZDataFileName); } +// Returns file path of the RLZ storage lock file. +FilePath GetRlzStoreLockPath() { + return g_testing_rlz_store_path_.empty() ? + file_util::GetHomeDir().Append(kRLZLockFileName) : + g_testing_rlz_store_path_.Append(kRLZLockFileName); +} + // Returns the dictionary key for storing access point-related prefs. std::string GetKeyName(std::string key, AccessPoint access_point) { std::string brand = SupplementaryBranding::GetBrand(); @@ -64,89 +75,58 @@ std::string GetKeyName(std::string key, Product product) { } // namespace -// static -base::SequencedTaskRunner* RlzValueStoreChromeOS::io_task_runner_ = NULL; - -// static -bool RlzValueStoreChromeOS::created_; - -// static -RlzValueStoreChromeOS* RlzValueStoreChromeOS::GetInstance() { - return Singleton<RlzValueStoreChromeOS>::get(); -} - -// static -void RlzValueStoreChromeOS::SetIOTaskRunner( - base::SequencedTaskRunner* io_task_runner) { - io_task_runner_ = io_task_runner; - // Make sure |io_task_runner_| lives until constructor is called. - io_task_runner_->AddRef(); -} - -// static -void RlzValueStoreChromeOS::ResetForTesting() { - // Make sure we don't create an instance if it didn't exist. - if (created_) - GetInstance()->ReadPrefs(); -} - -// static -void RlzValueStoreChromeOS::Cleanup() { - if (created_) - GetInstance()->rlz_store_ = NULL; -} - -RlzValueStoreChromeOS::RlzValueStoreChromeOS() { - ReadPrefs(); - created_ = true; +RlzValueStoreChromeOS::RlzValueStoreChromeOS(const FilePath& store_path) + : rlz_store_(new base::DictionaryValue), + store_path_(store_path), + read_only_(true) { + ReadStore(); } RlzValueStoreChromeOS::~RlzValueStoreChromeOS() { + WriteStore(); } bool RlzValueStoreChromeOS::HasAccess(AccessType type) { - return type == kReadAccess || !rlz_store_->ReadOnly(); + DCHECK(CalledOnValidThread()); + return type == kReadAccess || !read_only_; } bool RlzValueStoreChromeOS::WritePingTime(Product product, int64 time) { - std::string value = base::Int64ToString(time); - rlz_store_->SetValue(GetKeyName(kPingTimeKey, product), - base::Value::CreateStringValue(value)); + DCHECK(CalledOnValidThread()); + rlz_store_->SetString(GetKeyName(kPingTimeKey, product), + base::Int64ToString(time)); return true; } bool RlzValueStoreChromeOS::ReadPingTime(Product product, int64* time) { - const base::Value* value = NULL; - rlz_store_->GetValue(GetKeyName(kPingTimeKey, product), &value); - std::string s_value; - return value && value->GetAsString(&s_value) && - base::StringToInt64(s_value, time); + DCHECK(CalledOnValidThread()); + std::string ping_time; + return rlz_store_->GetString(GetKeyName(kPingTimeKey, product), &ping_time) && + base::StringToInt64(ping_time, time); } bool RlzValueStoreChromeOS::ClearPingTime(Product product) { - rlz_store_->RemoveValue(GetKeyName(kPingTimeKey, product)); + DCHECK(CalledOnValidThread()); + rlz_store_->Remove(GetKeyName(kPingTimeKey, product), NULL); return true; } bool RlzValueStoreChromeOS::WriteAccessPointRlz(AccessPoint access_point, const char* new_rlz) { - rlz_store_->SetValue( - GetKeyName(kAccessPointKey, access_point), - base::Value::CreateStringValue(new_rlz)); + DCHECK(CalledOnValidThread()); + rlz_store_->SetString( + GetKeyName(kAccessPointKey, access_point), new_rlz); return true; } bool RlzValueStoreChromeOS::ReadAccessPointRlz(AccessPoint access_point, char* rlz, size_t rlz_size) { - const base::Value* value = NULL; - rlz_store_->GetValue( - GetKeyName(kAccessPointKey, access_point), &value); - std::string s_value; - if (value) - value->GetAsString(&s_value); - if (s_value.size() < rlz_size) { - strncpy(rlz, s_value.c_str(), rlz_size); + DCHECK(CalledOnValidThread()); + std::string rlz_value; + rlz_store_->GetString(GetKeyName(kAccessPointKey, access_point), &rlz_value); + if (rlz_value.size() < rlz_size) { + strncpy(rlz, rlz_value.c_str(), rlz_size); return true; } if (rlz_size > 0) @@ -155,13 +135,14 @@ bool RlzValueStoreChromeOS::ReadAccessPointRlz(AccessPoint access_point, } bool RlzValueStoreChromeOS::ClearAccessPointRlz(AccessPoint access_point) { - rlz_store_->RemoveValue( - GetKeyName(kAccessPointKey, access_point)); + DCHECK(CalledOnValidThread()); + rlz_store_->Remove(GetKeyName(kAccessPointKey, access_point), NULL); return true; } bool RlzValueStoreChromeOS::AddProductEvent(Product product, const char* event_rlz) { + DCHECK(CalledOnValidThread()); return AddValueToList(GetKeyName(kProductEventKey, product), base::Value::CreateStringValue(event_rlz)); } @@ -169,8 +150,9 @@ bool RlzValueStoreChromeOS::AddProductEvent(Product product, bool RlzValueStoreChromeOS::ReadProductEvents( Product product, std::vector<std::string>* events) { - base::ListValue* events_list = GetList(GetKeyName(kProductEventKey, product)); - if (!events_list) + DCHECK(CalledOnValidThread()); + base::ListValue* events_list = NULL; ; + if (!rlz_store_->GetList(GetKeyName(kProductEventKey, product), &events_list)) return false; events->clear(); for (size_t i = 0; i < events_list->GetSize(); ++i) { @@ -183,99 +165,162 @@ bool RlzValueStoreChromeOS::ReadProductEvents( bool RlzValueStoreChromeOS::ClearProductEvent(Product product, const char* event_rlz) { + DCHECK(CalledOnValidThread()); base::StringValue event_value(event_rlz); return RemoveValueFromList(GetKeyName(kProductEventKey, product), event_value); } bool RlzValueStoreChromeOS::ClearAllProductEvents(Product product) { - rlz_store_->RemoveValue(GetKeyName(kProductEventKey, product)); + DCHECK(CalledOnValidThread()); + rlz_store_->Remove(GetKeyName(kProductEventKey, product), NULL); return true; } bool RlzValueStoreChromeOS::AddStatefulEvent(Product product, const char* event_rlz) { + DCHECK(CalledOnValidThread()); return AddValueToList(GetKeyName(kStatefulEventKey, product), base::Value::CreateStringValue(event_rlz)); } bool RlzValueStoreChromeOS::IsStatefulEvent(Product product, const char* event_rlz) { - base::ListValue* events_list = - GetList(GetKeyName(kStatefulEventKey, product)); + DCHECK(CalledOnValidThread()); base::StringValue event_value(event_rlz); - return events_list && events_list->Find(event_value) != events_list->end(); + base::ListValue* events_list = NULL; + return rlz_store_->GetList(GetKeyName(kStatefulEventKey, product), + &events_list) && + events_list->Find(event_value) != events_list->end(); } bool RlzValueStoreChromeOS::ClearAllStatefulEvents(Product product) { - rlz_store_->RemoveValue(GetKeyName(kStatefulEventKey, product)); + DCHECK(CalledOnValidThread()); + rlz_store_->Remove(GetKeyName(kStatefulEventKey, product), NULL); return true; } void RlzValueStoreChromeOS::CollectGarbage() { + DCHECK(CalledOnValidThread()); NOTIMPLEMENTED(); } -void RlzValueStoreChromeOS::ReadPrefs() { - DCHECK(io_task_runner_) - << "Calling GetInstance or ResetForTesting before SetIOTaskRunner?"; - rlz_store_ = new JsonPrefStore(GetRlzStorePath(), io_task_runner_); - rlz_store_->ReadPrefs(); - switch (rlz_store_->GetReadError()) { - case PersistentPrefStore::PREF_READ_ERROR_NONE: - case PersistentPrefStore::PREF_READ_ERROR_NO_FILE: +void RlzValueStoreChromeOS::ReadStore() { + int error_code = 0; + std::string error_msg; + JSONFileValueSerializer serializer(store_path_); + scoped_ptr<base::Value> value( + serializer.Deserialize(&error_code, &error_msg)); + switch (error_code) { + case JSONFileValueSerializer::JSON_NO_SUCH_FILE: + read_only_ = false; + break; + case JSONFileValueSerializer::JSON_NO_ERROR: + read_only_ = false; + rlz_store_.reset(static_cast<base::DictionaryValue*>(value.release())); break; default: - LOG(ERROR) << "Error read RLZ store: " << rlz_store_->GetReadError(); + LOG(ERROR) << "Error reading RLZ store: " << error_msg; } - // Restore refcount modified by SetIOTaskRunner(). - io_task_runner_->Release(); - io_task_runner_ = NULL; } -base::ListValue* RlzValueStoreChromeOS::GetList(std::string list_name) { - base::Value* list_value = NULL; - rlz_store_->GetMutableValue(list_name, &list_value); - base::ListValue* list = NULL; - if (!list_value || !list_value->GetAsList(&list)) - return NULL; - return list; +void RlzValueStoreChromeOS::WriteStore() { + std::string json_data; + JSONStringValueSerializer serializer(&json_data); + serializer.set_pretty_print(true); + scoped_ptr<DictionaryValue> copy(rlz_store_->DeepCopyWithoutEmptyChildren()); + if (!serializer.Serialize(*copy.get())) { + LOG(ERROR) << "Failed to serialize RLZ data"; + NOTREACHED(); + return; + } + if (!base::ImportantFileWriter::WriteFileAtomically(store_path_, json_data)) + LOG(ERROR) << "Error writing RLZ store"; } bool RlzValueStoreChromeOS::AddValueToList(std::string list_name, base::Value* value) { - base::ListValue* list = GetList(list_name); - if (!list) { - list = new base::ListValue; - rlz_store_->SetValue(list_name, list); + base::ListValue* list_value = NULL; + if (!rlz_store_->GetList(list_name, &list_value)) { + list_value = new base::ListValue; + rlz_store_->Set(list_name, list_value); } - if (list->AppendIfNotPresent(value)) - rlz_store_->ReportValueChanged(list_name); + list_value->AppendIfNotPresent(value); return true; } bool RlzValueStoreChromeOS::RemoveValueFromList(std::string list_name, const base::Value& value) { - base::ListValue* list = GetList(list_name); - if (!list) + base::ListValue* list_value = NULL; + if (!rlz_store_->GetList(list_name, &list_value)) return false; - rlz_store_->MarkNeedsEmptyValue(list_name); size_t index; - if (list->Remove(value, &index)) - rlz_store_->ReportValueChanged(list_name); + list_value->Remove(value, &index); return true; } +namespace { -ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() - : store_(RlzValueStoreChromeOS::GetInstance()) { +// RlzValueStoreChromeOS keeps its data in memory and only writes it to disk +// when ScopedRlzValueStoreLock goes out of scope. Hence, if several +// ScopedRlzValueStoreLocks are nested, they all need to use the same store +// object. + +RecursiveCrossProcessLock g_recursive_lock = + RECURSIVE_CROSS_PROCESS_LOCK_INITIALIZER; + +// This counts the nesting depth of |ScopedRlzValueStoreLock|. +int g_lock_depth = 0; + +// This is the shared store object. Non-|NULL| only when |g_lock_depth > 0|. +RlzValueStoreChromeOS* g_store = NULL; + +} // namespace + +ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() { + bool got_cross_process_lock = + g_recursive_lock.TryGetCrossProcessLock(GetRlzStoreLockPath()); + // At this point, we hold the in-process lock, no matter the value of + // |got_cross_process_lock|. + + ++g_lock_depth; + if (!got_cross_process_lock) { + // Acquiring cross-process lock failed, so simply return here. + // In-process lock will be released in dtor. + DCHECK(!g_store); + return; + } + + if (g_lock_depth > 1) { + // Reuse the already existing store object. + DCHECK(g_store); + store_.reset(g_store); + return; + } + + // This is the topmost lock, create a new store object. + DCHECK(!g_store); + g_store = new RlzValueStoreChromeOS(GetRlzStorePath()); + store_.reset(g_store); } ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() { + --g_lock_depth; + DCHECK(g_lock_depth >= 0); + + if (g_lock_depth > 0) { + // Other locks are still using store_, so don't free it yet. + ignore_result(store_.release()); + return; + } + + g_store = NULL; + + g_recursive_lock.ReleaseLock(); } RlzValueStore* ScopedRlzValueStoreLock::GetStore() { - return store_; + return store_.get(); } namespace testing { @@ -284,6 +329,10 @@ void SetRlzStoreDirectory(const FilePath& directory) { g_testing_rlz_store_path_ = directory; } +std::string RlzStoreFilenameStr() { + return GetRlzStorePath().value(); +} + } // namespace testing } // namespace rlz_lib diff --git a/rlz/chromeos/lib/rlz_value_store_chromeos.h b/rlz/chromeos/lib/rlz_value_store_chromeos.h index c9456a8..4760418 100644 --- a/rlz/chromeos/lib/rlz_value_store_chromeos.h +++ b/rlz/chromeos/lib/rlz_value_store_chromeos.h @@ -5,8 +5,9 @@ #ifndef RLZ_CHROMEOS_LIB_RLZ_VALUE_STORE_CHROMEOS_H_ #define RLZ_CHROMEOS_LIB_RLZ_VALUE_STORE_CHROMEOS_H_ -#include "base/prefs/persistent_pref_store.h" +#include "base/file_path.h" #include "base/threading/non_thread_safe.h" +#include "base/values.h" #include "rlz/lib/rlz_value_store.h" namespace base { @@ -15,28 +16,19 @@ class SequencedTaskRunner; class Value; } -template <typename T> struct DefaultSingletonTraits; - namespace rlz_lib { -// An implementation of RlzValueStore for ChromeOS. Unlike Mac and Win -// counterparts, it's non thread-safe and should only be accessed on a single -// Thread instance that has a MessageLoop. -class RlzValueStoreChromeOS : public RlzValueStore { +// An implementation of RlzValueStore for ChromeOS. +class RlzValueStoreChromeOS : public RlzValueStore, + public base::NonThreadSafe { public: - static RlzValueStoreChromeOS* GetInstance(); - - // Sets the MessageLoopProxy that underlying PersistentPrefStore will post I/O - // tasks to. Must be called before the first GetInstance() call. - static void SetIOTaskRunner(base::SequencedTaskRunner* io_task_runner); - - // Must be invoked during shutdown to commit pending I/O. - static void Cleanup(); + // // Sets the task runner that will be used by ImportantFileWriter for write + // // operations. + // static void SetFileTaskRunner(base::SequencedTaskRunner* file_task_runner); - // Resets the store to its initial state. Should only be used for testing. - // Same restrictions as for calling GetInstance() for the first time apply, - // i.e. must call SetIOTaskRunner first. - static void ResetForTesting(); + // Creates new instance and synchronously reads data from file. + RlzValueStoreChromeOS(const FilePath& store_path); + virtual ~RlzValueStoreChromeOS(); // RlzValueStore overrides: virtual bool HasAccess(AccessType type) OVERRIDE; @@ -68,28 +60,23 @@ class RlzValueStoreChromeOS : public RlzValueStore { virtual void CollectGarbage() OVERRIDE; private: - friend struct DefaultSingletonTraits<RlzValueStoreChromeOS>; - - // Used by JsonPrefStore for write operations. - static base::SequencedTaskRunner* io_task_runner_; + // Reads RLZ store from file. + void ReadStore(); - static bool created_; + // Writes RLZ store back to file. + void WriteStore(); - RlzValueStoreChromeOS(); - virtual ~RlzValueStoreChromeOS(); - - // Initializes RLZ store. - void ReadPrefs(); - - // Retrieves list at path |list_name| from JSON store. - base::ListValue* GetList(std::string list_name); // Adds |value| to list at |list_name| path in JSON store. bool AddValueToList(std::string list_name, base::Value* value); // Removes |value| from list at |list_name| path in JSON store. bool RemoveValueFromList(std::string list_name, const base::Value& value); - // Store with RLZ data. - scoped_refptr<PersistentPrefStore> rlz_store_; + // In-memory store with RLZ data. + scoped_ptr<base::DictionaryValue> rlz_store_; + + FilePath store_path_; + + bool read_only_; DISALLOW_COPY_AND_ASSIGN(RlzValueStoreChromeOS); }; diff --git a/rlz/lib/recursive_cross_process_lock_posix.cc b/rlz/lib/recursive_cross_process_lock_posix.cc new file mode 100644 index 0000000..071bf8c --- /dev/null +++ b/rlz/lib/recursive_cross_process_lock_posix.cc @@ -0,0 +1,81 @@ +// Copyright (c) 2012 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 "rlz/lib/recursive_cross_process_lock_posix.h" + +#include <sys/file.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "base/file_path.h" +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" + +namespace rlz_lib { + +bool RecursiveCrossProcessLock::TryGetCrossProcessLock( + const FilePath& lock_filename) { + bool just_got_lock = false; + + // Emulate a recursive mutex with a non-recursive one. + if (pthread_mutex_trylock(&recursive_lock_) == EBUSY) { + if (pthread_equal(pthread_self(), locking_thread_) == 0) { + // Some other thread has the lock, wait for it. + pthread_mutex_lock(&recursive_lock_); + CHECK(locking_thread_ == 0); + just_got_lock = true; + } + } else { + just_got_lock = true; + } + + locking_thread_ = pthread_self(); + + // Try to acquire file lock. + if (just_got_lock) { + const int kMaxTimeoutMS = 5000; // Matches Windows. + const int kSleepPerTryMS = 200; + + CHECK(file_lock_ == -1); + file_lock_ = open(lock_filename.value().c_str(), O_RDWR | O_CREAT, 0666); + if (file_lock_ == -1) { + perror("open"); + return false; + } + + int flock_result = -1; + int elapsed_ms = 0; + while ((flock_result = + HANDLE_EINTR(flock(file_lock_, LOCK_EX | LOCK_NB))) == -1 && + errno == EWOULDBLOCK && + elapsed_ms < kMaxTimeoutMS) { + usleep(kSleepPerTryMS * 1000); + elapsed_ms += kSleepPerTryMS; + } + + if (flock_result == -1) { + perror("flock"); + ignore_result(HANDLE_EINTR(close(file_lock_))); + file_lock_ = -1; + return false; + } + return true; + } else { + return file_lock_ != -1; + } +} + +void RecursiveCrossProcessLock::ReleaseLock() { + if (file_lock_ != -1) { + ignore_result(HANDLE_EINTR(flock(file_lock_, LOCK_UN))); + ignore_result(HANDLE_EINTR(close(file_lock_))); + file_lock_ = -1; + } + + locking_thread_ = 0; + pthread_mutex_unlock(&recursive_lock_); +} + +} // namespace rlz_lib diff --git a/rlz/lib/recursive_cross_process_lock_posix.h b/rlz/lib/recursive_cross_process_lock_posix.h new file mode 100644 index 0000000..a39d473 --- /dev/null +++ b/rlz/lib/recursive_cross_process_lock_posix.h @@ -0,0 +1,43 @@ +// Copyright (c) 2012 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. + +#ifndef RLZ_LIB_RECURSIVE_CROSS_PROCESS_LOCK_POSIX_H_ +#define RLZ_LIB_RECURSIVE_CROSS_PROCESS_LOCK_POSIX_H_ + +#include <pthread.h> + +class FilePath; + +namespace rlz_lib { + +// Creating a recursive cross-process mutex on Windows is one line. On POSIX, +// there's no primitive for that, so this lock is emulated by an in-process +// mutex to get the recursive part, followed by a cross-process lock for the +// cross-process part. +// This is a struct so that it doesn't need a static initializer. +struct RecursiveCrossProcessLock { + // Tries to acquire a recursive cross-process lock. Note that this _always_ + // acquires the in-process lock (if it wasn't already acquired). The parent + // directory of |lock_file| must exist. + bool TryGetCrossProcessLock(const FilePath& lock_filename); + + // Releases the lock. Should always be called, even if + // TryGetCrossProcessLock() returned |false|. + void ReleaseLock(); + + pthread_mutex_t recursive_lock_; + pthread_t locking_thread_; + + int file_lock_; +}; + +// On Mac, PTHREAD_RECURSIVE_MUTEX_INITIALIZER doesn't exist before 10.7 and +// is buggy on 10.7 (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51906#c34), +// so emulate recursive locking with a normal non-recursive mutex. +#define RECURSIVE_CROSS_PROCESS_LOCK_INITIALIZER \ + { PTHREAD_MUTEX_INITIALIZER, 0, -1 } + +} // namespace rlz_lib + +#endif // RLZ_LIB_RECURSIVE_CROSS_PROCESS_LOCK_POSIX_H_ diff --git a/rlz/lib/recursive_lock.cc b/rlz/lib/recursive_lock.cc deleted file mode 100644 index 686cf0e..0000000 --- a/rlz/lib/recursive_lock.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2012 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 "rlz/lib/recursive_lock.h" - -#include "base/logging.h" - -namespace rlz_lib { - -RecursiveLock::RecursiveLock() - : owner_(), - recursion_() { -} - -RecursiveLock::~RecursiveLock() { -} - -void RecursiveLock::Acquire() { - base::subtle::Atomic32 me = base::PlatformThread::CurrentId(); - if (me != base::subtle::NoBarrier_Load(&owner_)) { - lock_.Acquire(); - DCHECK(!recursion_); - DCHECK(!owner_); - base::subtle::NoBarrier_Store(&owner_, me); - } - ++recursion_; -} - -void RecursiveLock::Release() { - DCHECK_EQ(base::subtle::NoBarrier_Load(&owner_), - base::PlatformThread::CurrentId()); - DCHECK_GT(recursion_, 0); - if (!--recursion_) { - base::subtle::NoBarrier_Store(&owner_, 0); - lock_.Release(); - } -} - -} // namespace rlz_lib diff --git a/rlz/lib/recursive_lock.h b/rlz/lib/recursive_lock.h deleted file mode 100644 index 43c95747..0000000 --- a/rlz/lib/recursive_lock.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef RLZ_CHROMEOS_LIB_RECURSIVE_LOCK_H_ -#define RLZ_CHROMEOS_LIB_RECURSIVE_LOCK_H_ - -#include "base/atomicops.h" -#include "base/basictypes.h" -#include "base/synchronization/lock.h" - -namespace rlz_lib { - - -class RecursiveLock { - public: - RecursiveLock(); - ~RecursiveLock(); - - void Acquire(); - void Release(); - - private: - // Underlying non-recursive lock. - base::Lock lock_; - // Owner thread ID. - base::subtle::Atomic32 owner_; - // Recursion lock depth. - int recursion_; -}; - -} // namespace rlz_lib - -#endif // RLZ_CHROMEOS_LIB_RECURSIVE_LOCK_H_ diff --git a/rlz/lib/recursive_lock_unittest.cc b/rlz/lib/recursive_lock_unittest.cc deleted file mode 100644 index 916af7f..0000000 --- a/rlz/lib/recursive_lock_unittest.cc +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright (c) 2012 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 "rlz/lib/recursive_lock.h" - -#include <stdlib.h> - -#include "base/compiler_specific.h" -#include "base/threading/platform_thread.h" -#include "base/time.h" -#include "testing/gtest/include/gtest/gtest.h" - -using base::kNullThreadHandle; -using base::PlatformThread; -using base::PlatformThreadHandle; -using base::TimeDelta; - -namespace rlz_lib { - -// Basic test to make sure that Acquire()/Release() don't crash. -class BasicLockTestThread : public PlatformThread::Delegate { - public: - BasicLockTestThread(RecursiveLock* lock) : lock_(lock), acquired_(0) {} - - virtual void ThreadMain() OVERRIDE { - for (int i = 0; i < 10; i++) { - lock_->Acquire(); - acquired_++; - lock_->Release(); - } - for (int i = 0; i < 10; i++) { - lock_->Acquire(); - acquired_++; - PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 20)); - lock_->Release(); - } - } - - int acquired() const { return acquired_; } - - private: - RecursiveLock* lock_; - int acquired_; - - DISALLOW_COPY_AND_ASSIGN(BasicLockTestThread); -}; - -TEST(RecursiveLockTest, Basic) { - RecursiveLock lock; - BasicLockTestThread thread(&lock); - PlatformThreadHandle handle = kNullThreadHandle; - - ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); - - int acquired = 0; - for (int i = 0; i < 5; i++) { - lock.Acquire(); - acquired++; - lock.Release(); - } - for (int i = 0; i < 10; i++) { - lock.Acquire(); - acquired++; - PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 20)); - lock.Release(); - } - for (int i = 0; i < 5; i++) { - lock.Acquire(); - acquired++; - PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 20)); - lock.Release(); - } - - PlatformThread::Join(handle); - - EXPECT_EQ(acquired, 20); - EXPECT_EQ(thread.acquired(), 20); -} - -// Tests that locks are actually exclusive. -class MutexLockTestThread : public PlatformThread::Delegate { - public: - MutexLockTestThread(RecursiveLock* lock, int* value) - : lock_(lock), - value_(value) { - } - - // Static helper which can also be called from the main thread. - static void DoStuff(RecursiveLock* lock, int* value) { - for (int i = 0; i < 40; i++) { - lock->Acquire(); - int v = *value; - PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 10)); - *value = v + 1; - lock->Release(); - } - } - - virtual void ThreadMain() OVERRIDE { - DoStuff(lock_, value_); - } - - private: - RecursiveLock* lock_; - int* value_; - - DISALLOW_COPY_AND_ASSIGN(MutexLockTestThread); -}; - -TEST(RecursiveLockTest, MutexTwoThreads) { - RecursiveLock lock; - int value = 0; - - MutexLockTestThread thread(&lock, &value); - PlatformThreadHandle handle = kNullThreadHandle; - - ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); - - MutexLockTestThread::DoStuff(&lock, &value); - - PlatformThread::Join(handle); - - EXPECT_EQ(2 * 40, value); -} - -TEST(RecursiveLockTest, MutexFourThreads) { - RecursiveLock lock; - int value = 0; - - MutexLockTestThread thread1(&lock, &value); - MutexLockTestThread thread2(&lock, &value); - MutexLockTestThread thread3(&lock, &value); - PlatformThreadHandle handle1 = kNullThreadHandle; - PlatformThreadHandle handle2 = kNullThreadHandle; - PlatformThreadHandle handle3 = kNullThreadHandle; - - ASSERT_TRUE(PlatformThread::Create(0, &thread1, &handle1)); - ASSERT_TRUE(PlatformThread::Create(0, &thread2, &handle2)); - ASSERT_TRUE(PlatformThread::Create(0, &thread3, &handle3)); - - MutexLockTestThread::DoStuff(&lock, &value); - - PlatformThread::Join(handle1); - PlatformThread::Join(handle2); - PlatformThread::Join(handle3); - - EXPECT_EQ(4 * 40, value); -} - -// Tests that locks are recursive. -class MutexRecursiveLockTestThread : public PlatformThread::Delegate { - public: - MutexRecursiveLockTestThread(RecursiveLock* lock, int* value) - : lock_(lock), - value_(value) { - } - - // Static helper which can also be called from the main thread. - static void DoStuff(RecursiveLock* lock, int* value) { - for (int i = 0; i < 20; i++) { - // First lock. - lock->Acquire(); - int v = *value; - PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 10)); - *value = v + 1; - { - // Recursive lock. - lock->Acquire(); - int v = *value; - PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 10)); - *value = v + 1; - lock->Release(); - } - v = *value; - PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 10)); - *value = v + 1; - lock->Release(); - } - } - - virtual void ThreadMain() OVERRIDE { - DoStuff(lock_, value_); - } - - private: - RecursiveLock* lock_; - int* value_; - - DISALLOW_COPY_AND_ASSIGN(MutexRecursiveLockTestThread); -}; - - -TEST(RecursiveLockTest, MutexTwoThreadsRecursive) { - RecursiveLock lock; - int value = 0; - - MutexRecursiveLockTestThread thread(&lock, &value); - PlatformThreadHandle handle = kNullThreadHandle; - - ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); - - MutexRecursiveLockTestThread::DoStuff(&lock, &value); - - PlatformThread::Join(handle); - - EXPECT_EQ(2 * 60, value); -} - -TEST(RecursiveLockTest, MutexFourThreadsRecursive) { - RecursiveLock lock; - int value = 0; - - MutexRecursiveLockTestThread thread1(&lock, &value); - MutexRecursiveLockTestThread thread2(&lock, &value); - MutexRecursiveLockTestThread thread3(&lock, &value); - PlatformThreadHandle handle1 = kNullThreadHandle; - PlatformThreadHandle handle2 = kNullThreadHandle; - PlatformThreadHandle handle3 = kNullThreadHandle; - - ASSERT_TRUE(PlatformThread::Create(0, &thread1, &handle1)); - ASSERT_TRUE(PlatformThread::Create(0, &thread2, &handle2)); - ASSERT_TRUE(PlatformThread::Create(0, &thread3, &handle3)); - - MutexRecursiveLockTestThread::DoStuff(&lock, &value); - - PlatformThread::Join(handle1); - PlatformThread::Join(handle2); - PlatformThread::Join(handle3); - - EXPECT_EQ(4 * 60, value); -} - -} // namespace rlz_lib diff --git a/rlz/lib/rlz_lib.cc b/rlz/lib/rlz_lib.cc index a820d27..8a1b729 100644 --- a/rlz/lib/rlz_lib.cc +++ b/rlz/lib/rlz_lib.cc @@ -16,10 +16,6 @@ #include "rlz/lib/rlz_value_store.h" #include "rlz/lib/string_utils.h" -#if defined(OS_CHROMEOS) -#include "rlz/chromeos/lib/rlz_value_store_chromeos.h" -#endif // defined(OS_CHROMEOS) - namespace { // Event information returned from ping response. @@ -218,16 +214,6 @@ bool SetURLRequestContext(net::URLRequestContextGetter* context) { } #endif -#if defined(OS_CHROMEOS) -void RLZ_LIB_API SetIOTaskRunner(base::SequencedTaskRunner* io_task_runner) { - RlzValueStoreChromeOS::SetIOTaskRunner(io_task_runner); -} - -void RLZ_LIB_API CleanupRlz() { - RlzValueStoreChromeOS::Cleanup(); -} -#endif - bool GetProductEventsAsCgi(Product product, char* cgi, size_t cgi_size) { if (!cgi || cgi_size <= 0) { ASSERT_STRING("GetProductEventsAsCgi: Invalid buffer"); diff --git a/rlz/lib/rlz_lib.h b/rlz/lib/rlz_lib.h index 956fc9e..1f8be5c 100644 --- a/rlz/lib/rlz_lib.h +++ b/rlz/lib/rlz_lib.h @@ -44,12 +44,6 @@ #endif #endif -#if defined(OS_CHROMEOS) -namespace base { -class SequencedTaskRunner; -} // namespace base -#endif - #if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET) namespace net { class URLRequestContextGetter; @@ -77,15 +71,6 @@ const size_t kMaxPingResponseLength = 0x4000; // 16K bool RLZ_LIB_API SetURLRequestContext(net::URLRequestContextGetter* context); #endif -#if defined(OS_CHROMEOS) -// Set the MessageLoopProxy used by RLZ store to run I/O tasks on. Should be -// called before any other API calls. -void RLZ_LIB_API SetIOTaskRunner(base::SequencedTaskRunner* io_task_runner); - -// Must be invoked during shutdown to finish any remaining tasks. -void RLZ_LIB_API CleanupRlz(); -#endif - // RLZ storage functions. // Get all the events reported by this product as a CGI string to append to diff --git a/rlz/lib/rlz_lib_test.cc b/rlz/lib/rlz_lib_test.cc index 338473b..86e659c 100644 --- a/rlz/lib/rlz_lib_test.cc +++ b/rlz/lib/rlz_lib_test.cc @@ -788,7 +788,7 @@ TEST_F(RlzLibTest, BrandingWithStatefulEvents) { EXPECT_STREQ("events=I7S", value); } -#if defined(OS_MACOSX) +#if defined(OS_POSIX) class ReadonlyRlzDirectoryTest : public RlzLibTestNoMachineState { protected: virtual void SetUp() OVERRIDE; @@ -799,7 +799,6 @@ void ReadonlyRlzDirectoryTest::SetUp() { // Make the rlz directory non-writeable. int chmod_result = chmod(temp_dir_.path().value().c_str(), 0500); ASSERT_EQ(0, chmod_result); - } TEST_F(ReadonlyRlzDirectoryTest, WriteFails) { @@ -873,7 +872,7 @@ TEST_F(RlzLibTest, ConcurrentStoreAccessWithProcessExitsWhileLockHeld) { rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::INSTALL)); } -TEST_F(RlzLibTest, LockAcquistionSucceedsButPlistCannotBeCreated) { +TEST_F(RlzLibTest, LockAcquistionSucceedsButStoreFileCannotBeCreated) { // See the comment at the top of WriteFails. if (!rlz_lib::SupplementaryBranding::GetBrand().empty()) return; @@ -881,7 +880,7 @@ TEST_F(RlzLibTest, LockAcquistionSucceedsButPlistCannotBeCreated) { // Create a directory where the rlz file is supposed to appear. This way, // the lock file can be created successfully, but creation of the rlz file // itself will fail. - int mkdir_result = mkdir(rlz_lib::testing::RlzPlistFilenameStr().c_str(), + int mkdir_result = mkdir(rlz_lib::testing::RlzStoreFilenameStr().c_str(), 0500); ASSERT_EQ(0, mkdir_result); diff --git a/rlz/lib/rlz_value_store.h b/rlz/lib/rlz_value_store.h index 807f100..46e87df 100644 --- a/rlz/lib/rlz_value_store.h +++ b/rlz/lib/rlz_value_store.h @@ -95,12 +95,7 @@ class ScopedRlzValueStoreLock { RlzValueStore* GetStore(); private: -#if defined(OS_WIN) || defined(OS_MACOSX) - // On ChromeOS, there is a singleton instance of RlzValueStore. scoped_ptr<RlzValueStore> store_; -#elif defined(OS_CHROMEOS) - class RlzValueStoreChromeOS* store_; -#endif #if defined(OS_WIN) LibMutex lock_; #elif defined(OS_MACOSX) @@ -108,20 +103,15 @@ class ScopedRlzValueStoreLock { #endif }; -#if defined(OS_MACOSX) || defined(OS_CHROMEOS) +#if defined(OS_POSIX) namespace testing { // Prefix |directory| to the path where the RLZ data file lives, for tests. void SetRlzStoreDirectory(const FilePath& directory); -} // namespace testing -#endif // defined(OS_MACOSX) || defined(OS_CHROMEOS) -#if defined(OS_MACOSX) -namespace testing { -// Returns the path of the plist file used as data store. -std::string RlzPlistFilenameStr(); +// Returns the path of the file used as data store. +std::string RlzStoreFilenameStr(); } // namespace testing -#endif // defined(OS_MACOSX) - +#endif // defined(OS_POSIX) } // namespace rlz_lib diff --git a/rlz/mac/lib/rlz_value_store_mac.mm b/rlz/mac/lib/rlz_value_store_mac.mm index 6148e54..6b8e6f4 100644 --- a/rlz/mac/lib/rlz_value_store_mac.mm +++ b/rlz/mac/lib/rlz_value_store_mac.mm @@ -7,11 +7,11 @@ #include "base/mac/foundation_util.h" #include "base/file_path.h" #include "base/logging.h" -#include "base/posix/eintr_wrapper.h" #include "base/sys_string_conversions.h" #include "rlz/lib/assert.h" #include "rlz/lib/lib_values.h" #include "rlz/lib/rlz_lib.h" +#include "rlz/lib/recursive_cross_process_lock_posix.h" #import <Foundation/Foundation.h> #include <pthread.h> @@ -217,97 +217,8 @@ NSMutableDictionary* RlzValueStoreMac::ProductDict(Product p) { namespace { -// Creating a recursive cross-process mutex on windows is one line. On mac, -// there's no primitve for that, so this lock is emulated by an in-process -// mutex to get the recursive part, followed by a cross-process lock for the -// cross-process part. - -// This is a struct so that it doesn't need a static initializer. -struct RecursiveCrossProcessLock { - // Tries to acquire a recursive cross-process lock. Note that this _always_ - // acquires the in-process lock (if it wasn't already acquired). The parent - // directory of |lock_file| must exist. - bool TryGetCrossProcessLock(NSString* lock_filename); - - // Releases the lock. Should always be called, even if - // TryGetCrossProcessLock() returns false. - void ReleaseLock(); - - pthread_mutex_t recursive_lock_; - pthread_t locking_thread_; - - int file_lock_; -} g_recursive_lock = { - // PTHREAD_RECURSIVE_MUTEX_INITIALIZER doesn't exist before 10.7 and is buggy - // on 10.7 (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51906#c34), so emulate - // recursive locking with a normal non-recursive mutex. - PTHREAD_MUTEX_INITIALIZER, - 0, - -1 -}; - -bool RecursiveCrossProcessLock::TryGetCrossProcessLock( - NSString* lock_filename) { - bool just_got_lock = false; - - // Emulate a recursive mutex with a non-recursive one. - if (pthread_mutex_trylock(&recursive_lock_) == EBUSY) { - if (pthread_equal(pthread_self(), locking_thread_) == 0) { - // Some other thread has the lock, wait for it. - pthread_mutex_lock(&recursive_lock_); - CHECK(locking_thread_ == 0); - just_got_lock = true; - } - } else { - just_got_lock = true; - } - - locking_thread_ = pthread_self(); - - // Try to acquire file lock. - if (just_got_lock) { - const int kMaxTimeoutMS = 5000; // Matches windows. - const int kSleepPerTryMS = 200; - - CHECK(file_lock_ == -1); - file_lock_ = open([lock_filename fileSystemRepresentation], - O_RDWR | O_CREAT, - 0666); - if (file_lock_ == -1) - return false; - - int flock_result = -1; - int elapsed_ms = 0; - while ((flock_result = - HANDLE_EINTR(flock(file_lock_, LOCK_EX | LOCK_NB))) == -1 && - errno == EWOULDBLOCK && - elapsed_ms < kMaxTimeoutMS) { - usleep(kSleepPerTryMS * 1000); - elapsed_ms += kSleepPerTryMS; - } - - if (flock_result == -1) { - ignore_result(HANDLE_EINTR(close(file_lock_))); - file_lock_ = -1; - return false; - } - return true; - } else { - return file_lock_ != -1; - } -} - -void RecursiveCrossProcessLock::ReleaseLock() { - if (file_lock_ != -1) { - ignore_result(HANDLE_EINTR(flock(file_lock_, LOCK_UN))); - ignore_result(HANDLE_EINTR(close(file_lock_))); - file_lock_ = -1; - } - - locking_thread_ = 0; - pthread_mutex_unlock(&recursive_lock_); -} - +RecursiveCrossProcessLock g_recursive_lock = + RECURSIVE_CROSS_PROCESS_LOCK_INITIALIZER; // This is set during test execution, to write RLZ files into a temporary // directory instead of the user's Application Support folder. @@ -363,8 +274,8 @@ NSString* RlzLockFilename() { } // namespace ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() { - bool got_distributed_lock = - g_recursive_lock.TryGetCrossProcessLock(RlzLockFilename()); + bool got_distributed_lock = g_recursive_lock.TryGetCrossProcessLock( + FilePath([RlzLockFilename() fileSystemRepresentation])); // At this point, we hold the in-process lock, no matter the value of // |got_distributed_lock|. @@ -451,7 +362,7 @@ void SetRlzStoreDirectory(const FilePath& directory) { } } -std::string RlzPlistFilenameStr() { +std::string RlzStoreFilenameStr() { @autoreleasepool { return std::string([RlzPlistFilename() fileSystemRepresentation]); } diff --git a/rlz/rlz.gyp b/rlz/rlz.gyp index 3e04654..0d4b330 100644 --- a/rlz/rlz.gyp +++ b/rlz/rlz.gyp @@ -45,8 +45,8 @@ 'lib/lib_values.cc', 'lib/machine_id.cc', 'lib/machine_id.h', - 'lib/recursive_lock.cc', - 'lib/recursive_lock.h', + 'lib/recursive_cross_process_lock_posix.cc', + 'lib/recursive_cross_process_lock_posix.h', 'lib/rlz_enums.h', 'lib/rlz_lib.cc', 'lib/rlz_lib.h', @@ -97,12 +97,6 @@ ], }, }], - ['chromeos==0', { - 'sources!': [ - 'lib/recursive_lock.cc', - 'lib/recursive_lock.h', - ] - }], ], }, { @@ -122,7 +116,6 @@ 'lib/financial_ping_test.cc', 'lib/lib_values_unittest.cc', 'lib/machine_id_unittest.cc', - 'lib/recursive_lock_unittest.cc', 'lib/rlz_lib_test.cc', 'lib/string_utils_unittest.cc', 'test/rlz_test_helpers.cc', @@ -136,11 +129,6 @@ '../net/net.gyp:net_test_support', ], }], - ['chromeos==0', { - 'sources!': [ - 'lib/recursive_lock_unittest.cc', - ], - }] ], }, { diff --git a/rlz/test/rlz_test_helpers.cc b/rlz/test/rlz_test_helpers.cc index 4fe12d4..32719e0 100644 --- a/rlz/test/rlz_test_helpers.cc +++ b/rlz/test/rlz_test_helpers.cc @@ -13,13 +13,10 @@ #include <shlwapi.h> #include "base/win/registry.h" #include "rlz/win/lib/rlz_lib.h" -#elif defined(OS_MACOSX) || defined(OS_CHROMEOS) +#elif defined(OS_POSIX) #include "base/file_path.h" #include "rlz/lib/rlz_value_store.h" #endif -#if defined(OS_CHROMEOS) -#include "rlz/chromeos/lib/rlz_value_store_chromeos.h" -#endif #if defined(OS_WIN) namespace { @@ -62,41 +59,24 @@ void UndoOverrideRegistryHives() { } // namespace #endif // defined(OS_WIN) - -#if defined(OS_CHROMEOS) -RlzLibTestNoMachineState::RlzLibTestNoMachineState() - : pref_store_io_thread_("test_rlz_pref_store_io_thread") { -} -#endif // defined(OS_CHROMEOS) - void RlzLibTestNoMachineState::SetUp() { #if defined(OS_WIN) OverrideRegistryHives(); #elif defined(OS_MACOSX) base::mac::ScopedNSAutoreleasePool pool; #endif // defined(OS_WIN) -#if defined(OS_MACOSX) || defined(OS_CHROMEOS) +#if defined(OS_POSIX) ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); rlz_lib::testing::SetRlzStoreDirectory(temp_dir_.path()); -#endif // defined(OS_MACOSX) || defined(OS_CHROMEOS) -#if defined(OS_CHROMEOS) - base::Thread::Options options; - options.message_loop_type = MessageLoop::TYPE_IO; - ASSERT_TRUE(pref_store_io_thread_.StartWithOptions(options)); - rlz_lib::SetIOTaskRunner(pref_store_io_thread_.message_loop_proxy()); - rlz_lib::RlzValueStoreChromeOS::ResetForTesting(); -#endif // defined(OS_CHROMEOS) +#endif // defined(OS_POSIX) } void RlzLibTestNoMachineState::TearDown() { #if defined(OS_WIN) UndoOverrideRegistryHives(); -#elif defined(OS_MACOSX) || defined(OS_CHROMEOS) +#elif defined(OS_POSIX) rlz_lib::testing::SetRlzStoreDirectory(FilePath()); #endif // defined(OS_WIN) -#if defined(OS_CHROMEOS) - pref_store_io_thread_.Stop(); -#endif // defined(OS_CHROMEOS) } void RlzLibTestBase::SetUp() { diff --git a/rlz/test/rlz_test_helpers.h b/rlz/test/rlz_test_helpers.h index 8b0565d..a78764f 100644 --- a/rlz/test/rlz_test_helpers.h +++ b/rlz/test/rlz_test_helpers.h @@ -10,30 +10,19 @@ #include "base/compiler_specific.h" #include "testing/gtest/include/gtest/gtest.h" -#if defined(OS_MACOSX) || defined(OS_CHROMEOS) +#if defined(OS_POSIX) #include "base/files/scoped_temp_dir.h" #endif -#if defined(OS_CHROMEOS) -#include "base/message_loop.h" -#include "base/threading/thread.h" -#endif class RlzLibTestNoMachineState : public ::testing::Test { protected: -#if defined(OS_CHROMEOS) - RlzLibTestNoMachineState(); -#endif virtual void SetUp() OVERRIDE; virtual void TearDown() OVERRIDE; -#if defined(OS_MACOSX) || defined(OS_CHROMEOS) +#if defined(OS_POSIX) base::ScopedTempDir temp_dir_; #endif -#if defined(OS_CHROMEOS) - base::Thread pref_store_io_thread_; - MessageLoop message_loop_; -#endif }; class RlzLibTestBase : public RlzLibTestNoMachineState { |