diff options
author | rlarocque@chromium.org <rlarocque@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-20 18:58:26 +0000 |
---|---|---|
committer | rlarocque@chromium.org <rlarocque@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-20 18:58:26 +0000 |
commit | 15732679ce96d261ff3e838d3f8cdb6ff86a2189 (patch) | |
tree | 00fe7e29bf4e67d4bd765be5d637811cee3bbaef /sync | |
parent | 77e4dc088d316a0507c6ff25b5d1f9f060cb30d8 (diff) | |
download | chromium_src-15732679ce96d261ff3e838d3f8cdb6ff86a2189.zip chromium_src-15732679ce96d261ff3e838d3f8cdb6ff86a2189.tar.gz chromium_src-15732679ce96d261ff3e838d3f8cdb6ff86a2189.tar.bz2 |
Split syncable.{h,cc} into several files
The only functional change was to remove the dirkernel_ member from
BaseTransaction, which was to reduce the number of includes required.
Everything else is just moving code around, and updating includes, forward
declarations, and 'using' statements.
BUG=103332
TEST=
Review URL: https://chromiumcodereview.appspot.com/10579036
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@143218 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sync')
79 files changed, 2243 insertions, 1967 deletions
diff --git a/sync/engine/apply_updates_command.cc b/sync/engine/apply_updates_command.cc index 79fd039..35490c3 100644 --- a/sync/engine/apply_updates_command.cc +++ b/sync/engine/apply_updates_command.cc @@ -7,7 +7,9 @@ #include "base/location.h" #include "sync/engine/update_applicator.h" #include "sync/sessions/sync_session.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/read_transaction.h" +#include "sync/syncable/write_transaction.h" namespace browser_sync { diff --git a/sync/engine/apply_updates_command_unittest.cc b/sync/engine/apply_updates_command_unittest.cc index 6ff6dae..e60c15f 100644 --- a/sync/engine/apply_updates_command_unittest.cc +++ b/sync/engine/apply_updates_command_unittest.cc @@ -14,8 +14,10 @@ #include "sync/protocol/bookmark_specifics.pb.h" #include "sync/protocol/password_specifics.pb.h" #include "sync/sessions/sync_session.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/mutable_entry.h" +#include "sync/syncable/read_transaction.h" #include "sync/syncable/syncable_id.h" +#include "sync/syncable/write_transaction.h" #include "sync/test/engine/fake_model_worker.h" #include "sync/test/engine/syncer_command_test.h" #include "sync/test/engine/test_id_factory.h" @@ -27,7 +29,6 @@ namespace browser_sync { using sessions::SyncSession; using std::string; -using syncable::Entry; using syncable::Id; using syncable::MutableEntry; using syncable::ReadTransaction; diff --git a/sync/engine/build_commit_command.cc b/sync/engine/build_commit_command.cc index 974cd37..665613a 100644 --- a/sync/engine/build_commit_command.cc +++ b/sync/engine/build_commit_command.cc @@ -14,8 +14,10 @@ #include "sync/protocol/bookmark_specifics.pb.h" #include "sync/sessions/ordered_commit_set.h" #include "sync/sessions/sync_session.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/mutable_entry.h" #include "sync/syncable/syncable_changes_version.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/write_transaction.h" #include "sync/util/time.h" using std::set; diff --git a/sync/engine/build_commit_command.h b/sync/engine/build_commit_command.h index 91eb3203..bdcefe1 100644 --- a/sync/engine/build_commit_command.h +++ b/sync/engine/build_commit_command.h @@ -8,9 +8,14 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/gtest_prod_util.h" #include "sync/engine/syncer_command.h" #include "sync/engine/syncproto.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/entry_kernel.h" + +namespace syncable { +class Entry; +} namespace browser_sync { diff --git a/sync/engine/cleanup_disabled_types_command.cc b/sync/engine/cleanup_disabled_types_command.cc index 4a774b7..1a94663 100644 --- a/sync/engine/cleanup_disabled_types_command.cc +++ b/sync/engine/cleanup_disabled_types_command.cc @@ -9,7 +9,7 @@ #include "sync/internal_api/public/syncable/model_type.h" #include "sync/sessions/sync_session.h" #include "sync/sessions/sync_session_context.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" namespace browser_sync { diff --git a/sync/engine/commit.cc b/sync/engine/commit.cc index 2f39c23..abef162 100644 --- a/sync/engine/commit.cc +++ b/sync/engine/commit.cc @@ -10,6 +10,8 @@ #include "sync/engine/process_commit_response_command.h" #include "sync/engine/syncer_proto_util.h" #include "sync/sessions/sync_session.h" +#include "sync/syncable/mutable_entry.h" +#include "sync/syncable/write_transaction.h" using syncable::SYNCER; using syncable::WriteTransaction; diff --git a/sync/engine/conflict_resolver.cc b/sync/engine/conflict_resolver.cc index 3422f18..d775c1d 100644 --- a/sync/engine/conflict_resolver.cc +++ b/sync/engine/conflict_resolver.cc @@ -15,7 +15,9 @@ #include "sync/engine/syncer_util.h" #include "sync/protocol/nigori_specifics.pb.h" #include "sync/sessions/status_controller.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/mutable_entry.h" +#include "sync/syncable/write_transaction.h" #include "sync/util/cryptographer.h" using std::list; diff --git a/sync/engine/download_updates_command.cc b/sync/engine/download_updates_command.cc index ef41a76..cc8cf4e 100644 --- a/sync/engine/download_updates_command.cc +++ b/sync/engine/download_updates_command.cc @@ -11,7 +11,7 @@ #include "sync/engine/syncer_proto_util.h" #include "sync/engine/syncproto.h" #include "sync/internal_api/public/syncable/model_type_payload_map.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" using sync_pb::DebugInfo; diff --git a/sync/engine/get_commit_ids_command.cc b/sync/engine/get_commit_ids_command.cc index e8653b2..6936cc7 100644 --- a/sync/engine/get_commit_ids_command.cc +++ b/sync/engine/get_commit_ids_command.cc @@ -11,7 +11,9 @@ #include "sync/engine/nigori_util.h" #include "sync/engine/syncer_util.h" #include "sync/engine/throttled_data_type_tracker.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/entry.h" +#include "sync/syncable/mutable_entry.h" +#include "sync/syncable/write_transaction.h" #include "sync/util/cryptographer.h" using std::set; diff --git a/sync/engine/get_commit_ids_command.h b/sync/engine/get_commit_ids_command.h index 9aca7cd..3e18f60 100644 --- a/sync/engine/get_commit_ids_command.h +++ b/sync/engine/get_commit_ids_command.h @@ -14,6 +14,7 @@ #include "sync/engine/syncer_util.h" #include "sync/sessions/ordered_commit_set.h" #include "sync/sessions/sync_session.h" +#include "sync/syncable/directory.h" using std::pair; using std::vector; diff --git a/sync/engine/net/server_connection_manager.cc b/sync/engine/net/server_connection_manager.cc index c9a675f..5ffd6a7 100644 --- a/sync/engine/net/server_connection_manager.cc +++ b/sync/engine/net/server_connection_manager.cc @@ -18,7 +18,7 @@ #include "sync/engine/syncer.h" #include "sync/engine/syncproto.h" #include "sync/protocol/sync.pb.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" namespace browser_sync { diff --git a/sync/engine/nigori_util.cc b/sync/engine/nigori_util.cc index 78bb33c..0b34a02 100644 --- a/sync/engine/nigori_util.cc +++ b/sync/engine/nigori_util.cc @@ -10,7 +10,10 @@ #include "base/json/json_writer.h" #include "sync/engine/syncer_util.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/entry.h" +#include "sync/syncable/mutable_entry.h" +#include "sync/syncable/write_transaction.h" #include "sync/util/cryptographer.h" namespace syncable { diff --git a/sync/engine/process_commit_response_command.cc b/sync/engine/process_commit_response_command.cc index dfecd39..fbfe941 100644 --- a/sync/engine/process_commit_response_command.cc +++ b/sync/engine/process_commit_response_command.cc @@ -15,7 +15,11 @@ #include "sync/engine/syncer_util.h" #include "sync/engine/syncproto.h" #include "sync/sessions/sync_session.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/entry.h" +#include "sync/syncable/mutable_entry.h" +#include "sync/syncable/read_transaction.h" +#include "sync/syncable/syncable_util.h" +#include "sync/syncable/write_transaction.h" #include "sync/util/time.h" using syncable::WriteTransaction; diff --git a/sync/engine/process_commit_response_command_unittest.cc b/sync/engine/process_commit_response_command_unittest.cc index 195d58a..bd1b661 100644 --- a/sync/engine/process_commit_response_command_unittest.cc +++ b/sync/engine/process_commit_response_command_unittest.cc @@ -2,19 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "sync/engine/process_commit_response_command.h" + #include <vector> #include "base/location.h" #include "base/stringprintf.h" -#include "sync/engine/process_commit_response_command.h" +#include "sync/protocol/bookmark_specifics.pb.h" +#include "sync/protocol/sync.pb.h" #include "sync/sessions/sync_session.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/entry.h" +#include "sync/syncable/mutable_entry.h" +#include "sync/syncable/read_transaction.h" #include "sync/syncable/syncable_id.h" +#include "sync/syncable/write_transaction.h" #include "sync/test/engine/fake_model_worker.h" #include "sync/test/engine/syncer_command_test.h" #include "sync/test/engine/test_id_factory.h" -#include "sync/protocol/bookmark_specifics.pb.h" -#include "sync/protocol/sync.pb.h" #include "testing/gtest/include/gtest/gtest.h" namespace browser_sync { diff --git a/sync/engine/process_updates_command.cc b/sync/engine/process_updates_command.cc index 59c1d77..a7073a5 100644 --- a/sync/engine/process_updates_command.cc +++ b/sync/engine/process_updates_command.cc @@ -13,7 +13,10 @@ #include "sync/engine/syncer_util.h" #include "sync/engine/syncproto.h" #include "sync/sessions/sync_session.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/mutable_entry.h" +#include "sync/syncable/syncable_util.h" +#include "sync/syncable/write_transaction.h" #include "sync/util/cryptographer.h" using std::vector; diff --git a/sync/engine/resolve_conflicts_command.cc b/sync/engine/resolve_conflicts_command.cc index 457e4b7..bac365f 100644 --- a/sync/engine/resolve_conflicts_command.cc +++ b/sync/engine/resolve_conflicts_command.cc @@ -7,7 +7,8 @@ #include "sync/engine/conflict_resolver.h" #include "sync/sessions/session_state.h" #include "sync/sessions/sync_session.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/write_transaction.h" namespace browser_sync { diff --git a/sync/engine/store_timestamps_command.cc b/sync/engine/store_timestamps_command.cc index e942328..6791ad2 100644 --- a/sync/engine/store_timestamps_command.cc +++ b/sync/engine/store_timestamps_command.cc @@ -7,7 +7,7 @@ #include "sync/internal_api/public/syncable/model_type.h" #include "sync/sessions/status_controller.h" #include "sync/sessions/sync_session.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" namespace browser_sync { diff --git a/sync/engine/syncer.cc b/sync/engine/syncer.cc index 01c8ae6..da4e27b 100644 --- a/sync/engine/syncer.cc +++ b/sync/engine/syncer.cc @@ -25,13 +25,12 @@ #include "sync/engine/syncproto.h" #include "sync/engine/throttled_data_type_tracker.h" #include "sync/engine/verify_updates_command.h" +#include "sync/syncable/mutable_entry.h" #include "sync/syncable/syncable-inl.h" -#include "sync/syncable/syncable.h" using base::Time; using base::TimeDelta; using sync_pb::ClientCommand; -using syncable::Blob; using syncable::IS_UNAPPLIED_UPDATE; using syncable::SERVER_CTIME; using syncable::SERVER_IS_DEL; @@ -42,8 +41,6 @@ using syncable::SERVER_PARENT_ID; using syncable::SERVER_POSITION_IN_PARENT; using syncable::SERVER_SPECIFICS; using syncable::SERVER_VERSION; -using syncable::SYNCER; -using syncable::WriteTransaction; namespace browser_sync { diff --git a/sync/engine/syncer_proto_util.cc b/sync/engine/syncer_proto_util.cc index 294f0ef..116556f 100644 --- a/sync/engine/syncer_proto_util.cc +++ b/sync/engine/syncer_proto_util.cc @@ -16,8 +16,9 @@ #include "sync/protocol/sync_enums.pb.h" #include "sync/protocol/sync_protocol_error.h" #include "sync/sessions/sync_session.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/entry.h" #include "sync/syncable/syncable-inl.h" -#include "sync/syncable/syncable.h" #include "sync/util/time.h" using browser_sync::SyncProtocolErrorType; diff --git a/sync/engine/syncer_proto_util_unittest.cc b/sync/engine/syncer_proto_util_unittest.cc index fe9f0dc..7f81221 100644 --- a/sync/engine/syncer_proto_util_unittest.cc +++ b/sync/engine/syncer_proto_util_unittest.cc @@ -20,10 +20,9 @@ #include "sync/sessions/session_state.h" #include "sync/sessions/sync_session_context.h" #include "sync/syncable/blob.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" #include "sync/test/engine/mock_connection_manager.h" #include "sync/test/engine/test_directory_setter_upper.h" - #include "testing/gtest/include/gtest/gtest.h" using syncable::Blob; diff --git a/sync/engine/syncer_unittest.cc b/sync/engine/syncer_unittest.cc index cce69aa..81dfec5 100644 --- a/sync/engine/syncer_unittest.cc +++ b/sync/engine/syncer_unittest.cc @@ -41,7 +41,9 @@ #include "sync/protocol/preference_specifics.pb.h" #include "sync/protocol/sync.pb.h" #include "sync/sessions/sync_session_context.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/mutable_entry.h" +#include "sync/syncable/read_transaction.h" +#include "sync/syncable/write_transaction.h" #include "sync/test/engine/fake_model_worker.h" #include "sync/test/engine/mock_connection_manager.h" #include "sync/test/engine/test_directory_setter_upper.h" @@ -77,7 +79,6 @@ using syncable::WriteTransaction; using syncable::BASE_VERSION; using syncable::CREATE; -using syncable::CREATE_NEW_UPDATE_ITEM; using syncable::GET_BY_HANDLE; using syncable::GET_BY_ID; using syncable::GET_BY_CLIENT_TAG; @@ -95,7 +96,6 @@ using syncable::PARENT_ID; using syncable::PREV_ID; using syncable::BASE_SERVER_SPECIFICS; using syncable::SERVER_IS_DEL; -using syncable::SERVER_NON_UNIQUE_NAME; using syncable::SERVER_PARENT_ID; using syncable::SERVER_POSITION_IN_PARENT; using syncable::SERVER_SPECIFICS; diff --git a/sync/engine/syncer_util.cc b/sync/engine/syncer_util.cc index 4f184bd..0f734cd 100644 --- a/sync/engine/syncer_util.cc +++ b/sync/engine/syncer_util.cc @@ -21,20 +21,22 @@ #include "sync/protocol/nigori_specifics.pb.h" #include "sync/protocol/password_specifics.pb.h" #include "sync/protocol/sync.pb.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/entry.h" +#include "sync/syncable/mutable_entry.h" +#include "sync/syncable/read_transaction.h" #include "sync/syncable/syncable_changes_version.h" +#include "sync/syncable/syncable_util.h" +#include "sync/syncable/write_transaction.h" #include "sync/util/cryptographer.h" #include "sync/util/time.h" using syncable::BASE_VERSION; -using syncable::Blob; using syncable::CHANGES_VERSION; -using syncable::CREATE; using syncable::CREATE_NEW_UPDATE_ITEM; using syncable::CTIME; using syncable::Directory; using syncable::Entry; -using syncable::GetModelTypeFromSpecifics; using syncable::GET_BY_HANDLE; using syncable::GET_BY_ID; using syncable::ID; @@ -43,11 +45,9 @@ using syncable::IS_DIR; using syncable::IS_UNAPPLIED_UPDATE; using syncable::IS_UNSYNCED; using syncable::Id; -using syncable::IsRealDataType; using syncable::META_HANDLE; using syncable::MTIME; using syncable::MutableEntry; -using syncable::NEXT_ID; using syncable::NON_UNIQUE_NAME; using syncable::BASE_SERVER_SPECIFICS; using syncable::PARENT_ID; diff --git a/sync/engine/syncer_util.h b/sync/engine/syncer_util.h index 39c03bf..546ac3ff 100644 --- a/sync/engine/syncer_util.h +++ b/sync/engine/syncer_util.h @@ -15,7 +15,8 @@ #include "sync/engine/syncer.h" #include "sync/engine/syncer_types.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/entry_kernel.h" +#include "sync/syncable/metahandle_set.h" #include "sync/syncable/syncable_id.h" namespace browser_sync { diff --git a/sync/engine/update_applicator.cc b/sync/engine/update_applicator.cc index 5f510f3..921cfb5 100644 --- a/sync/engine/update_applicator.cc +++ b/sync/engine/update_applicator.cc @@ -9,8 +9,10 @@ #include "base/logging.h" #include "sync/engine/syncer_util.h" #include "sync/sessions/session_state.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/entry.h" +#include "sync/syncable/mutable_entry.h" #include "sync/syncable/syncable_id.h" +#include "sync/syncable/write_transaction.h" using std::vector; diff --git a/sync/engine/update_applicator.h b/sync/engine/update_applicator.h index 0e28180..cbd2b5e 100644 --- a/sync/engine/update_applicator.h +++ b/sync/engine/update_applicator.h @@ -17,7 +17,12 @@ #include "base/basictypes.h" #include "base/port.h" #include "sync/internal_api/public/engine/model_safe_worker.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/syncable_id.h" + +namespace syncable { +class WriteTransaction; +class Entry; +} namespace browser_sync { diff --git a/sync/engine/verify_updates_command.cc b/sync/engine/verify_updates_command.cc index 0ba0d16..40c75cf 100644 --- a/sync/engine/verify_updates_command.cc +++ b/sync/engine/verify_updates_command.cc @@ -14,15 +14,16 @@ #include "sync/engine/syncproto.h" #include "sync/internal_api/public/engine/model_safe_worker.h" #include "sync/protocol/bookmark_specifics.pb.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/entry.h" +#include "sync/syncable/mutable_entry.h" +#include "sync/syncable/write_transaction.h" namespace browser_sync { -using syncable::WriteTransaction; - using syncable::GET_BY_ID; using syncable::ModelTypeSet; using syncable::SYNCER; +using syncable::WriteTransaction; namespace { diff --git a/sync/engine/verify_updates_command_unittest.cc b/sync/engine/verify_updates_command_unittest.cc index 5f7356e..15324f4 100644 --- a/sync/engine/verify_updates_command_unittest.cc +++ b/sync/engine/verify_updates_command_unittest.cc @@ -7,7 +7,7 @@ #include "sync/protocol/bookmark_specifics.pb.h" #include "sync/sessions/session_state.h" #include "sync/sessions/sync_session.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/mutable_entry.h" #include "sync/syncable/syncable_id.h" #include "sync/test/engine/fake_model_worker.h" #include "sync/test/engine/syncer_command_test.h" @@ -15,13 +15,10 @@ namespace browser_sync { -using sessions::SyncSession; using sessions::StatusController; using std::string; -using syncable::Entry; using syncable::Id; using syncable::MutableEntry; -using syncable::ReadTransaction; using syncable::UNITTEST; using syncable::WriteTransaction; diff --git a/sync/internal_api/base_node.cc b/sync/internal_api/base_node.cc index cfcddcd..a66d452 100644 --- a/sync/internal_api/base_node.cc +++ b/sync/internal_api/base_node.cc @@ -20,7 +20,8 @@ #include "sync/protocol/session_specifics.pb.h" #include "sync/protocol/theme_specifics.pb.h" #include "sync/protocol/typed_url_specifics.pb.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/entry.h" #include "sync/syncable/syncable_id.h" #include "sync/util/time.h" diff --git a/sync/internal_api/base_transaction.cc b/sync/internal_api/base_transaction.cc index 5fedc64..23b6944 100644 --- a/sync/internal_api/base_transaction.cc +++ b/sync/internal_api/base_transaction.cc @@ -4,7 +4,7 @@ #include "sync/internal_api/public/base_transaction.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" #include "sync/util/cryptographer.h" using browser_sync::Cryptographer; diff --git a/sync/internal_api/change_reorder_buffer.cc b/sync/internal_api/change_reorder_buffer.cc index 34e44f6..5a53763 100644 --- a/sync/internal_api/change_reorder_buffer.cc +++ b/sync/internal_api/change_reorder_buffer.cc @@ -8,17 +8,17 @@ #include <queue> #include <set> #include <utility> // for pair<> -#include <vector> #include "sync/internal_api/public/read_node.h" #include "sync/internal_api/public/syncable/model_type.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/base_transaction.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/entry.h" using std::numeric_limits; using std::pair; using std::queue; using std::set; -using std::vector; namespace sync_api { diff --git a/sync/internal_api/public/base_transaction.h b/sync/internal_api/public/base_transaction.h index 77e102a..e2ed24a 100644 --- a/sync/internal_api/public/base_transaction.h +++ b/sync/internal_api/public/base_transaction.h @@ -26,7 +26,7 @@ namespace sync_api { // API BaseTransaction is created from a UserShare object. class BaseTransaction { public: - // Provide access to the underlying syncable.h objects from BaseNode. + // Provide access to the underlying syncable objects from BaseNode. virtual syncable::BaseTransaction* GetWrappedTrans() const = 0; browser_sync::Cryptographer* GetCryptographer() const; diff --git a/sync/internal_api/public/write_transaction.h b/sync/internal_api/public/write_transaction.h index 1321ed1..fcc372d 100644 --- a/sync/internal_api/public/write_transaction.h +++ b/sync/internal_api/public/write_transaction.h @@ -32,7 +32,7 @@ class WriteTransaction : public BaseTransaction { UserShare* share); virtual ~WriteTransaction(); - // Provide access to the syncable.h transaction from the API WriteNode. + // Provide access to the syncable transaction from the API WriteNode. virtual syncable::BaseTransaction* GetWrappedTrans() const OVERRIDE; syncable::WriteTransaction* GetWrappedWriteTrans() { return transaction_; } diff --git a/sync/internal_api/read_node.cc b/sync/internal_api/read_node.cc index 970f981..f09f5f8 100644 --- a/sync/internal_api/read_node.cc +++ b/sync/internal_api/read_node.cc @@ -6,7 +6,8 @@ #include "base/logging.h" #include "sync/internal_api/public/base_transaction.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/base_transaction.h" +#include "sync/syncable/entry.h" namespace sync_api { diff --git a/sync/internal_api/read_transaction.cc b/sync/internal_api/read_transaction.cc index 1af6c21..256193c 100644 --- a/sync/internal_api/read_transaction.cc +++ b/sync/internal_api/read_transaction.cc @@ -4,7 +4,7 @@ #include "sync/internal_api/public/read_transaction.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/read_transaction.h" namespace sync_api { diff --git a/sync/internal_api/sync_manager.cc b/sync/internal_api/sync_manager.cc index 6982750..96a1ceb 100644 --- a/sync/internal_api/sync_manager.cc +++ b/sync/internal_api/sync_manager.cc @@ -52,8 +52,9 @@ #include "sync/protocol/encryption.pb.h" #include "sync/protocol/proto_value_conversions.h" #include "sync/protocol/sync.pb.h" +#include "sync/syncable/directory.h" #include "sync/syncable/directory_change_delegate.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/entry.h" #include "sync/util/cryptographer.h" #include "sync/util/get_session_name.h" #include "sync/util/time.h" diff --git a/sync/internal_api/syncapi_unittest.cc b/sync/internal_api/syncapi_unittest.cc index 2aa44f0..b12c048 100644 --- a/sync/internal_api/syncapi_unittest.cc +++ b/sync/internal_api/syncapi_unittest.cc @@ -52,8 +52,11 @@ #include "sync/protocol/proto_value_conversions.h" #include "sync/protocol/sync.pb.h" #include "sync/sessions/sync_session.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/entry.h" +#include "sync/syncable/mutable_entry.h" #include "sync/syncable/syncable_id.h" +#include "sync/syncable/write_transaction.h" #include "sync/test/fake_encryptor.h" #include "sync/test/fake_extensions_activity_monitor.h" #include "sync/util/cryptographer.h" diff --git a/sync/internal_api/test/test_user_share.cc b/sync/internal_api/test/test_user_share.cc index c028d66..2fc0d60 100644 --- a/sync/internal_api/test/test_user_share.cc +++ b/sync/internal_api/test/test_user_share.cc @@ -5,6 +5,7 @@ #include "sync/internal_api/public/test/test_user_share.h" #include "base/compiler_specific.h" +#include "sync/syncable/directory.h" #include "sync/test/engine/test_directory_setter_upper.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/sync/internal_api/user_share.cc b/sync/internal_api/user_share.cc index 9bf0b46..57b26bc 100644 --- a/sync/internal_api/user_share.cc +++ b/sync/internal_api/user_share.cc @@ -4,7 +4,7 @@ #include "sync/internal_api/public/user_share.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" namespace sync_api { diff --git a/sync/internal_api/write_node.cc b/sync/internal_api/write_node.cc index bbd3035..e281fcd 100644 --- a/sync/internal_api/write_node.cc +++ b/sync/internal_api/write_node.cc @@ -18,7 +18,7 @@ #include "sync/protocol/session_specifics.pb.h" #include "sync/protocol/theme_specifics.pb.h" #include "sync/protocol/typed_url_specifics.pb.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/mutable_entry.h" #include "sync/util/cryptographer.h" using browser_sync::Cryptographer; diff --git a/sync/internal_api/write_transaction.cc b/sync/internal_api/write_transaction.cc index a354bcd..867a88c 100644 --- a/sync/internal_api/write_transaction.cc +++ b/sync/internal_api/write_transaction.cc @@ -4,7 +4,7 @@ #include "sync/internal_api/public/write_transaction.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/write_transaction.h" namespace sync_api { diff --git a/sync/sessions/sync_session.cc b/sync/sessions/sync_session.cc index 36bc2ca..eed4f88 100644 --- a/sync/sessions/sync_session.cc +++ b/sync/sessions/sync_session.cc @@ -9,7 +9,7 @@ #include "base/logging.h" #include "sync/internal_api/public/syncable/model_type.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" namespace browser_sync { namespace sessions { diff --git a/sync/sessions/sync_session_unittest.cc b/sync/sessions/sync_session_unittest.cc index aa3dd7e..fa5e986 100644 --- a/sync/sessions/sync_session_unittest.cc +++ b/sync/sessions/sync_session_unittest.cc @@ -14,8 +14,8 @@ #include "sync/internal_api/public/syncable/model_type.h" #include "sync/sessions/session_state.h" #include "sync/sessions/status_controller.h" -#include "sync/syncable/syncable.h" #include "sync/syncable/syncable_id.h" +#include "sync/syncable/write_transaction.h" #include "sync/test/engine/fake_model_worker.h" #include "sync/test/engine/test_directory_setter_upper.h" #include "sync/test/fake_extensions_activity_monitor.h" diff --git a/sync/sync.gyp b/sync/sync.gyp index 318f34a..8202ea2 100644 --- a/sync/sync.gyp +++ b/sync/sync.gyp @@ -148,26 +148,44 @@ 'sessions/sync_session.h', 'sessions/sync_session_context.cc', 'sessions/sync_session_context.h', + 'syncable/base_transaction.cc', + 'syncable/base_transaction.h', 'syncable/blob.h', + 'syncable/dir_open_result.h', + 'syncable/directory.cc', + 'syncable/directory.h', 'syncable/directory_backing_store.cc', 'syncable/directory_backing_store.h', 'syncable/directory_change_delegate.h', - 'syncable/dir_open_result.h', + 'syncable/entry.cc', + 'syncable/entry.h', + 'syncable/entry_kernel.cc', + 'syncable/entry_kernel.h', 'syncable/in_memory_directory_backing_store.cc', 'syncable/in_memory_directory_backing_store.h', + 'syncable/metahandle_set.h', 'syncable/model_type.cc', + 'syncable/mutable_entry.cc', + 'syncable/mutable_entry.h', 'syncable/on_disk_directory_backing_store.cc', 'syncable/on_disk_directory_backing_store.h', - 'syncable/syncable.cc', + 'syncable/read_transaction.cc', + 'syncable/read_transaction.h', + 'syncable/scoped_kernel_lock.h', + 'syncable/syncable-inl.h', 'syncable/syncable_changes_version.h', 'syncable/syncable_columns.h', 'syncable/syncable_enum_conversions.cc', 'syncable/syncable_enum_conversions.h', - 'syncable/syncable.h', 'syncable/syncable_id.cc', 'syncable/syncable_id.h', - 'syncable/syncable-inl.h', + 'syncable/syncable_util.cc', + 'syncable/syncable_util.h', 'syncable/transaction_observer.h', + 'syncable/write_transaction.cc', + 'syncable/write_transaction.h', + 'syncable/write_transaction_info.cc', + 'syncable/write_transaction_info.h', 'util/cryptographer.cc', 'util/cryptographer.h', diff --git a/sync/syncable/base_transaction.cc b/sync/syncable/base_transaction.cc new file mode 100644 index 0000000..b27e31a --- /dev/null +++ b/sync/syncable/base_transaction.cc @@ -0,0 +1,68 @@ +// 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 "sync/syncable/base_transaction.h" + +#include "base/debug/trace_event.h" +#include "sync/syncable/directory.h" + +namespace syncable { + +void BaseTransaction::Lock() { + TRACE_EVENT2("sync_lock_contention", "AcquireLock", + "src_file", from_here_.file_name(), + "src_func", from_here_.function_name()); + + directory_->kernel_->transaction_mutex.Acquire(); +} + +void BaseTransaction::Unlock() { + directory_->kernel_->transaction_mutex.Release(); +} + +void BaseTransaction::OnUnrecoverableError( + const tracked_objects::Location& location, + const std::string& message) { + unrecoverable_error_set_ = true; + unrecoverable_error_location_ = location; + unrecoverable_error_msg_ = message; + + // Note: We dont call the Directory's OnUnrecoverableError method right + // away. Instead we wait to unwind the stack and in the destructor of the + // transaction we would call the OnUnrecoverableError method. + + directory()->ReportUnrecoverableError(); +} + +bool BaseTransaction::unrecoverable_error_set() const { + return unrecoverable_error_set_; +} + +void BaseTransaction::HandleUnrecoverableErrorIfSet() { + if (unrecoverable_error_set_) { + directory()->OnUnrecoverableError(this, + unrecoverable_error_location_, + unrecoverable_error_msg_); + } +} + +BaseTransaction::BaseTransaction(const tracked_objects::Location& from_here, + const char* name, + WriterTag writer, + Directory* directory) + : from_here_(from_here), name_(name), writer_(writer), + directory_(directory), unrecoverable_error_set_(false) { + // TODO(lipalani): Don't issue a good transaction if the directory has + // unrecoverable error set. And the callers have to check trans.good before + // proceeding. + TRACE_EVENT_BEGIN2("sync", name_, + "src_file", from_here_.file_name(), + "src_func", from_here_.function_name()); +} + +BaseTransaction::~BaseTransaction() { + TRACE_EVENT_END0("sync", name_); +} + +} // namespace syncable diff --git a/sync/syncable/base_transaction.h b/sync/syncable/base_transaction.h new file mode 100644 index 0000000..2de4608 --- /dev/null +++ b/sync/syncable/base_transaction.h @@ -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. + +#ifndef SYNC_SYNCABLE_BASE_TRANSACTION_H_ +#define SYNC_SYNCABLE_BASE_TRANSACTION_H_ +#pragma once + +#include "base/location.h" +#include "sync/syncable/syncable_id.h" + +namespace syncable { + +class Directory; + +// A WriteTransaction has a writer tag describing which body of code is doing +// the write. This is defined up here since WriteTransactionInfo also contains +// one. +enum WriterTag { + INVALID, + SYNCER, + AUTHWATCHER, + UNITTEST, + VACUUM_AFTER_SAVE, + PURGE_ENTRIES, + SYNCAPI +}; + +// Make sure to update this if you update WriterTag. +std::string WriterTagToString(WriterTag writer_tag); + +class BaseTransaction { + public: + inline Directory* directory() const { return directory_; } + inline Id root_id() const { return Id(); } + + virtual ~BaseTransaction(); + + // This should be called when a database corruption is detected and there is + // no way for us to recover short of wiping the database clean. When this is + // called we set a bool in the transaction. The caller has to unwind the + // stack. When the destructor for the transaction is called it acts upon the + // bool and calls the Directory to handle the unrecoverable error. + void OnUnrecoverableError(const tracked_objects::Location& location, + const std::string& message); + + bool unrecoverable_error_set() const; + + protected: + BaseTransaction(const tracked_objects::Location& from_here, + const char* name, + WriterTag writer, + Directory* directory); + + void Lock(); + void Unlock(); + + // This should be called before unlocking because it calls the Direcotry's + // OnUnrecoverableError method which is not protected by locks and could + // be called from any thread. Holding the transaction lock ensures only one + // thread could call the method at a time. + void HandleUnrecoverableErrorIfSet(); + + const tracked_objects::Location from_here_; + const char* const name_; + WriterTag writer_; + Directory* const directory_; + + // Error information. + bool unrecoverable_error_set_; + tracked_objects::Location unrecoverable_error_location_; + std::string unrecoverable_error_msg_; + + private: + friend class Entry; + DISALLOW_COPY_AND_ASSIGN(BaseTransaction); +}; + +} + +#endif // SYNC_SYNCABLE_BASE_TRANSACTION_H_ diff --git a/sync/syncable/syncable.cc b/sync/syncable/directory.cc index 7bf16fc..40179b2 100644 --- a/sync/syncable/syncable.cc +++ b/sync/syncable/directory.cc @@ -2,61 +2,45 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "sync/syncable/syncable.h" - -#include <algorithm> -#include <cstring> -#include <functional> -#include <iomanip> -#include <iterator> -#include <limits> -#include <set> -#include <string> - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/debug/trace_event.h" +#include "sync/syncable/directory.h" + #include "base/debug/trace_event.h" -#include "base/file_util.h" -#include "base/hash_tables.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" #include "base/perftimer.h" #include "base/stl_util.h" #include "base/string_number_conversions.h" -#include "base/string_util.h" -#include "base/time.h" -#include "base/utf_string_conversions.h" -#include "base/values.h" -#include "net/base/escape.h" -#include "sync/internal_api/public/syncable/model_type.h" -#include "sync/protocol/proto_value_conversions.h" -#include "sync/syncable/directory_backing_store.h" -#include "sync/syncable/directory_change_delegate.h" +#include "sync/internal_api/public/util/unrecoverable_error_handler.h" +#include "sync/syncable/base_transaction.h" +#include "sync/syncable/entry.h" +#include "sync/syncable/entry_kernel.h" #include "sync/syncable/in_memory_directory_backing_store.h" #include "sync/syncable/on_disk_directory_backing_store.h" +#include "sync/syncable/read_transaction.h" +#include "sync/syncable/scoped_index_updater.h" #include "sync/syncable/syncable-inl.h" #include "sync/syncable/syncable_changes_version.h" -#include "sync/syncable/syncable_columns.h" -#include "sync/syncable/syncable_enum_conversions.h" -#include "sync/syncable/transaction_observer.h" -#include "sync/util/cryptographer.h" -#include "sync/util/logging.h" - -namespace { +#include "sync/syncable/syncable_util.h" +#include "sync/syncable/write_transaction.h" -enum InvariantCheckLevel { - OFF = 0, - VERIFY_IN_MEMORY = 1, - FULL_DB_VERIFICATION = 2 -}; +using browser_sync::Encryptor; +using browser_sync::ReportUnrecoverableErrorFunction; +using browser_sync::UnrecoverableErrorHandler; +using std::string; -const InvariantCheckLevel kInvariantCheckLevel = VERIFY_IN_MEMORY; +namespace syncable { +namespace { // Max number of milliseconds to spend checking syncable entry invariants const int kInvariantCheckMaxMs = 50; +// Helper function to add an item to the index, if it ought to be added. +template<typename Indexer> +void InitializeIndexEntry(EntryKernel* entry, + typename Index<Indexer>::Set* index) { + if (Indexer::ShouldInclude(entry)) { + index->insert(entry); + } +} + // This function checks to see if the given list of Metahandles has any nodes // whose PREV_ID, PARENT_ID or NEXT_ID values refer to ID values that do not // actually exist. Returns true on success. @@ -92,161 +76,8 @@ bool VerifyReferenceIntegrityUnsafe(const syncable::MetahandlesIndex &index) { return is_ok; } -} // namespace - -using std::string; -using browser_sync::Encryptor; -using browser_sync::ReportUnrecoverableErrorFunction; -using browser_sync::UnrecoverableErrorHandler; - -namespace syncable { - -namespace { - -// Function to handle runtime failures on syncable code. Rather than crashing, -// if the |condition| is false the following will happen: -// 1. Sets unrecoverable error on transaction. -// 2. Returns false. -bool SyncAssert(bool condition, - const tracked_objects::Location& location, - const char* msg, - BaseTransaction* trans) { - if (!condition) { - trans->OnUnrecoverableError(location, msg); - return false; - } - return true; -} - -} // namespace - -#define ENUM_CASE(x) case x: return #x; break - -std::string WriterTagToString(WriterTag writer_tag) { - switch (writer_tag) { - ENUM_CASE(INVALID); - ENUM_CASE(SYNCER); - ENUM_CASE(AUTHWATCHER); - ENUM_CASE(UNITTEST); - ENUM_CASE(VACUUM_AFTER_SAVE); - ENUM_CASE(PURGE_ENTRIES); - ENUM_CASE(SYNCAPI); - }; - NOTREACHED(); - return ""; -} - -#undef ENUM_CASE - -WriteTransactionInfo::WriteTransactionInfo( - int64 id, - tracked_objects::Location location, - WriterTag writer, - ImmutableEntryKernelMutationMap mutations) - : id(id), - location_string(location.ToString()), - writer(writer), - mutations(mutations) {} - -WriteTransactionInfo::WriteTransactionInfo() - : id(-1), writer(INVALID) {} - -WriteTransactionInfo::~WriteTransactionInfo() {} - -base::DictionaryValue* WriteTransactionInfo::ToValue( - size_t max_mutations_size) const { - DictionaryValue* dict = new DictionaryValue(); - dict->SetString("id", base::Int64ToString(id)); - dict->SetString("location", location_string); - dict->SetString("writer", WriterTagToString(writer)); - Value* mutations_value = NULL; - const size_t mutations_size = mutations.Get().size(); - if (mutations_size <= max_mutations_size) { - mutations_value = EntryKernelMutationMapToValue(mutations.Get()); - } else { - mutations_value = - Value::CreateStringValue( - base::Uint64ToString(static_cast<uint64>(mutations_size)) + - " mutations"); - } - dict->Set("mutations", mutations_value); - return dict; -} - -DictionaryValue* EntryKernelMutationToValue( - const EntryKernelMutation& mutation) { - DictionaryValue* dict = new DictionaryValue(); - dict->Set("original", mutation.original.ToValue()); - dict->Set("mutated", mutation.mutated.ToValue()); - return dict; -} - -ListValue* EntryKernelMutationMapToValue( - const EntryKernelMutationMap& mutations) { - ListValue* list = new ListValue(); - for (EntryKernelMutationMap::const_iterator it = mutations.begin(); - it != mutations.end(); ++it) { - list->Append(EntryKernelMutationToValue(it->second)); - } - return list; -} - -namespace { - -// A ScopedIndexUpdater temporarily removes an entry from an index, -// and restores it to the index when the scope exits. This simplifies -// the common pattern where items need to be removed from an index -// before updating the field. -// -// This class is parameterized on the Indexer traits type, which -// must define a Comparator and a static bool ShouldInclude -// function for testing whether the item ought to be included -// in the index. -template<typename Indexer> -class ScopedIndexUpdater { - public: - ScopedIndexUpdater(const ScopedKernelLock& proof_of_lock, - EntryKernel* entry, - typename Index<Indexer>::Set* index) - : entry_(entry), - index_(index) { - // First call to ShouldInclude happens before the field is updated. - if (Indexer::ShouldInclude(entry_)) { - // TODO(lipalani): Replace this CHECK with |SyncAssert| by refactorting - // this class into a function. - CHECK(index_->erase(entry_)); - } - } - - ~ScopedIndexUpdater() { - // Second call to ShouldInclude happens after the field is updated. - if (Indexer::ShouldInclude(entry_)) { - // TODO(lipalani): Replace this CHECK with |SyncAssert| by refactorting - // this class into a function. - CHECK(index_->insert(entry_).second); - } - } - private: - // The entry that was temporarily removed from the index. - EntryKernel* entry_; - // The index which we are updating. - typename Index<Indexer>::Set* const index_; -}; - -// Helper function to add an item to the index, if it ought to be added. -template<typename Indexer> -void InitializeIndexEntry(EntryKernel* entry, - typename Index<Indexer>::Set* index) { - if (Indexer::ShouldInclude(entry)) { - index->insert(entry); - } } -} // namespace - -/////////////////////////////////////////////////////////////////////////// -// Comparator and filter functions for the indices. - // static bool ClientTagIndexer::ShouldInclude(const EntryKernel* a) { return !a->ref(UNIQUE_CLIENT_TAG).empty(); @@ -275,132 +106,6 @@ bool ParentIdAndHandleIndexer::ShouldInclude(const EntryKernel* a) { return !a->ref(IS_DEL) && !a->ref(ID).IsRoot(); } -/////////////////////////////////////////////////////////////////////////// -// EntryKernel - -EntryKernel::EntryKernel() : dirty_(false) { - // Everything else should already be default-initialized. - for (int i = INT64_FIELDS_BEGIN; i < INT64_FIELDS_END; ++i) { - int64_fields[i] = 0; - } -} - -EntryKernel::~EntryKernel() {} - -syncable::ModelType EntryKernel::GetServerModelType() const { - ModelType specifics_type = GetModelTypeFromSpecifics(ref(SERVER_SPECIFICS)); - if (specifics_type != UNSPECIFIED) - return specifics_type; - if (ref(ID).IsRoot()) - return TOP_LEVEL_FOLDER; - // Loose check for server-created top-level folders that aren't - // bound to a particular model type. - if (!ref(UNIQUE_SERVER_TAG).empty() && ref(SERVER_IS_DIR)) - return TOP_LEVEL_FOLDER; - - return UNSPECIFIED; -} - -namespace { - -// Utility function to loop through a set of enum values and add the -// field keys/values in the kernel to the given dictionary. -// -// V should be convertible to Value. -template <class T, class U, class V> -void SetFieldValues(const EntryKernel& kernel, - DictionaryValue* dictionary_value, - const char* (*enum_key_fn)(T), - V* (*enum_value_fn)(U), - int field_key_min, int field_key_max) { - DCHECK_LE(field_key_min, field_key_max); - for (int i = field_key_min; i <= field_key_max; ++i) { - T field = static_cast<T>(i); - const std::string& key = enum_key_fn(field); - V* value = enum_value_fn(kernel.ref(field)); - dictionary_value->Set(key, value); - } -} - -// Helper functions for SetFieldValues(). - -StringValue* Int64ToValue(int64 i) { - return Value::CreateStringValue(base::Int64ToString(i)); -} - -StringValue* TimeToValue(const base::Time& t) { - return Value::CreateStringValue(browser_sync::GetTimeDebugString(t)); -} - -StringValue* IdToValue(const Id& id) { - return id.ToValue(); -} - -} // namespace - -DictionaryValue* EntryKernel::ToValue() const { - DictionaryValue* kernel_info = new DictionaryValue(); - kernel_info->SetBoolean("isDirty", is_dirty()); - kernel_info->Set("serverModelType", ModelTypeToValue(GetServerModelType())); - - // Int64 fields. - SetFieldValues(*this, kernel_info, - &GetMetahandleFieldString, &Int64ToValue, - INT64_FIELDS_BEGIN, META_HANDLE); - SetFieldValues(*this, kernel_info, - &GetBaseVersionString, &Int64ToValue, - META_HANDLE + 1, BASE_VERSION); - SetFieldValues(*this, kernel_info, - &GetInt64FieldString, &Int64ToValue, - BASE_VERSION + 1, INT64_FIELDS_END - 1); - - // Time fields. - SetFieldValues(*this, kernel_info, - &GetTimeFieldString, &TimeToValue, - TIME_FIELDS_BEGIN, TIME_FIELDS_END - 1); - - // ID fields. - SetFieldValues(*this, kernel_info, - &GetIdFieldString, &IdToValue, - ID_FIELDS_BEGIN, ID_FIELDS_END - 1); - - // Bit fields. - SetFieldValues(*this, kernel_info, - &GetIndexedBitFieldString, &Value::CreateBooleanValue, - BIT_FIELDS_BEGIN, INDEXED_BIT_FIELDS_END - 1); - SetFieldValues(*this, kernel_info, - &GetIsDelFieldString, &Value::CreateBooleanValue, - INDEXED_BIT_FIELDS_END, IS_DEL); - SetFieldValues(*this, kernel_info, - &GetBitFieldString, &Value::CreateBooleanValue, - IS_DEL + 1, BIT_FIELDS_END - 1); - - // String fields. - { - // Pick out the function overload we want. - StringValue* (*string_to_value)(const std::string&) = - &Value::CreateStringValue; - SetFieldValues(*this, kernel_info, - &GetStringFieldString, string_to_value, - STRING_FIELDS_BEGIN, STRING_FIELDS_END - 1); - } - - // Proto fields. - SetFieldValues(*this, kernel_info, - &GetProtoFieldString, &browser_sync::EntitySpecificsToValue, - PROTO_FIELDS_BEGIN, PROTO_FIELDS_END - 1); - - // Bit temps. - SetFieldValues(*this, kernel_info, - &GetBitTempString, &Value::CreateBooleanValue, - BIT_TEMPS_BEGIN, BIT_TEMPS_END - 1); - - return kernel_info; -} - -/////////////////////////////////////////////////////////////////////////// -// Directory - // static const FilePath::CharType Directory::kSyncDatabaseFilename[] = FILE_PATH_LITERAL("SyncData.sqlite3"); @@ -1161,7 +866,6 @@ void Directory::GetUnappliedUpdateMetaHandles( } } - class IdFilter { public: virtual ~IdFilter() { } @@ -1370,613 +1074,6 @@ bool Directory::CheckTreeInvariants(syncable::BaseTransaction* trans, return true; } -/////////////////////////////////////////////////////////////////////////////// -// ScopedKernelLock - -ScopedKernelLock::ScopedKernelLock(const Directory* dir) - : scoped_lock_(dir->kernel_->mutex), dir_(const_cast<Directory*>(dir)) { -} - -/////////////////////////////////////////////////////////////////////////// -// Transactions - -void BaseTransaction::Lock() { - TRACE_EVENT2("sync_lock_contention", "AcquireLock", - "src_file", from_here_.file_name(), - "src_func", from_here_.function_name()); - - dirkernel_->transaction_mutex.Acquire(); -} - -void BaseTransaction::Unlock() { - dirkernel_->transaction_mutex.Release(); -} - -void BaseTransaction::OnUnrecoverableError( - const tracked_objects::Location& location, - const std::string& message) { - unrecoverable_error_set_ = true; - unrecoverable_error_location_ = location; - unrecoverable_error_msg_ = message; - - // Note: We dont call the Directory's OnUnrecoverableError method right - // away. Instead we wait to unwind the stack and in the destructor of the - // transaction we would call the OnUnrecoverableError method. - - directory()->ReportUnrecoverableError(); -} - -bool BaseTransaction::unrecoverable_error_set() const { - return unrecoverable_error_set_; -} - -void BaseTransaction::HandleUnrecoverableErrorIfSet() { - if (unrecoverable_error_set_) { - directory()->OnUnrecoverableError(this, - unrecoverable_error_location_, - unrecoverable_error_msg_); - } -} - -BaseTransaction::BaseTransaction(const tracked_objects::Location& from_here, - const char* name, - WriterTag writer, - Directory* directory) - : from_here_(from_here), name_(name), writer_(writer), - directory_(directory), dirkernel_(directory->kernel_), - unrecoverable_error_set_(false) { - // TODO(lipalani): Don't issue a good transaction if the directory has - // unrecoverable error set. And the callers have to check trans.good before - // proceeding. - TRACE_EVENT_BEGIN2("sync", name_, - "src_file", from_here_.file_name(), - "src_func", from_here_.function_name()); -} - -BaseTransaction::~BaseTransaction() { - TRACE_EVENT_END0("sync", name_); -} - -ReadTransaction::ReadTransaction(const tracked_objects::Location& location, - Directory* directory) - : BaseTransaction(location, "ReadTransaction", INVALID, directory) { - Lock(); -} - -ReadTransaction::~ReadTransaction() { - HandleUnrecoverableErrorIfSet(); - Unlock(); -} - -WriteTransaction::WriteTransaction(const tracked_objects::Location& location, - WriterTag writer, Directory* directory) - : BaseTransaction(location, "WriteTransaction", writer, directory) { - Lock(); -} - -void WriteTransaction::SaveOriginal(const EntryKernel* entry) { - if (!entry) { - return; - } - // Insert only if it's not already there. - const int64 handle = entry->ref(META_HANDLE); - EntryKernelMutationMap::iterator it = mutations_.lower_bound(handle); - if (it == mutations_.end() || it->first != handle) { - EntryKernelMutation mutation; - mutation.original = *entry; - ignore_result(mutations_.insert(it, std::make_pair(handle, mutation))); - } -} - -ImmutableEntryKernelMutationMap WriteTransaction::RecordMutations() { - dirkernel_->transaction_mutex.AssertAcquired(); - for (syncable::EntryKernelMutationMap::iterator it = mutations_.begin(); - it != mutations_.end();) { - EntryKernel* kernel = directory()->GetEntryByHandle(it->first); - if (!kernel) { - NOTREACHED(); - continue; - } - if (kernel->is_dirty()) { - it->second.mutated = *kernel; - ++it; - } else { - DCHECK(!it->second.original.is_dirty()); - // Not actually mutated, so erase from |mutations_|. - mutations_.erase(it++); - } - } - return ImmutableEntryKernelMutationMap(&mutations_); -} - -void WriteTransaction::UnlockAndNotify( - const ImmutableEntryKernelMutationMap& mutations) { - // Work while transaction mutex is held. - ModelTypeSet models_with_changes; - bool has_mutations = !mutations.Get().empty(); - if (has_mutations) { - models_with_changes = NotifyTransactionChangingAndEnding(mutations); - } - Unlock(); - - // Work after mutex is relased. - if (has_mutations) { - NotifyTransactionComplete(models_with_changes); - } -} - -ModelTypeSet WriteTransaction::NotifyTransactionChangingAndEnding( - const ImmutableEntryKernelMutationMap& mutations) { - dirkernel_->transaction_mutex.AssertAcquired(); - DCHECK(!mutations.Get().empty()); - - WriteTransactionInfo write_transaction_info( - dirkernel_->next_write_transaction_id, from_here_, writer_, mutations); - ++dirkernel_->next_write_transaction_id; - - ImmutableWriteTransactionInfo immutable_write_transaction_info( - &write_transaction_info); - DirectoryChangeDelegate* const delegate = dirkernel_->delegate; - if (writer_ == syncable::SYNCAPI) { - delegate->HandleCalculateChangesChangeEventFromSyncApi( - immutable_write_transaction_info, this); - } else { - delegate->HandleCalculateChangesChangeEventFromSyncer( - immutable_write_transaction_info, this); - } - - ModelTypeSet models_with_changes = - delegate->HandleTransactionEndingChangeEvent( - immutable_write_transaction_info, this); - - dirkernel_->transaction_observer.Call(FROM_HERE, - &TransactionObserver::OnTransactionWrite, - immutable_write_transaction_info, models_with_changes); - - return models_with_changes; -} - -void WriteTransaction::NotifyTransactionComplete( - ModelTypeSet models_with_changes) { - dirkernel_->delegate->HandleTransactionCompleteChangeEvent( - models_with_changes); -} - -WriteTransaction::~WriteTransaction() { - const ImmutableEntryKernelMutationMap& mutations = RecordMutations(); - - if (!unrecoverable_error_set_) { - if (OFF != kInvariantCheckLevel) { - const bool full_scan = (FULL_DB_VERIFICATION == kInvariantCheckLevel); - if (full_scan) - directory()->CheckTreeInvariants(this, full_scan); - else - directory()->CheckTreeInvariants(this, mutations.Get()); - } - } - - // |CheckTreeInvariants| could have thrown an unrecoverable error. - if (unrecoverable_error_set_) { - HandleUnrecoverableErrorIfSet(); - Unlock(); - return; - } - - UnlockAndNotify(mutations); -} - -/////////////////////////////////////////////////////////////////////////// -// Entry - -Entry::Entry(BaseTransaction* trans, GetById, const Id& id) - : basetrans_(trans) { - kernel_ = trans->directory()->GetEntryById(id); -} - -Entry::Entry(BaseTransaction* trans, GetByClientTag, const string& tag) - : basetrans_(trans) { - kernel_ = trans->directory()->GetEntryByClientTag(tag); -} - -Entry::Entry(BaseTransaction* trans, GetByServerTag, const string& tag) - : basetrans_(trans) { - kernel_ = trans->directory()->GetEntryByServerTag(tag); -} - -Entry::Entry(BaseTransaction* trans, GetByHandle, int64 metahandle) - : basetrans_(trans) { - kernel_ = trans->directory()->GetEntryByHandle(metahandle); -} - -Directory* Entry::dir() const { - return basetrans_->directory(); -} - -Id Entry::ComputePrevIdFromServerPosition(const Id& parent_id) const { - return dir()->ComputePrevIdFromServerPosition(kernel_, parent_id); -} - -DictionaryValue* Entry::ToValue() const { - DictionaryValue* entry_info = new DictionaryValue(); - entry_info->SetBoolean("good", good()); - if (good()) { - entry_info->Set("kernel", kernel_->ToValue()); - entry_info->Set("modelType", - ModelTypeToValue(GetModelType())); - entry_info->SetBoolean("existsOnClientBecauseNameIsNonEmpty", - ExistsOnClientBecauseNameIsNonEmpty()); - entry_info->SetBoolean("isRoot", IsRoot()); - } - return entry_info; -} - -const string& Entry::Get(StringField field) const { - DCHECK(kernel_); - return kernel_->ref(field); -} - -syncable::ModelType Entry::GetServerModelType() const { - ModelType specifics_type = kernel_->GetServerModelType(); - if (specifics_type != UNSPECIFIED) - return specifics_type; - - // Otherwise, we don't have a server type yet. That should only happen - // if the item is an uncommitted locally created item. - // It's possible we'll need to relax these checks in the future; they're - // just here for now as a safety measure. - DCHECK(Get(IS_UNSYNCED)); - DCHECK_EQ(Get(SERVER_VERSION), 0); - DCHECK(Get(SERVER_IS_DEL)); - // Note: can't enforce !Get(ID).ServerKnows() here because that could - // actually happen if we hit AttemptReuniteLostCommitResponses. - return UNSPECIFIED; -} - -syncable::ModelType Entry::GetModelType() const { - ModelType specifics_type = GetModelTypeFromSpecifics(Get(SPECIFICS)); - if (specifics_type != UNSPECIFIED) - return specifics_type; - if (IsRoot()) - return TOP_LEVEL_FOLDER; - // Loose check for server-created top-level folders that aren't - // bound to a particular model type. - if (!Get(UNIQUE_SERVER_TAG).empty() && Get(IS_DIR)) - return TOP_LEVEL_FOLDER; - - return UNSPECIFIED; -} - -/////////////////////////////////////////////////////////////////////////// -// MutableEntry - -MutableEntry::MutableEntry(WriteTransaction* trans, Create, - const Id& parent_id, const string& name) - : Entry(trans), - write_transaction_(trans) { - Init(trans, parent_id, name); -} - - -void MutableEntry::Init(WriteTransaction* trans, const Id& parent_id, - const string& name) { - scoped_ptr<EntryKernel> kernel(new EntryKernel); - kernel_ = NULL; - - kernel->put(ID, trans->directory_->NextId()); - kernel->put(META_HANDLE, trans->directory_->NextMetahandle()); - kernel->mark_dirty(trans->directory_->kernel_->dirty_metahandles); - kernel->put(PARENT_ID, parent_id); - kernel->put(NON_UNIQUE_NAME, name); - const base::Time& now = base::Time::Now(); - kernel->put(CTIME, now); - kernel->put(MTIME, now); - // We match the database defaults here - kernel->put(BASE_VERSION, CHANGES_VERSION); - if (!trans->directory()->InsertEntry(trans, kernel.get())) { - return; // We failed inserting, nothing more to do. - } - // Because this entry is new, it was originally deleted. - kernel->put(IS_DEL, true); - trans->SaveOriginal(kernel.get()); - kernel->put(IS_DEL, false); - - // Now swap the pointers. - kernel_ = kernel.release(); -} - -MutableEntry::MutableEntry(WriteTransaction* trans, CreateNewUpdateItem, - const Id& id) - : Entry(trans), write_transaction_(trans) { - Entry same_id(trans, GET_BY_ID, id); - kernel_ = NULL; - if (same_id.good()) { - return; // already have an item with this ID. - } - scoped_ptr<EntryKernel> kernel(new EntryKernel()); - - kernel->put(ID, id); - kernel->put(META_HANDLE, trans->directory_->NextMetahandle()); - kernel->mark_dirty(trans->directory_->kernel_->dirty_metahandles); - kernel->put(IS_DEL, true); - // We match the database defaults here - kernel->put(BASE_VERSION, CHANGES_VERSION); - if (!trans->directory()->InsertEntry(trans, kernel.get())) { - return; // Failed inserting. - } - trans->SaveOriginal(kernel.get()); - - kernel_ = kernel.release(); -} - -MutableEntry::MutableEntry(WriteTransaction* trans, GetById, const Id& id) - : Entry(trans, GET_BY_ID, id), write_transaction_(trans) { -} - -MutableEntry::MutableEntry(WriteTransaction* trans, GetByHandle, - int64 metahandle) - : Entry(trans, GET_BY_HANDLE, metahandle), write_transaction_(trans) { -} - -MutableEntry::MutableEntry(WriteTransaction* trans, GetByClientTag, - const std::string& tag) - : Entry(trans, GET_BY_CLIENT_TAG, tag), write_transaction_(trans) { -} - -MutableEntry::MutableEntry(WriteTransaction* trans, GetByServerTag, - const string& tag) - : Entry(trans, GET_BY_SERVER_TAG, tag), write_transaction_(trans) { -} - -bool MutableEntry::PutIsDel(bool is_del) { - DCHECK(kernel_); - write_transaction_->SaveOriginal(kernel_); - if (is_del == kernel_->ref(IS_DEL)) { - return true; - } - if (is_del) { - if (!UnlinkFromOrder()) { - return false; - } - - // If the server never knew about this item and it's deleted then we don't - // need to keep it around. Unsetting IS_UNSYNCED will: - // - Ensure that the item is never committed to the server. - // - Allow any items with the same UNIQUE_CLIENT_TAG created on other - // clients to override this entry. - // - Let us delete this entry permanently through - // DirectoryBackingStore::DropDeletedEntries() when we next restart sync. - // This will save memory and avoid crbug.com/125381. - if (!Get(ID).ServerKnows()) { - Put(IS_UNSYNCED, false); - } - } - - { - ScopedKernelLock lock(dir()); - // Some indices don't include deleted items and must be updated - // upon a value change. - ScopedIndexUpdater<ParentIdAndHandleIndexer> updater(lock, kernel_, - dir()->kernel_->parent_id_child_index); - - kernel_->put(IS_DEL, is_del); - kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); - } - - if (!is_del) - // Restores position to the 0th index. - if (!PutPredecessor(Id())) { - // TODO(lipalani) : Propagate the error to caller. crbug.com/100444. - NOTREACHED(); - } - - return true; -} - -bool MutableEntry::Put(Int64Field field, const int64& value) { - DCHECK(kernel_); - write_transaction_->SaveOriginal(kernel_); - if (kernel_->ref(field) != value) { - ScopedKernelLock lock(dir()); - if (SERVER_POSITION_IN_PARENT == field) { - ScopedIndexUpdater<ParentIdAndHandleIndexer> updater(lock, kernel_, - dir()->kernel_->parent_id_child_index); - kernel_->put(field, value); - } else { - kernel_->put(field, value); - } - kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); - } - return true; -} - -bool MutableEntry::Put(TimeField field, const base::Time& value) { - DCHECK(kernel_); - write_transaction_->SaveOriginal(kernel_); - if (kernel_->ref(field) != value) { - kernel_->put(field, value); - kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); - } - return true; -} - -bool MutableEntry::Put(IdField field, const Id& value) { - DCHECK(kernel_); - write_transaction_->SaveOriginal(kernel_); - if (kernel_->ref(field) != value) { - if (ID == field) { - if (!dir()->ReindexId(write_transaction(), kernel_, value)) - return false; - } else if (PARENT_ID == field) { - PutParentIdPropertyOnly(value); // Makes sibling order inconsistent. - // Fixes up the sibling order inconsistency. - if (!PutPredecessor(Id())) { - // TODO(lipalani) : Propagate the error to caller. crbug.com/100444. - NOTREACHED(); - } - } else { - kernel_->put(field, value); - } - kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); - } - return true; -} - -void MutableEntry::PutParentIdPropertyOnly(const Id& parent_id) { - write_transaction_->SaveOriginal(kernel_); - dir()->ReindexParentId(write_transaction(), kernel_, parent_id); - kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); -} - -bool MutableEntry::Put(BaseVersion field, int64 value) { - DCHECK(kernel_); - write_transaction_->SaveOriginal(kernel_); - if (kernel_->ref(field) != value) { - kernel_->put(field, value); - kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); - } - return true; -} - -bool MutableEntry::Put(StringField field, const string& value) { - DCHECK(kernel_); - write_transaction_->SaveOriginal(kernel_); - if (field == UNIQUE_CLIENT_TAG) { - return PutUniqueClientTag(value); - } - - if (kernel_->ref(field) != value) { - kernel_->put(field, value); - kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); - } - return true; -} - -bool MutableEntry::Put(ProtoField field, - const sync_pb::EntitySpecifics& value) { - DCHECK(kernel_); - write_transaction_->SaveOriginal(kernel_); - // TODO(ncarter): This is unfortunately heavyweight. Can we do - // better? - if (kernel_->ref(field).SerializeAsString() != value.SerializeAsString()) { - const bool update_unapplied_updates_index = - (field == SERVER_SPECIFICS) && kernel_->ref(IS_UNAPPLIED_UPDATE); - if (update_unapplied_updates_index) { - // Remove ourselves from unapplied_update_metahandles with our - // old server type. - const syncable::ModelType old_server_type = - kernel_->GetServerModelType(); - const int64 metahandle = kernel_->ref(META_HANDLE); - size_t erase_count = - dir()->kernel_->unapplied_update_metahandles[old_server_type] - .erase(metahandle); - DCHECK_EQ(erase_count, 1u); - } - - kernel_->put(field, value); - kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); - - if (update_unapplied_updates_index) { - // Add ourselves back into unapplied_update_metahandles with our - // new server type. - const syncable::ModelType new_server_type = - kernel_->GetServerModelType(); - const int64 metahandle = kernel_->ref(META_HANDLE); - dir()->kernel_->unapplied_update_metahandles[new_server_type] - .insert(metahandle); - } - } - return true; -} - -bool MutableEntry::Put(BitField field, bool value) { - DCHECK(kernel_); - write_transaction_->SaveOriginal(kernel_); - if (kernel_->ref(field) != value) { - kernel_->put(field, value); - kernel_->mark_dirty(GetDirtyIndexHelper()); - } - return true; -} - -MetahandleSet* MutableEntry::GetDirtyIndexHelper() { - return dir()->kernel_->dirty_metahandles; -} - -bool MutableEntry::PutUniqueClientTag(const string& new_tag) { - write_transaction_->SaveOriginal(kernel_); - // There is no SERVER_UNIQUE_CLIENT_TAG. This field is similar to ID. - string old_tag = kernel_->ref(UNIQUE_CLIENT_TAG); - if (old_tag == new_tag) { - return true; - } - - ScopedKernelLock lock(dir()); - if (!new_tag.empty()) { - // Make sure your new value is not in there already. - EntryKernel lookup_kernel_ = *kernel_; - lookup_kernel_.put(UNIQUE_CLIENT_TAG, new_tag); - bool new_tag_conflicts = - (dir()->kernel_->client_tag_index->count(&lookup_kernel_) > 0); - if (new_tag_conflicts) { - return false; - } - } - - { - ScopedIndexUpdater<ClientTagIndexer> index_updater(lock, kernel_, - dir()->kernel_->client_tag_index); - kernel_->put(UNIQUE_CLIENT_TAG, new_tag); - kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); - } - return true; -} - -bool MutableEntry::Put(IndexedBitField field, bool value) { - DCHECK(kernel_); - write_transaction_->SaveOriginal(kernel_); - if (kernel_->ref(field) != value) { - MetahandleSet* index; - if (IS_UNSYNCED == field) { - index = dir()->kernel_->unsynced_metahandles; - } else { - // Use kernel_->GetServerModelType() instead of - // GetServerModelType() as we may trigger some DCHECKs in the - // latter. - index = - &dir()->kernel_->unapplied_update_metahandles[ - kernel_->GetServerModelType()]; - } - - ScopedKernelLock lock(dir()); - if (value) { - if (!SyncAssert(index->insert(kernel_->ref(META_HANDLE)).second, - FROM_HERE, - "Could not insert", - write_transaction())) { - return false; - } - } else { - if (!SyncAssert(1U == index->erase(kernel_->ref(META_HANDLE)), - FROM_HERE, - "Entry Not succesfully erased", - write_transaction())) { - return false; - } - } - kernel_->put(field, value); - kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); - } - return true; -} - -bool MutableEntry::UnlinkFromOrder() { - ScopedKernelLock lock(dir()); - return dir()->UnlinkEntryFromOrder(kernel_, - write_transaction(), - &lock, - NODE_MANIPULATION); -} - bool Directory::UnlinkEntryFromOrder(EntryKernel* entry, WriteTransaction* trans, ScopedKernelLock* lock, @@ -2047,71 +1144,6 @@ bool Directory::UnlinkEntryFromOrder(EntryKernel* entry, return true; } -bool MutableEntry::PutPredecessor(const Id& predecessor_id) { - if (!UnlinkFromOrder()) - return false; - - if (Get(IS_DEL)) { - DCHECK(predecessor_id.IsNull()); - return true; - } - - // TODO(ncarter): It should be possible to not maintain position for - // non-bookmark items. However, we'd need to robustly handle all possible - // permutations of setting IS_DEL and the SPECIFICS to identify the - // object type; or else, we'd need to add a ModelType to the - // MutableEntry's Create ctor. - // if (!ShouldMaintainPosition()) { - // return false; - // } - - // This is classic insert-into-doubly-linked-list from CS 101 and your last - // job interview. An "IsRoot" Id signifies the head or tail. - Id successor_id; - if (!predecessor_id.IsRoot()) { - MutableEntry predecessor(write_transaction(), GET_BY_ID, predecessor_id); - if (!predecessor.good()) { - LOG(ERROR) << "Predecessor is not good : " - << predecessor_id.GetServerId(); - return false; - } - if (predecessor.Get(PARENT_ID) != Get(PARENT_ID)) - return false; - successor_id = predecessor.Get(NEXT_ID); - predecessor.Put(NEXT_ID, Get(ID)); - } else { - syncable::Directory* dir = trans()->directory(); - if (!dir->GetFirstChildId(trans(), Get(PARENT_ID), &successor_id)) { - return false; - } - } - if (!successor_id.IsRoot()) { - MutableEntry successor(write_transaction(), GET_BY_ID, successor_id); - if (!successor.good()) { - LOG(ERROR) << "Successor is not good: " - << successor_id.GetServerId(); - return false; - } - if (successor.Get(PARENT_ID) != Get(PARENT_ID)) - return false; - successor.Put(PREV_ID, Get(ID)); - } - DCHECK(predecessor_id != Get(ID)); - DCHECK(successor_id != Get(ID)); - Put(PREV_ID, predecessor_id); - Put(NEXT_ID, successor_id); - return true; -} - -bool MutableEntry::Put(BitTemp field, bool value) { - DCHECK(kernel_); - kernel_->put(field, value); - return true; -} - -/////////////////////////////////////////////////////////////////////////// -// High-level functions - int64 Directory::NextMetahandle() { ScopedKernelLock lock(this); int64 metahandle = (kernel_->next_metahandle)++; @@ -2229,82 +1261,6 @@ Id Directory::ComputePrevIdFromServerPosition( return Id(); } -bool IsLegalNewParent(BaseTransaction* trans, const Id& entry_id, - const Id& new_parent_id) { - if (entry_id.IsRoot()) - return false; - // we have to ensure that the entry is not an ancestor of the new parent. - Id ancestor_id = new_parent_id; - while (!ancestor_id.IsRoot()) { - if (entry_id == ancestor_id) - return false; - Entry new_parent(trans, GET_BY_ID, ancestor_id); - if (!SyncAssert(new_parent.good(), - FROM_HERE, - "Invalid new parent", - trans)) - return false; - ancestor_id = new_parent.Get(PARENT_ID); - } - return true; -} - -// This function sets only the flags needed to get this entry to sync. -bool MarkForSyncing(syncable::MutableEntry* e) { - DCHECK_NE(static_cast<MutableEntry*>(NULL), e); - DCHECK(!e->IsRoot()) << "We shouldn't mark a permanent object for syncing."; - if (!(e->Put(IS_UNSYNCED, true))) - return false; - e->Put(SYNCING, false); - return true; -} - -std::ostream& operator<<(std::ostream& os, const Entry& entry) { - int i; - EntryKernel* const kernel = entry.kernel_; - for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) { - os << g_metas_columns[i].name << ": " - << kernel->ref(static_cast<Int64Field>(i)) << ", "; - } - for ( ; i < TIME_FIELDS_END; ++i) { - os << g_metas_columns[i].name << ": " - << browser_sync::GetTimeDebugString( - kernel->ref(static_cast<TimeField>(i))) << ", "; - } - for ( ; i < ID_FIELDS_END; ++i) { - os << g_metas_columns[i].name << ": " - << kernel->ref(static_cast<IdField>(i)) << ", "; - } - os << "Flags: "; - for ( ; i < BIT_FIELDS_END; ++i) { - if (kernel->ref(static_cast<BitField>(i))) - os << g_metas_columns[i].name << ", "; - } - for ( ; i < STRING_FIELDS_END; ++i) { - const string& field = kernel->ref(static_cast<StringField>(i)); - os << g_metas_columns[i].name << ": " << field << ", "; - } - for ( ; i < PROTO_FIELDS_END; ++i) { - os << g_metas_columns[i].name << ": " - << net::EscapePath( - kernel->ref(static_cast<ProtoField>(i)).SerializeAsString()) - << ", "; - } - os << "TempFlags: "; - for ( ; i < BIT_TEMPS_END; ++i) { - if (kernel->ref(static_cast<BitTemp>(i))) - os << "#" << i - BIT_TEMPS_BEGIN << ", "; - } - return os; -} - -std::ostream& operator<<(std::ostream& s, const Blob& blob) { - for (Blob::const_iterator i = blob.begin(); i != blob.end(); ++i) - s << std::hex << std::setw(2) - << std::setfill('0') << static_cast<unsigned int>(*i); - return s << std::dec; -} - Directory::ParentIdChildIndex::iterator Directory::LocateInParentChildIndex( const ScopedKernelLock& lock, const Id& parent_id, @@ -2395,47 +1351,8 @@ EntryKernel* Directory::GetPossibleLastChildForTest( return NULL; } -void ChangeEntryIDAndUpdateChildren( - syncable::WriteTransaction* trans, - syncable::MutableEntry* entry, - const syncable::Id& new_id) { - syncable::Id old_id = entry->Get(ID); - if (!entry->Put(ID, new_id)) { - Entry old_entry(trans, GET_BY_ID, new_id); - CHECK(old_entry.good()); - LOG(FATAL) << "Attempt to change ID to " << new_id - << " conflicts with existing entry.\n\n" - << *entry << "\n\n" << old_entry; - } - if (entry->Get(IS_DIR)) { - // Get all child entries of the old id. - syncable::Directory::ChildHandles children; - trans->directory()->GetChildHandlesById(trans, old_id, &children); - Directory::ChildHandles::iterator i = children.begin(); - while (i != children.end()) { - MutableEntry child_entry(trans, GET_BY_HANDLE, *i++); - CHECK(child_entry.good()); - // Use the unchecked setter here to avoid touching the child's NEXT_ID - // and PREV_ID fields (which Put(PARENT_ID) would normally do to - // maintain linked-list invariants). In this case, NEXT_ID and PREV_ID - // among the children will be valid after the loop, since we update all - // the children at once. - child_entry.PutParentIdPropertyOnly(new_id); - } - } - // Update Id references on the previous and next nodes in the sibling - // order. Do this by reinserting into the linked list; the first - // step in PutPredecessor is to Unlink from the existing order, which - // will overwrite the stale Id value from the adjacent nodes. - if (entry->Get(PREV_ID) == entry->Get(NEXT_ID) && - entry->Get(PREV_ID) == old_id) { - // We just need a shallow update to |entry|'s fields since it is already - // self looped. - entry->Put(NEXT_ID, new_id); - entry->Put(PREV_ID, new_id); - } else { - entry->PutPredecessor(entry->Get(PREV_ID)); - } +ScopedKernelLock::ScopedKernelLock(const Directory* dir) + : scoped_lock_(dir->kernel_->mutex), dir_(const_cast<Directory*>(dir)) { } -} // namespace syncable +} diff --git a/sync/syncable/syncable.h b/sync/syncable/directory.h index 0cab389..dc47732 100644 --- a/sync/syncable/syncable.h +++ b/sync/syncable/directory.h @@ -2,656 +2,38 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SYNC_SYNCABLE_SYNCABLE_H_ -#define SYNC_SYNCABLE_SYNCABLE_H_ +#ifndef SYNC_SYNCABLE_DIRECTORY_H_ +#define SYNC_SYNCABLE_DIRECTORY_H_ #pragma once -#include <algorithm> -#include <bitset> -#include <cstddef> -#include <iosfwd> -#include <limits> -#include <map> #include <set> #include <string> #include <vector> -#include "base/atomicops.h" -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/file_path.h" +#include "base/file_util.h" #include "base/gtest_prod_util.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "base/time.h" -#include "sync/internal_api/public/syncable/model_type.h" -#include "sync/internal_api/public/util/immutable.h" #include "sync/internal_api/public/util/report_unrecoverable_error_function.h" -#include "sync/internal_api/public/util/unrecoverable_error_handler.h" #include "sync/internal_api/public/util/weak_handle.h" -#include "sync/protocol/sync.pb.h" -#include "sync/syncable/blob.h" #include "sync/syncable/dir_open_result.h" -#include "sync/syncable/syncable_id.h" +#include "sync/syncable/entry_kernel.h" +#include "sync/syncable/metahandle_set.h" +#include "sync/syncable/scoped_kernel_lock.h" #include "sync/util/cryptographer.h" -#include "sync/util/time.h" - -namespace base { -class DictionaryValue; -class ListValue; -} namespace browser_sync { class Encryptor; -} // namespace browser_sync - -namespace sync_api { -class ReadTransaction; -class WriteNode; -class ReadNode; -} // sync_api +class UnrecoverableErrorHandler; +} namespace syncable { + class DirectoryChangeDelegate; class TransactionObserver; -class Entry; - -std::ostream& operator<<(std::ostream& s, const Entry& e); - -class DirectoryBackingStore; - -static const int64 kInvalidMetaHandle = 0; - -// Things you need to update if you change any of the fields below: -// - EntryKernel struct in syncable.h (this file) -// - syncable_columns.h -// - syncable_enum_conversions{.h,.cc,_unittest.cc} -// - EntryKernel::EntryKernel(), EntryKernel::ToValue(), operator<< -// for Entry in syncable.cc -// - BindFields() and UnpackEntry() in directory_backing_store.cc -// - TestSimpleFieldsPreservedDuringSaveChanges in syncable_unittest.cc - -enum { - BEGIN_FIELDS = 0, - INT64_FIELDS_BEGIN = BEGIN_FIELDS -}; - -enum MetahandleField { - // Primary key into the table. Keep this as a handle to the meta entry - // across transactions. - META_HANDLE = INT64_FIELDS_BEGIN -}; - -enum BaseVersion { - // After initial upload, the version is controlled by the server, and is - // increased whenever the data or metadata changes on the server. - BASE_VERSION = META_HANDLE + 1, -}; - -enum Int64Field { - SERVER_VERSION = BASE_VERSION + 1, - - // A numeric position value that indicates the relative ordering of - // this object among its siblings. - SERVER_POSITION_IN_PARENT, - - LOCAL_EXTERNAL_ID, // ID of an item in the external local storage that this - // entry is associated with. (such as bookmarks.js) - - INT64_FIELDS_END -}; - -enum { - INT64_FIELDS_COUNT = INT64_FIELDS_END - INT64_FIELDS_BEGIN, - TIME_FIELDS_BEGIN = INT64_FIELDS_END, -}; - -enum TimeField { - MTIME = TIME_FIELDS_BEGIN, - SERVER_MTIME, - CTIME, - SERVER_CTIME, - TIME_FIELDS_END, -}; - -enum { - TIME_FIELDS_COUNT = TIME_FIELDS_END - TIME_FIELDS_BEGIN, - ID_FIELDS_BEGIN = TIME_FIELDS_END, -}; - -enum IdField { - // Code in InitializeTables relies on ID being the first IdField value. - ID = ID_FIELDS_BEGIN, - PARENT_ID, - SERVER_PARENT_ID, - - PREV_ID, - NEXT_ID, - ID_FIELDS_END -}; - -enum { - ID_FIELDS_COUNT = ID_FIELDS_END - ID_FIELDS_BEGIN, - BIT_FIELDS_BEGIN = ID_FIELDS_END -}; - -enum IndexedBitField { - IS_UNSYNCED = BIT_FIELDS_BEGIN, - IS_UNAPPLIED_UPDATE, - INDEXED_BIT_FIELDS_END, -}; - -enum IsDelField { - IS_DEL = INDEXED_BIT_FIELDS_END, -}; - -enum BitField { - IS_DIR = IS_DEL + 1, - SERVER_IS_DIR, - SERVER_IS_DEL, - BIT_FIELDS_END -}; - -enum { - BIT_FIELDS_COUNT = BIT_FIELDS_END - BIT_FIELDS_BEGIN, - STRING_FIELDS_BEGIN = BIT_FIELDS_END -}; - -enum StringField { - // Name, will be truncated by server. Can be duplicated in a folder. - NON_UNIQUE_NAME = STRING_FIELDS_BEGIN, - // The server version of |NON_UNIQUE_NAME|. - SERVER_NON_UNIQUE_NAME, - - // A tag string which identifies this node as a particular top-level - // permanent object. The tag can be thought of as a unique key that - // identifies a singleton instance. - UNIQUE_SERVER_TAG, // Tagged by the server - UNIQUE_CLIENT_TAG, // Tagged by the client - STRING_FIELDS_END, -}; - -enum { - STRING_FIELDS_COUNT = STRING_FIELDS_END - STRING_FIELDS_BEGIN, - PROTO_FIELDS_BEGIN = STRING_FIELDS_END -}; - -// From looking at the sqlite3 docs, it's not directly stated, but it -// seems the overhead for storing a NULL blob is very small. -enum ProtoField { - SPECIFICS = PROTO_FIELDS_BEGIN, - SERVER_SPECIFICS, - BASE_SERVER_SPECIFICS, - PROTO_FIELDS_END, -}; - -enum { - PROTO_FIELDS_COUNT = PROTO_FIELDS_END - PROTO_FIELDS_BEGIN -}; - -enum { - FIELD_COUNT = PROTO_FIELDS_END, - // Past this point we have temporaries, stored in memory only. - BEGIN_TEMPS = PROTO_FIELDS_END, - BIT_TEMPS_BEGIN = BEGIN_TEMPS, -}; - -enum BitTemp { - // Not to be confused with IS_UNSYNCED, this bit is used to detect local - // changes to items that happen during the server Commit operation. - SYNCING = BIT_TEMPS_BEGIN, - BIT_TEMPS_END, -}; - -enum { - BIT_TEMPS_COUNT = BIT_TEMPS_END - BIT_TEMPS_BEGIN -}; - class BaseTransaction; class WriteTransaction; -class ReadTransaction; -class Directory; - -// Instead of: -// Entry e = transaction.GetById(id); -// use: -// Entry e(transaction, GET_BY_ID, id); -// -// Why? The former would require a copy constructor, and it would be difficult -// to enforce that an entry never outlived its transaction if there were a copy -// constructor. -enum GetById { - GET_BY_ID -}; - -enum GetByClientTag { - GET_BY_CLIENT_TAG -}; - -enum GetByServerTag { - GET_BY_SERVER_TAG -}; - -enum GetByHandle { - GET_BY_HANDLE -}; - -enum Create { - CREATE -}; - -enum CreateNewUpdateItem { - CREATE_NEW_UPDATE_ITEM -}; - -typedef std::set<int64> MetahandleSet; - -// Reason for unlinking. -enum UnlinkReason { - NODE_MANIPULATION, // To be used by any operation manipulating the linked - // list. - DATA_TYPE_PURGE // To be used when purging a dataype. -}; - -// TODO(akalin): Move EntryKernel and related into its own header file. - -// Why the singular enums? So the code compile-time dispatches instead of -// runtime dispatches as it would with a single enum and an if() statement. - -// The EntryKernel class contains the actual data for an entry. -struct EntryKernel { - private: - std::string string_fields[STRING_FIELDS_COUNT]; - sync_pb::EntitySpecifics specifics_fields[PROTO_FIELDS_COUNT]; - int64 int64_fields[INT64_FIELDS_COUNT]; - base::Time time_fields[TIME_FIELDS_COUNT]; - Id id_fields[ID_FIELDS_COUNT]; - std::bitset<BIT_FIELDS_COUNT> bit_fields; - std::bitset<BIT_TEMPS_COUNT> bit_temps; - - public: - EntryKernel(); - ~EntryKernel(); - - // Set the dirty bit, and optionally add this entry's metahandle to - // a provided index on dirty bits in |dirty_index|. Parameter may be null, - // and will result only in setting the dirty bit of this entry. - inline void mark_dirty(syncable::MetahandleSet* dirty_index) { - if (!dirty_ && dirty_index) { - DCHECK_NE(0, ref(META_HANDLE)); - dirty_index->insert(ref(META_HANDLE)); - } - dirty_ = true; - } - - // Clear the dirty bit, and optionally remove this entry's metahandle from - // a provided index on dirty bits in |dirty_index|. Parameter may be null, - // and will result only in clearing dirty bit of this entry. - inline void clear_dirty(syncable::MetahandleSet* dirty_index) { - if (dirty_ && dirty_index) { - DCHECK_NE(0, ref(META_HANDLE)); - dirty_index->erase(ref(META_HANDLE)); - } - dirty_ = false; - } - - inline bool is_dirty() const { - return dirty_; - } - - // Setters. - inline void put(MetahandleField field, int64 value) { - int64_fields[field - INT64_FIELDS_BEGIN] = value; - } - inline void put(Int64Field field, int64 value) { - int64_fields[field - INT64_FIELDS_BEGIN] = value; - } - inline void put(TimeField field, const base::Time& value) { - // Round-trip to proto time format and back so that we have - // consistent time resolutions (ms). - time_fields[field - TIME_FIELDS_BEGIN] = - browser_sync::ProtoTimeToTime( - browser_sync::TimeToProtoTime(value)); - } - inline void put(IdField field, const Id& value) { - id_fields[field - ID_FIELDS_BEGIN] = value; - } - inline void put(BaseVersion field, int64 value) { - int64_fields[field - INT64_FIELDS_BEGIN] = value; - } - inline void put(IndexedBitField field, bool value) { - bit_fields[field - BIT_FIELDS_BEGIN] = value; - } - inline void put(IsDelField field, bool value) { - bit_fields[field - BIT_FIELDS_BEGIN] = value; - } - inline void put(BitField field, bool value) { - bit_fields[field - BIT_FIELDS_BEGIN] = value; - } - inline void put(StringField field, const std::string& value) { - string_fields[field - STRING_FIELDS_BEGIN] = value; - } - inline void put(ProtoField field, const sync_pb::EntitySpecifics& value) { - specifics_fields[field - PROTO_FIELDS_BEGIN].CopyFrom(value); - } - inline void put(BitTemp field, bool value) { - bit_temps[field - BIT_TEMPS_BEGIN] = value; - } - - // Const ref getters. - inline int64 ref(MetahandleField field) const { - return int64_fields[field - INT64_FIELDS_BEGIN]; - } - inline int64 ref(Int64Field field) const { - return int64_fields[field - INT64_FIELDS_BEGIN]; - } - inline const base::Time& ref(TimeField field) const { - return time_fields[field - TIME_FIELDS_BEGIN]; - } - inline const Id& ref(IdField field) const { - return id_fields[field - ID_FIELDS_BEGIN]; - } - inline int64 ref(BaseVersion field) const { - return int64_fields[field - INT64_FIELDS_BEGIN]; - } - inline bool ref(IndexedBitField field) const { - return bit_fields[field - BIT_FIELDS_BEGIN]; - } - inline bool ref(IsDelField field) const { - return bit_fields[field - BIT_FIELDS_BEGIN]; - } - inline bool ref(BitField field) const { - return bit_fields[field - BIT_FIELDS_BEGIN]; - } - inline const std::string& ref(StringField field) const { - return string_fields[field - STRING_FIELDS_BEGIN]; - } - inline const sync_pb::EntitySpecifics& ref(ProtoField field) const { - return specifics_fields[field - PROTO_FIELDS_BEGIN]; - } - inline bool ref(BitTemp field) const { - return bit_temps[field - BIT_TEMPS_BEGIN]; - } - - // Non-const, mutable ref getters for object types only. - inline std::string& mutable_ref(StringField field) { - return string_fields[field - STRING_FIELDS_BEGIN]; - } - inline sync_pb::EntitySpecifics& mutable_ref(ProtoField field) { - return specifics_fields[field - PROTO_FIELDS_BEGIN]; - } - inline Id& mutable_ref(IdField field) { - return id_fields[field - ID_FIELDS_BEGIN]; - } - - syncable::ModelType GetServerModelType() const; - - // Dumps all kernel info into a DictionaryValue and returns it. - // Transfers ownership of the DictionaryValue to the caller. - base::DictionaryValue* ToValue() const; - - private: - // Tracks whether this entry needs to be saved to the database. - bool dirty_; -}; - -// A read-only meta entry. -class Entry { - friend class Directory; - friend std::ostream& operator << (std::ostream& s, const Entry& e); - - public: - // After constructing, you must check good() to test whether the Get - // succeeded. - Entry(BaseTransaction* trans, GetByHandle, int64 handle); - Entry(BaseTransaction* trans, GetById, const Id& id); - Entry(BaseTransaction* trans, GetByServerTag, const std::string& tag); - Entry(BaseTransaction* trans, GetByClientTag, const std::string& tag); - - bool good() const { return 0 != kernel_; } - - BaseTransaction* trans() const { return basetrans_; } - - // Field accessors. - inline int64 Get(MetahandleField field) const { - DCHECK(kernel_); - return kernel_->ref(field); - } - inline Id Get(IdField field) const { - DCHECK(kernel_); - return kernel_->ref(field); - } - inline int64 Get(Int64Field field) const { - DCHECK(kernel_); - return kernel_->ref(field); - } - inline const base::Time& Get(TimeField field) const { - DCHECK(kernel_); - return kernel_->ref(field); - } - inline int64 Get(BaseVersion field) const { - DCHECK(kernel_); - return kernel_->ref(field); - } - inline bool Get(IndexedBitField field) const { - DCHECK(kernel_); - return kernel_->ref(field); - } - inline bool Get(IsDelField field) const { - DCHECK(kernel_); - return kernel_->ref(field); - } - inline bool Get(BitField field) const { - DCHECK(kernel_); - return kernel_->ref(field); - } - const std::string& Get(StringField field) const; - inline const sync_pb::EntitySpecifics& Get(ProtoField field) const { - DCHECK(kernel_); - return kernel_->ref(field); - } - inline bool Get(BitTemp field) const { - DCHECK(kernel_); - return kernel_->ref(field); - } - - ModelType GetServerModelType() const; - ModelType GetModelType() const; - - inline bool ExistsOnClientBecauseNameIsNonEmpty() const { - DCHECK(kernel_); - return !kernel_->ref(NON_UNIQUE_NAME).empty(); - } - - inline bool IsRoot() const { - DCHECK(kernel_); - return kernel_->ref(ID).IsRoot(); - } - - Directory* dir() const; - - const EntryKernel GetKernelCopy() const { - return *kernel_; - } - - // Compute a local predecessor position for |update_item|, based on its - // absolute server position. The returned ID will be a valid predecessor - // under SERVER_PARENT_ID that is consistent with the - // SERVER_POSITION_IN_PARENT ordering. - Id ComputePrevIdFromServerPosition(const Id& parent_id) const; - - // Dumps all entry info into a DictionaryValue and returns it. - // Transfers ownership of the DictionaryValue to the caller. - base::DictionaryValue* ToValue() const; - - protected: // Don't allow creation on heap, except by sync API wrappers. - friend class sync_api::ReadNode; - void* operator new(size_t size) { return (::operator new)(size); } - - inline explicit Entry(BaseTransaction* trans) - : basetrans_(trans), - kernel_(NULL) { } - - protected: - BaseTransaction* const basetrans_; - - EntryKernel* kernel_; - - private: - DISALLOW_COPY_AND_ASSIGN(Entry); -}; - -// A mutable meta entry. Changes get committed to the database when the -// WriteTransaction is destroyed. -class MutableEntry : public Entry { - friend class WriteTransaction; - friend class Directory; - void Init(WriteTransaction* trans, const Id& parent_id, - const std::string& name); - - public: - MutableEntry(WriteTransaction* trans, Create, const Id& parent_id, - const std::string& name); - MutableEntry(WriteTransaction* trans, CreateNewUpdateItem, const Id& id); - MutableEntry(WriteTransaction* trans, GetByHandle, int64); - MutableEntry(WriteTransaction* trans, GetById, const Id&); - MutableEntry(WriteTransaction* trans, GetByClientTag, const std::string& tag); - MutableEntry(WriteTransaction* trans, GetByServerTag, const std::string& tag); - - inline WriteTransaction* write_transaction() const { - return write_transaction_; - } - - // Field Accessors. Some of them trigger the re-indexing of the entry. - // Return true on success, return false on failure, which means - // that putting the value would have caused a duplicate in the index. - // TODO(chron): Remove some of these unecessary return values. - bool Put(Int64Field field, const int64& value); - bool Put(TimeField field, const base::Time& value); - bool Put(IdField field, const Id& value); - - // Do a simple property-only update if the PARENT_ID field. Use with caution. - // - // The normal Put(IS_PARENT) call will move the item to the front of the - // sibling order to maintain the linked list invariants when the parent - // changes. That's usually what you want to do, but it's inappropriate - // when the caller is trying to change the parent ID of a the whole set - // of children (e.g. because the ID changed during a commit). For those - // cases, there's this function. It will corrupt the sibling ordering - // if you're not careful. - void PutParentIdPropertyOnly(const Id& parent_id); - - bool Put(StringField field, const std::string& value); - bool Put(BaseVersion field, int64 value); - - bool Put(ProtoField field, const sync_pb::EntitySpecifics& value); - bool Put(BitField field, bool value); - inline bool Put(IsDelField field, bool value) { - return PutIsDel(value); - } - bool Put(IndexedBitField field, bool value); - - // Sets the position of this item, and updates the entry kernels of the - // adjacent siblings so that list invariants are maintained. Returns false - // and fails if |predecessor_id| does not identify a sibling. Pass the root - // ID to put the node in first position. - bool PutPredecessor(const Id& predecessor_id); - - bool Put(BitTemp field, bool value); - - protected: - syncable::MetahandleSet* GetDirtyIndexHelper(); - - bool PutIsDel(bool value); - - private: // Don't allow creation on heap, except by sync API wrappers. - friend class sync_api::WriteNode; - void* operator new(size_t size) { return (::operator new)(size); } - - bool PutUniqueClientTag(const std::string& value); - - // Adjusts the successor and predecessor entries so that they no longer - // refer to this entry. - bool UnlinkFromOrder(); - - // Kind of redundant. We should reduce the number of pointers - // floating around if at all possible. Could we store this in Directory? - // Scope: Set on construction, never changed after that. - WriteTransaction* const write_transaction_; - - protected: - MutableEntry(); - - DISALLOW_COPY_AND_ASSIGN(MutableEntry); -}; - -template <typename FieldType, FieldType field_index> class LessField; - -class EntryKernelLessByMetaHandle { - public: - inline bool operator()(const EntryKernel& a, - const EntryKernel& b) const { - return a.ref(META_HANDLE) < b.ref(META_HANDLE); - } -}; -typedef std::set<EntryKernel, EntryKernelLessByMetaHandle> EntryKernelSet; - -struct EntryKernelMutation { - EntryKernel original, mutated; -}; -typedef std::map<int64, EntryKernelMutation> EntryKernelMutationMap; - -typedef browser_sync::Immutable<EntryKernelMutationMap> - ImmutableEntryKernelMutationMap; - -// A WriteTransaction has a writer tag describing which body of code is doing -// the write. This is defined up here since WriteTransactionInfo also contains -// one. -enum WriterTag { - INVALID, - SYNCER, - AUTHWATCHER, - UNITTEST, - VACUUM_AFTER_SAVE, - PURGE_ENTRIES, - SYNCAPI -}; - -// Make sure to update this if you update WriterTag. -std::string WriterTagToString(WriterTag writer_tag); - -struct WriteTransactionInfo { - WriteTransactionInfo(int64 id, - tracked_objects::Location location, - WriterTag writer, - ImmutableEntryKernelMutationMap mutations); - WriteTransactionInfo(); - ~WriteTransactionInfo(); - - // Caller owns the return value. - base::DictionaryValue* ToValue(size_t max_mutations_size) const; - - int64 id; - // If tracked_objects::Location becomes assignable, we can use that - // instead. - std::string location_string; - WriterTag writer; - ImmutableEntryKernelMutationMap mutations; -}; - -typedef - browser_sync::Immutable<WriteTransactionInfo> - ImmutableWriteTransactionInfo; - -// Caller owns the return value. -base::DictionaryValue* EntryKernelMutationToValue( - const EntryKernelMutation& mutation); - -// Caller owns the return value. -base::ListValue* EntryKernelMutationMapToValue( - const EntryKernelMutationMap& mutations); +class ScopedKernelLock; +class DirectoryBackingStore; +class IdFilter; // How syncable indices & Indexers work. // @@ -670,6 +52,8 @@ base::ListValue* EntryKernelMutationMapToValue( // index, the traits are grouped into a class called an Indexer which // can be used as a template type parameter. +template <typename FieldType, FieldType field_index> class LessField; + // Traits type for metahandle index. struct MetahandleIndexer { // This index is of the metahandle field values. @@ -723,25 +107,21 @@ struct Index { typedef std::set<EntryKernel*, typename Indexer::Comparator> Set; }; -// The name Directory in this case means the entire directory -// structure within a single user account. -// -// The db is protected against concurrent modification by a reader/ -// writer lock, negotiated by the ReadTransaction and WriteTransaction -// friend classes. The in-memory indices are protected against -// concurrent modification by the kernel lock. -// -// All methods which require the reader/writer lock to be held either -// are protected and only called from friends in a transaction -// or are public and take a Transaction* argument. -// -// All methods which require the kernel lock to be already held take a -// ScopeKernelLock* argument. -// -// To prevent deadlock, the reader writer transaction lock must always -// be held before acquiring the kernel lock. -class ScopedKernelLock; -class IdFilter; +// Reason for unlinking. +enum UnlinkReason { + NODE_MANIPULATION, // To be used by any operation manipulating the linked + // list. + DATA_TYPE_PURGE // To be used when purging a dataype. +}; + +class EntryKernelLessByMetaHandle { + public: + inline bool operator()(const EntryKernel& a, + const EntryKernel& b) const { + return a.ref(META_HANDLE) < b.ref(META_HANDLE); + } +}; +typedef std::set<EntryKernel, EntryKernelLessByMetaHandle> EntryKernelSet; class Directory { friend class BaseTransaction; @@ -1237,122 +617,6 @@ class Directory { bool unrecoverable_error_set_; }; -class ScopedKernelLock { - public: - explicit ScopedKernelLock(const Directory*); - ~ScopedKernelLock() {} - - base::AutoLock scoped_lock_; - Directory* const dir_; - DISALLOW_COPY_AND_ASSIGN(ScopedKernelLock); -}; - -// Transactions are now processed FIFO with a straight lock -class BaseTransaction { - friend class Entry; - public: - inline Directory* directory() const { return directory_; } - inline Id root_id() const { return Id(); } - - virtual ~BaseTransaction(); - - // This should be called when a database corruption is detected and there is - // no way for us to recover short of wiping the database clean. When this is - // called we set a bool in the transaction. The caller has to unwind the - // stack. When the destructor for the transaction is called it acts upon the - // bool and calls the Directory to handle the unrecoverable error. - void OnUnrecoverableError(const tracked_objects::Location& location, - const std::string& message); - - bool unrecoverable_error_set() const; - - protected: - BaseTransaction(const tracked_objects::Location& from_here, - const char* name, - WriterTag writer, - Directory* directory); - - void Lock(); - void Unlock(); - - // This should be called before unlocking because it calls the Direcotry's - // OnUnrecoverableError method which is not protected by locks and could - // be called from any thread. Holding the transaction lock ensures only one - // thread could call the method at a time. - void HandleUnrecoverableErrorIfSet(); - - const tracked_objects::Location from_here_; - const char* const name_; - WriterTag writer_; - Directory* const directory_; - Directory::Kernel* const dirkernel_; // for brevity - - // Error information. - bool unrecoverable_error_set_; - tracked_objects::Location unrecoverable_error_location_; - std::string unrecoverable_error_msg_; - - private: - DISALLOW_COPY_AND_ASSIGN(BaseTransaction); -}; - -// Locks db in constructor, unlocks in destructor. -class ReadTransaction : public BaseTransaction { - public: - ReadTransaction(const tracked_objects::Location& from_here, - Directory* directory); - - virtual ~ReadTransaction(); - - protected: // Don't allow creation on heap, except by sync API wrapper. - friend class sync_api::ReadTransaction; - void* operator new(size_t size) { return (::operator new)(size); } - - DISALLOW_COPY_AND_ASSIGN(ReadTransaction); -}; - -// Locks db in constructor, unlocks in destructor. -class WriteTransaction : public BaseTransaction { - friend class MutableEntry; - public: - WriteTransaction(const tracked_objects::Location& from_here, - WriterTag writer, Directory* directory); - - virtual ~WriteTransaction(); - - void SaveOriginal(const EntryKernel* entry); - - protected: - // Overridden by tests. - virtual void NotifyTransactionComplete(ModelTypeSet models_with_changes); - - private: - // Clears |mutations_|. - ImmutableEntryKernelMutationMap RecordMutations(); - - void UnlockAndNotify(const ImmutableEntryKernelMutationMap& mutations); - - ModelTypeSet NotifyTransactionChangingAndEnding( - const ImmutableEntryKernelMutationMap& mutations); - - // Only the original fields are filled in until |RecordMutations()|. - // We use a mutation map instead of a kernel set to avoid copying. - EntryKernelMutationMap mutations_; - - DISALLOW_COPY_AND_ASSIGN(WriteTransaction); -}; - -bool IsLegalNewParent(BaseTransaction* trans, const Id& id, const Id& parentid); - -// This function sets only the flags needed to get this entry to sync. -bool MarkForSyncing(syncable::MutableEntry* e); - -void ChangeEntryIDAndUpdateChildren(syncable::WriteTransaction* trans, - syncable::MutableEntry* entry, - const syncable::Id& new_id); - -} // namespace syncable - -std::ostream& operator <<(std::ostream&, const syncable::Blob&); +} // namespace syncable -#endif // SYNC_SYNCABLE_SYNCABLE_H_ +#endif // SYNC_SYNCABLE_DIRECTORY_H_ diff --git a/sync/syncable/directory_backing_store.h b/sync/syncable/directory_backing_store.h index 7e86eb9..2463521 100644 --- a/sync/syncable/directory_backing_store.h +++ b/sync/syncable/directory_backing_store.h @@ -14,7 +14,8 @@ #include "sql/statement.h" #include "sync/internal_api/public/syncable/model_type.h" #include "sync/syncable/dir_open_result.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/metahandle_set.h" namespace sync_pb { class EntitySpecifics; diff --git a/sync/syncable/directory_backing_store_unittest.cc b/sync/syncable/directory_backing_store_unittest.cc index 5161817..62b7ccf 100644 --- a/sync/syncable/directory_backing_store_unittest.cc +++ b/sync/syncable/directory_backing_store_unittest.cc @@ -12,16 +12,15 @@ #include "base/scoped_temp_dir.h" #include "base/stl_util.h" #include "base/string_number_conversions.h" +#include "sql/connection.h" +#include "sql/statement.h" +#include "sync/protocol/bookmark_specifics.pb.h" +#include "sync/protocol/sync.pb.h" #include "sync/syncable/directory_backing_store.h" #include "sync/syncable/on_disk_directory_backing_store.h" #include "sync/syncable/syncable-inl.h" -#include "sync/syncable/syncable.h" #include "sync/test/test_directory_backing_store.h" #include "sync/util/time.h" -#include "sql/connection.h" -#include "sql/statement.h" -#include "sync/protocol/bookmark_specifics.pb.h" -#include "sync/protocol/sync.pb.h" #include "testing/gtest/include/gtest/gtest-param-test.h" namespace syncable { diff --git a/sync/syncable/directory_change_delegate.h b/sync/syncable/directory_change_delegate.h index a86b770..fdd7294 100644 --- a/sync/syncable/directory_change_delegate.h +++ b/sync/syncable/directory_change_delegate.h @@ -7,7 +7,7 @@ #pragma once #include "sync/internal_api/public/syncable/model_type.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/write_transaction_info.h" namespace syncable { diff --git a/sync/syncable/entry.cc b/sync/syncable/entry.cc new file mode 100644 index 0000000..3f3e785 --- /dev/null +++ b/sync/syncable/entry.cc @@ -0,0 +1,143 @@ +// 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 "sync/syncable/entry.h" + +#include <iomanip> + +#include "net/base/escape.h" +#include "sync/syncable/base_transaction.h" +#include "sync/syncable/blob.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/syncable_columns.h" + +using std::string; + +namespace syncable { + +Entry::Entry(BaseTransaction* trans, GetById, const Id& id) + : basetrans_(trans) { + kernel_ = trans->directory()->GetEntryById(id); +} + +Entry::Entry(BaseTransaction* trans, GetByClientTag, const string& tag) + : basetrans_(trans) { + kernel_ = trans->directory()->GetEntryByClientTag(tag); +} + +Entry::Entry(BaseTransaction* trans, GetByServerTag, const string& tag) + : basetrans_(trans) { + kernel_ = trans->directory()->GetEntryByServerTag(tag); +} + +Entry::Entry(BaseTransaction* trans, GetByHandle, int64 metahandle) + : basetrans_(trans) { + kernel_ = trans->directory()->GetEntryByHandle(metahandle); +} + +Directory* Entry::dir() const { + return basetrans_->directory(); +} + +Id Entry::ComputePrevIdFromServerPosition(const Id& parent_id) const { + return dir()->ComputePrevIdFromServerPosition(kernel_, parent_id); +} + +DictionaryValue* Entry::ToValue() const { + DictionaryValue* entry_info = new DictionaryValue(); + entry_info->SetBoolean("good", good()); + if (good()) { + entry_info->Set("kernel", kernel_->ToValue()); + entry_info->Set("modelType", + ModelTypeToValue(GetModelType())); + entry_info->SetBoolean("existsOnClientBecauseNameIsNonEmpty", + ExistsOnClientBecauseNameIsNonEmpty()); + entry_info->SetBoolean("isRoot", IsRoot()); + } + return entry_info; +} + +const string& Entry::Get(StringField field) const { + DCHECK(kernel_); + return kernel_->ref(field); +} + +syncable::ModelType Entry::GetServerModelType() const { + ModelType specifics_type = kernel_->GetServerModelType(); + if (specifics_type != UNSPECIFIED) + return specifics_type; + + // Otherwise, we don't have a server type yet. That should only happen + // if the item is an uncommitted locally created item. + // It's possible we'll need to relax these checks in the future; they're + // just here for now as a safety measure. + DCHECK(Get(IS_UNSYNCED)); + DCHECK_EQ(Get(SERVER_VERSION), 0); + DCHECK(Get(SERVER_IS_DEL)); + // Note: can't enforce !Get(ID).ServerKnows() here because that could + // actually happen if we hit AttemptReuniteLostCommitResponses. + return UNSPECIFIED; +} + +syncable::ModelType Entry::GetModelType() const { + ModelType specifics_type = GetModelTypeFromSpecifics(Get(SPECIFICS)); + if (specifics_type != UNSPECIFIED) + return specifics_type; + if (IsRoot()) + return TOP_LEVEL_FOLDER; + // Loose check for server-created top-level folders that aren't + // bound to a particular model type. + if (!Get(UNIQUE_SERVER_TAG).empty() && Get(IS_DIR)) + return TOP_LEVEL_FOLDER; + + return UNSPECIFIED; +} + +std::ostream& operator<<(std::ostream& s, const Blob& blob) { + for (Blob::const_iterator i = blob.begin(); i != blob.end(); ++i) + s << std::hex << std::setw(2) + << std::setfill('0') << static_cast<unsigned int>(*i); + return s << std::dec; +} + +std::ostream& operator<<(std::ostream& os, const Entry& entry) { + int i; + EntryKernel* const kernel = entry.kernel_; + for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) { + os << g_metas_columns[i].name << ": " + << kernel->ref(static_cast<Int64Field>(i)) << ", "; + } + for ( ; i < TIME_FIELDS_END; ++i) { + os << g_metas_columns[i].name << ": " + << browser_sync::GetTimeDebugString( + kernel->ref(static_cast<TimeField>(i))) << ", "; + } + for ( ; i < ID_FIELDS_END; ++i) { + os << g_metas_columns[i].name << ": " + << kernel->ref(static_cast<IdField>(i)) << ", "; + } + os << "Flags: "; + for ( ; i < BIT_FIELDS_END; ++i) { + if (kernel->ref(static_cast<BitField>(i))) + os << g_metas_columns[i].name << ", "; + } + for ( ; i < STRING_FIELDS_END; ++i) { + const std::string& field = kernel->ref(static_cast<StringField>(i)); + os << g_metas_columns[i].name << ": " << field << ", "; + } + for ( ; i < PROTO_FIELDS_END; ++i) { + os << g_metas_columns[i].name << ": " + << net::EscapePath( + kernel->ref(static_cast<ProtoField>(i)).SerializeAsString()) + << ", "; + } + os << "TempFlags: "; + for ( ; i < BIT_TEMPS_END; ++i) { + if (kernel->ref(static_cast<BitTemp>(i))) + os << "#" << i - BIT_TEMPS_BEGIN << ", "; + } + return os; +} + +} // namespace syncable diff --git a/sync/syncable/entry.h b/sync/syncable/entry.h new file mode 100644 index 0000000..3974902 --- /dev/null +++ b/sync/syncable/entry.h @@ -0,0 +1,154 @@ +// 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 SYNC_SYNCABLE_ENTRY_H_ +#define SYNC_SYNCABLE_ENTRY_H_ +#pragma once + +#include "sync/syncable/entry_kernel.h" + +namespace sync_api { +class ReadNode; +} + +namespace syncable { + +class Directory; +class BaseTransaction; + +// A read-only meta entry +// Instead of: +// Entry e = transaction.GetById(id); +// use: +// Entry e(transaction, GET_BY_ID, id); +// +// Why? The former would require a copy constructor, and it would be difficult +// to enforce that an entry never outlived its transaction if there were a copy +// constructor. +enum GetById { + GET_BY_ID +}; + +enum GetByClientTag { + GET_BY_CLIENT_TAG +}; + +enum GetByServerTag { + GET_BY_SERVER_TAG +}; + +enum GetByHandle { + GET_BY_HANDLE +}; + +class Entry { + public: + // After constructing, you must check good() to test whether the Get + // succeeded. + Entry(BaseTransaction* trans, GetByHandle, int64 handle); + Entry(BaseTransaction* trans, GetById, const Id& id); + Entry(BaseTransaction* trans, GetByServerTag, const std::string& tag); + Entry(BaseTransaction* trans, GetByClientTag, const std::string& tag); + + bool good() const { return 0 != kernel_; } + + BaseTransaction* trans() const { return basetrans_; } + + // Field accessors. + inline int64 Get(MetahandleField field) const { + DCHECK(kernel_); + return kernel_->ref(field); + } + inline Id Get(IdField field) const { + DCHECK(kernel_); + return kernel_->ref(field); + } + inline int64 Get(Int64Field field) const { + DCHECK(kernel_); + return kernel_->ref(field); + } + inline const base::Time& Get(TimeField field) const { + DCHECK(kernel_); + return kernel_->ref(field); + } + inline int64 Get(BaseVersion field) const { + DCHECK(kernel_); + return kernel_->ref(field); + } + inline bool Get(IndexedBitField field) const { + DCHECK(kernel_); + return kernel_->ref(field); + } + inline bool Get(IsDelField field) const { + DCHECK(kernel_); + return kernel_->ref(field); + } + inline bool Get(BitField field) const { + DCHECK(kernel_); + return kernel_->ref(field); + } + const std::string& Get(StringField field) const; + inline const sync_pb::EntitySpecifics& Get(ProtoField field) const { + DCHECK(kernel_); + return kernel_->ref(field); + } + inline bool Get(BitTemp field) const { + DCHECK(kernel_); + return kernel_->ref(field); + } + + ModelType GetServerModelType() const; + ModelType GetModelType() const; + + inline bool ExistsOnClientBecauseNameIsNonEmpty() const { + DCHECK(kernel_); + return !kernel_->ref(NON_UNIQUE_NAME).empty(); + } + + inline bool IsRoot() const { + DCHECK(kernel_); + return kernel_->ref(ID).IsRoot(); + } + + Directory* dir() const; + + const EntryKernel GetKernelCopy() const { + return *kernel_; + } + + // Compute a local predecessor position for |update_item|, based on its + // absolute server position. The returned ID will be a valid predecessor + // under SERVER_PARENT_ID that is consistent with the + // SERVER_POSITION_IN_PARENT ordering. + Id ComputePrevIdFromServerPosition(const Id& parent_id) const; + + // Dumps all entry info into a DictionaryValue and returns it. + // Transfers ownership of the DictionaryValue to the caller. + base::DictionaryValue* ToValue() const; + + protected: // Don't allow creation on heap, except by sync API wrappers. + void* operator new(size_t size) { return (::operator new)(size); } + + inline explicit Entry(BaseTransaction* trans) + : basetrans_(trans), + kernel_(NULL) { } + + protected: + BaseTransaction* const basetrans_; + + EntryKernel* kernel_; + + private: + friend class Directory; + friend class sync_api::ReadNode; + friend std::ostream& operator << (std::ostream& s, const Entry& e); + + DISALLOW_COPY_AND_ASSIGN(Entry); +}; + +std::ostream& operator<<(std::ostream& os, const Entry& entry); + +} // namespace syncable + +#endif // SYNC_SYNCABLE_ENTRY_H_ diff --git a/sync/syncable/entry_kernel.cc b/sync/syncable/entry_kernel.cc new file mode 100644 index 0000000..0ea3d4e --- /dev/null +++ b/sync/syncable/entry_kernel.cc @@ -0,0 +1,151 @@ +// 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 "sync/syncable/entry_kernel.h" + +#include "base/string_number_conversions.h" +#include "sync/protocol/proto_value_conversions.h" +#include "sync/syncable/syncable_enum_conversions.h" + +namespace syncable { + +EntryKernel::EntryKernel() : dirty_(false) { + // Everything else should already be default-initialized. + for (int i = INT64_FIELDS_BEGIN; i < INT64_FIELDS_END; ++i) { + int64_fields[i] = 0; + } +} + +EntryKernel::~EntryKernel() {} + +syncable::ModelType EntryKernel::GetServerModelType() const { + ModelType specifics_type = GetModelTypeFromSpecifics(ref(SERVER_SPECIFICS)); + if (specifics_type != UNSPECIFIED) + return specifics_type; + if (ref(ID).IsRoot()) + return TOP_LEVEL_FOLDER; + // Loose check for server-created top-level folders that aren't + // bound to a particular model type. + if (!ref(UNIQUE_SERVER_TAG).empty() && ref(SERVER_IS_DIR)) + return TOP_LEVEL_FOLDER; + + return UNSPECIFIED; +} + +namespace { + +// Utility function to loop through a set of enum values and add the +// field keys/values in the kernel to the given dictionary. +// +// V should be convertible to Value. +template <class T, class U, class V> +void SetFieldValues(const EntryKernel& kernel, + DictionaryValue* dictionary_value, + const char* (*enum_key_fn)(T), + V* (*enum_value_fn)(U), + int field_key_min, int field_key_max) { + DCHECK_LE(field_key_min, field_key_max); + for (int i = field_key_min; i <= field_key_max; ++i) { + T field = static_cast<T>(i); + const std::string& key = enum_key_fn(field); + V* value = enum_value_fn(kernel.ref(field)); + dictionary_value->Set(key, value); + } +} + +// Helper functions for SetFieldValues(). + +StringValue* Int64ToValue(int64 i) { + return Value::CreateStringValue(base::Int64ToString(i)); +} + +StringValue* TimeToValue(const base::Time& t) { + return Value::CreateStringValue(browser_sync::GetTimeDebugString(t)); +} + +StringValue* IdToValue(const Id& id) { + return id.ToValue(); +} + +} // namespace + +DictionaryValue* EntryKernel::ToValue() const { + DictionaryValue* kernel_info = new DictionaryValue(); + kernel_info->SetBoolean("isDirty", is_dirty()); + kernel_info->Set("serverModelType", ModelTypeToValue(GetServerModelType())); + + // Int64 fields. + SetFieldValues(*this, kernel_info, + &GetMetahandleFieldString, &Int64ToValue, + INT64_FIELDS_BEGIN, META_HANDLE); + SetFieldValues(*this, kernel_info, + &GetBaseVersionString, &Int64ToValue, + META_HANDLE + 1, BASE_VERSION); + SetFieldValues(*this, kernel_info, + &GetInt64FieldString, &Int64ToValue, + BASE_VERSION + 1, INT64_FIELDS_END - 1); + + // Time fields. + SetFieldValues(*this, kernel_info, + &GetTimeFieldString, &TimeToValue, + TIME_FIELDS_BEGIN, TIME_FIELDS_END - 1); + + // ID fields. + SetFieldValues(*this, kernel_info, + &GetIdFieldString, &IdToValue, + ID_FIELDS_BEGIN, ID_FIELDS_END - 1); + + // Bit fields. + SetFieldValues(*this, kernel_info, + &GetIndexedBitFieldString, &Value::CreateBooleanValue, + BIT_FIELDS_BEGIN, INDEXED_BIT_FIELDS_END - 1); + SetFieldValues(*this, kernel_info, + &GetIsDelFieldString, &Value::CreateBooleanValue, + INDEXED_BIT_FIELDS_END, IS_DEL); + SetFieldValues(*this, kernel_info, + &GetBitFieldString, &Value::CreateBooleanValue, + IS_DEL + 1, BIT_FIELDS_END - 1); + + // String fields. + { + // Pick out the function overload we want. + StringValue* (*string_to_value)(const std::string&) = + &Value::CreateStringValue; + SetFieldValues(*this, kernel_info, + &GetStringFieldString, string_to_value, + STRING_FIELDS_BEGIN, STRING_FIELDS_END - 1); + } + + // Proto fields. + SetFieldValues(*this, kernel_info, + &GetProtoFieldString, &browser_sync::EntitySpecificsToValue, + PROTO_FIELDS_BEGIN, PROTO_FIELDS_END - 1); + + // Bit temps. + SetFieldValues(*this, kernel_info, + &GetBitTempString, &Value::CreateBooleanValue, + BIT_TEMPS_BEGIN, BIT_TEMPS_END - 1); + + return kernel_info; +} + +ListValue* EntryKernelMutationMapToValue( + const EntryKernelMutationMap& mutations) { + ListValue* list = new ListValue(); + for (EntryKernelMutationMap::const_iterator it = mutations.begin(); + it != mutations.end(); ++it) { + list->Append(EntryKernelMutationToValue(it->second)); + } + return list; +} + +DictionaryValue* EntryKernelMutationToValue( + const EntryKernelMutation& mutation) { + DictionaryValue* dict = new DictionaryValue(); + dict->Set("original", mutation.original.ToValue()); + dict->Set("mutated", mutation.mutated.ToValue()); + return dict; +} + +} diff --git a/sync/syncable/entry_kernel.h b/sync/syncable/entry_kernel.h new file mode 100644 index 0000000..5c16f1d --- /dev/null +++ b/sync/syncable/entry_kernel.h @@ -0,0 +1,323 @@ +// 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 SYNC_SYNCABLE_ENTRY_KERNEL_H_ +#define SYNC_SYNCABLE_ENTRY_KERNEL_H_ +#pragma once + +#include "base/time.h" +#include "base/values.h" +#include "sync/internal_api/public/syncable/model_type.h" +#include "sync/internal_api/public/util/immutable.h" +#include "sync/protocol/sync.pb.h" +#include "sync/syncable/metahandle_set.h" +#include "sync/syncable/syncable_id.h" +#include "sync/util/time.h" + +namespace syncable { + +// Things you need to update if you change any of the fields below: +// - EntryKernel struct in this file +// - syncable_columns.h +// - syncable_enum_conversions{.h,.cc,_unittest.cc} +// - EntryKernel::EntryKernel(), EntryKernel::ToValue(), operator<< +// for Entry in syncable.cc +// - BindFields() and UnpackEntry() in directory_backing_store.cc +// - TestSimpleFieldsPreservedDuringSaveChanges in syncable_unittest.cc + +static const int64 kInvalidMetaHandle = 0; + +enum { + BEGIN_FIELDS = 0, + INT64_FIELDS_BEGIN = BEGIN_FIELDS +}; + +enum MetahandleField { + // Primary key into the table. Keep this as a handle to the meta entry + // across transactions. + META_HANDLE = INT64_FIELDS_BEGIN +}; + +enum BaseVersion { + // After initial upload, the version is controlled by the server, and is + // increased whenever the data or metadata changes on the server. + BASE_VERSION = META_HANDLE + 1, +}; + +enum Int64Field { + SERVER_VERSION = BASE_VERSION + 1, + + // A numeric position value that indicates the relative ordering of + // this object among its siblings. + SERVER_POSITION_IN_PARENT, + + LOCAL_EXTERNAL_ID, // ID of an item in the external local storage that this + // entry is associated with. (such as bookmarks.js) + + INT64_FIELDS_END +}; + +enum { + INT64_FIELDS_COUNT = INT64_FIELDS_END - INT64_FIELDS_BEGIN, + TIME_FIELDS_BEGIN = INT64_FIELDS_END, +}; + +enum TimeField { + MTIME = TIME_FIELDS_BEGIN, + SERVER_MTIME, + CTIME, + SERVER_CTIME, + TIME_FIELDS_END, +}; + +enum { + TIME_FIELDS_COUNT = TIME_FIELDS_END - TIME_FIELDS_BEGIN, + ID_FIELDS_BEGIN = TIME_FIELDS_END, +}; + +enum IdField { + // Code in InitializeTables relies on ID being the first IdField value. + ID = ID_FIELDS_BEGIN, + PARENT_ID, + SERVER_PARENT_ID, + + PREV_ID, + NEXT_ID, + ID_FIELDS_END +}; + +enum { + ID_FIELDS_COUNT = ID_FIELDS_END - ID_FIELDS_BEGIN, + BIT_FIELDS_BEGIN = ID_FIELDS_END +}; + +enum IndexedBitField { + IS_UNSYNCED = BIT_FIELDS_BEGIN, + IS_UNAPPLIED_UPDATE, + INDEXED_BIT_FIELDS_END, +}; + +enum IsDelField { + IS_DEL = INDEXED_BIT_FIELDS_END, +}; + +enum BitField { + IS_DIR = IS_DEL + 1, + SERVER_IS_DIR, + SERVER_IS_DEL, + BIT_FIELDS_END +}; + +enum { + BIT_FIELDS_COUNT = BIT_FIELDS_END - BIT_FIELDS_BEGIN, + STRING_FIELDS_BEGIN = BIT_FIELDS_END +}; + +enum StringField { + // Name, will be truncated by server. Can be duplicated in a folder. + NON_UNIQUE_NAME = STRING_FIELDS_BEGIN, + // The server version of |NON_UNIQUE_NAME|. + SERVER_NON_UNIQUE_NAME, + + // A tag string which identifies this node as a particular top-level + // permanent object. The tag can be thought of as a unique key that + // identifies a singleton instance. + UNIQUE_SERVER_TAG, // Tagged by the server + UNIQUE_CLIENT_TAG, // Tagged by the client + STRING_FIELDS_END, +}; + +enum { + STRING_FIELDS_COUNT = STRING_FIELDS_END - STRING_FIELDS_BEGIN, + PROTO_FIELDS_BEGIN = STRING_FIELDS_END +}; + +// From looking at the sqlite3 docs, it's not directly stated, but it +// seems the overhead for storing a NULL blob is very small. +enum ProtoField { + SPECIFICS = PROTO_FIELDS_BEGIN, + SERVER_SPECIFICS, + BASE_SERVER_SPECIFICS, + PROTO_FIELDS_END, +}; + +enum { + FIELD_COUNT = PROTO_FIELDS_END, + // Past this point we have temporaries, stored in memory only. + BEGIN_TEMPS = PROTO_FIELDS_END, + BIT_TEMPS_BEGIN = BEGIN_TEMPS, +}; + +enum BitTemp { + // Not to be confused with IS_UNSYNCED, this bit is used to detect local + // changes to items that happen during the server Commit operation. + SYNCING = BIT_TEMPS_BEGIN, + BIT_TEMPS_END, +}; + +enum { + BIT_TEMPS_COUNT = BIT_TEMPS_END - BIT_TEMPS_BEGIN +}; + +enum { + PROTO_FIELDS_COUNT = PROTO_FIELDS_END - PROTO_FIELDS_BEGIN +}; + + +struct EntryKernel { + private: + std::string string_fields[STRING_FIELDS_COUNT]; + sync_pb::EntitySpecifics specifics_fields[PROTO_FIELDS_COUNT]; + int64 int64_fields[INT64_FIELDS_COUNT]; + base::Time time_fields[TIME_FIELDS_COUNT]; + Id id_fields[ID_FIELDS_COUNT]; + std::bitset<BIT_FIELDS_COUNT> bit_fields; + std::bitset<BIT_TEMPS_COUNT> bit_temps; + + public: + EntryKernel(); + ~EntryKernel(); + + // Set the dirty bit, and optionally add this entry's metahandle to + // a provided index on dirty bits in |dirty_index|. Parameter may be null, + // and will result only in setting the dirty bit of this entry. + inline void mark_dirty(syncable::MetahandleSet* dirty_index) { + if (!dirty_ && dirty_index) { + DCHECK_NE(0, ref(META_HANDLE)); + dirty_index->insert(ref(META_HANDLE)); + } + dirty_ = true; + } + + // Clear the dirty bit, and optionally remove this entry's metahandle from + // a provided index on dirty bits in |dirty_index|. Parameter may be null, + // and will result only in clearing dirty bit of this entry. + inline void clear_dirty(syncable::MetahandleSet* dirty_index) { + if (dirty_ && dirty_index) { + DCHECK_NE(0, ref(META_HANDLE)); + dirty_index->erase(ref(META_HANDLE)); + } + dirty_ = false; + } + + inline bool is_dirty() const { + return dirty_; + } + + // Setters. + inline void put(MetahandleField field, int64 value) { + int64_fields[field - INT64_FIELDS_BEGIN] = value; + } + inline void put(Int64Field field, int64 value) { + int64_fields[field - INT64_FIELDS_BEGIN] = value; + } + inline void put(TimeField field, const base::Time& value) { + // Round-trip to proto time format and back so that we have + // consistent time resolutions (ms). + time_fields[field - TIME_FIELDS_BEGIN] = + browser_sync::ProtoTimeToTime( + browser_sync::TimeToProtoTime(value)); + } + inline void put(IdField field, const Id& value) { + id_fields[field - ID_FIELDS_BEGIN] = value; + } + inline void put(BaseVersion field, int64 value) { + int64_fields[field - INT64_FIELDS_BEGIN] = value; + } + inline void put(IndexedBitField field, bool value) { + bit_fields[field - BIT_FIELDS_BEGIN] = value; + } + inline void put(IsDelField field, bool value) { + bit_fields[field - BIT_FIELDS_BEGIN] = value; + } + inline void put(BitField field, bool value) { + bit_fields[field - BIT_FIELDS_BEGIN] = value; + } + inline void put(StringField field, const std::string& value) { + string_fields[field - STRING_FIELDS_BEGIN] = value; + } + inline void put(ProtoField field, const sync_pb::EntitySpecifics& value) { + specifics_fields[field - PROTO_FIELDS_BEGIN].CopyFrom(value); + } + inline void put(BitTemp field, bool value) { + bit_temps[field - BIT_TEMPS_BEGIN] = value; + } + + // Const ref getters. + inline int64 ref(MetahandleField field) const { + return int64_fields[field - INT64_FIELDS_BEGIN]; + } + inline int64 ref(Int64Field field) const { + return int64_fields[field - INT64_FIELDS_BEGIN]; + } + inline const base::Time& ref(TimeField field) const { + return time_fields[field - TIME_FIELDS_BEGIN]; + } + inline const Id& ref(IdField field) const { + return id_fields[field - ID_FIELDS_BEGIN]; + } + inline int64 ref(BaseVersion field) const { + return int64_fields[field - INT64_FIELDS_BEGIN]; + } + inline bool ref(IndexedBitField field) const { + return bit_fields[field - BIT_FIELDS_BEGIN]; + } + inline bool ref(IsDelField field) const { + return bit_fields[field - BIT_FIELDS_BEGIN]; + } + inline bool ref(BitField field) const { + return bit_fields[field - BIT_FIELDS_BEGIN]; + } + inline const std::string& ref(StringField field) const { + return string_fields[field - STRING_FIELDS_BEGIN]; + } + inline const sync_pb::EntitySpecifics& ref(ProtoField field) const { + return specifics_fields[field - PROTO_FIELDS_BEGIN]; + } + inline bool ref(BitTemp field) const { + return bit_temps[field - BIT_TEMPS_BEGIN]; + } + + // Non-const, mutable ref getters for object types only. + inline std::string& mutable_ref(StringField field) { + return string_fields[field - STRING_FIELDS_BEGIN]; + } + inline sync_pb::EntitySpecifics& mutable_ref(ProtoField field) { + return specifics_fields[field - PROTO_FIELDS_BEGIN]; + } + inline Id& mutable_ref(IdField field) { + return id_fields[field - ID_FIELDS_BEGIN]; + } + + syncable::ModelType GetServerModelType() const; + + // Dumps all kernel info into a DictionaryValue and returns it. + // Transfers ownership of the DictionaryValue to the caller. + base::DictionaryValue* ToValue() const; + + private: + // Tracks whether this entry needs to be saved to the database. + bool dirty_; +}; + +struct EntryKernelMutation { + EntryKernel original, mutated; +}; + +typedef std::map<int64, EntryKernelMutation> EntryKernelMutationMap; + +typedef browser_sync::Immutable<EntryKernelMutationMap> + ImmutableEntryKernelMutationMap; + +// Caller owns the return value. +base::DictionaryValue* EntryKernelMutationToValue( + const EntryKernelMutation& mutation); + +// Caller owns the return value. +base::ListValue* EntryKernelMutationMapToValue( + const EntryKernelMutationMap& mutations); + +} // namespace syncable + +#endif // SYNC_SYNCABLE_ENTRY_KERNEL_H_ diff --git a/sync/syncable/metahandle_set.h b/sync/syncable/metahandle_set.h new file mode 100644 index 0000000..42fd04f --- /dev/null +++ b/sync/syncable/metahandle_set.h @@ -0,0 +1,16 @@ +// 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 SYNC_SYNCABLE_METAHANDLE_SET_ +#define SYNC_SYNCABLE_METAHANDLE_SET_ + +#include "base/basictypes.h" + +namespace syncable { + +typedef std::set<int64> MetahandleSet; + +} + +#endif // SYNC_SYNCABLE_METAHANDLE_SET_ diff --git a/sync/syncable/mutable_entry.cc b/sync/syncable/mutable_entry.cc new file mode 100644 index 0000000..77aa32c --- /dev/null +++ b/sync/syncable/mutable_entry.cc @@ -0,0 +1,410 @@ +// 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 "sync/syncable/mutable_entry.h" + +#include "base/memory/scoped_ptr.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/scoped_index_updater.h" +#include "sync/syncable/scoped_kernel_lock.h" +#include "sync/syncable/syncable-inl.h" +#include "sync/syncable/syncable_changes_version.h" +#include "sync/syncable/syncable_util.h" +#include "sync/syncable/write_transaction.h" + +using std::string; + +namespace syncable { + +MutableEntry::MutableEntry(WriteTransaction* trans, Create, + const Id& parent_id, const string& name) + : Entry(trans), + write_transaction_(trans) { + Init(trans, parent_id, name); +} + + +void MutableEntry::Init(WriteTransaction* trans, const Id& parent_id, + const string& name) { + scoped_ptr<EntryKernel> kernel(new EntryKernel); + kernel_ = NULL; + + kernel->put(ID, trans->directory_->NextId()); + kernel->put(META_HANDLE, trans->directory_->NextMetahandle()); + kernel->mark_dirty(trans->directory_->kernel_->dirty_metahandles); + kernel->put(PARENT_ID, parent_id); + kernel->put(NON_UNIQUE_NAME, name); + const base::Time& now = base::Time::Now(); + kernel->put(CTIME, now); + kernel->put(MTIME, now); + // We match the database defaults here + kernel->put(BASE_VERSION, CHANGES_VERSION); + if (!trans->directory()->InsertEntry(trans, kernel.get())) { + return; // We failed inserting, nothing more to do. + } + // Because this entry is new, it was originally deleted. + kernel->put(IS_DEL, true); + trans->SaveOriginal(kernel.get()); + kernel->put(IS_DEL, false); + + // Now swap the pointers. + kernel_ = kernel.release(); +} + +MutableEntry::MutableEntry(WriteTransaction* trans, CreateNewUpdateItem, + const Id& id) + : Entry(trans), write_transaction_(trans) { + Entry same_id(trans, GET_BY_ID, id); + kernel_ = NULL; + if (same_id.good()) { + return; // already have an item with this ID. + } + scoped_ptr<EntryKernel> kernel(new EntryKernel()); + + kernel->put(ID, id); + kernel->put(META_HANDLE, trans->directory_->NextMetahandle()); + kernel->mark_dirty(trans->directory_->kernel_->dirty_metahandles); + kernel->put(IS_DEL, true); + // We match the database defaults here + kernel->put(BASE_VERSION, CHANGES_VERSION); + if (!trans->directory()->InsertEntry(trans, kernel.get())) { + return; // Failed inserting. + } + trans->SaveOriginal(kernel.get()); + + kernel_ = kernel.release(); +} + +MutableEntry::MutableEntry(WriteTransaction* trans, GetById, const Id& id) + : Entry(trans, GET_BY_ID, id), write_transaction_(trans) { +} + +MutableEntry::MutableEntry(WriteTransaction* trans, GetByHandle, + int64 metahandle) + : Entry(trans, GET_BY_HANDLE, metahandle), write_transaction_(trans) { +} + +MutableEntry::MutableEntry(WriteTransaction* trans, GetByClientTag, + const std::string& tag) + : Entry(trans, GET_BY_CLIENT_TAG, tag), write_transaction_(trans) { +} + +MutableEntry::MutableEntry(WriteTransaction* trans, GetByServerTag, + const string& tag) + : Entry(trans, GET_BY_SERVER_TAG, tag), write_transaction_(trans) { +} + +bool MutableEntry::PutIsDel(bool is_del) { + DCHECK(kernel_); + write_transaction_->SaveOriginal(kernel_); + if (is_del == kernel_->ref(IS_DEL)) { + return true; + } + if (is_del) { + if (!UnlinkFromOrder()) { + return false; + } + + // If the server never knew about this item and it's deleted then we don't + // need to keep it around. Unsetting IS_UNSYNCED will: + // - Ensure that the item is never committed to the server. + // - Allow any items with the same UNIQUE_CLIENT_TAG created on other + // clients to override this entry. + // - Let us delete this entry permanently through + // DirectoryBackingStore::DropDeletedEntries() when we next restart sync. + // This will save memory and avoid crbug.com/125381. + if (!Get(ID).ServerKnows()) { + Put(IS_UNSYNCED, false); + } + } + + { + ScopedKernelLock lock(dir()); + // Some indices don't include deleted items and must be updated + // upon a value change. + ScopedIndexUpdater<ParentIdAndHandleIndexer> updater(lock, kernel_, + dir()->kernel_->parent_id_child_index); + + kernel_->put(IS_DEL, is_del); + kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); + } + + if (!is_del) + // Restores position to the 0th index. + if (!PutPredecessor(Id())) { + // TODO(lipalani) : Propagate the error to caller. crbug.com/100444. + NOTREACHED(); + } + + return true; +} + +bool MutableEntry::Put(Int64Field field, const int64& value) { + DCHECK(kernel_); + write_transaction_->SaveOriginal(kernel_); + if (kernel_->ref(field) != value) { + ScopedKernelLock lock(dir()); + if (SERVER_POSITION_IN_PARENT == field) { + ScopedIndexUpdater<ParentIdAndHandleIndexer> updater(lock, kernel_, + dir()->kernel_->parent_id_child_index); + kernel_->put(field, value); + } else { + kernel_->put(field, value); + } + kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); + } + return true; +} + +bool MutableEntry::Put(TimeField field, const base::Time& value) { + DCHECK(kernel_); + write_transaction_->SaveOriginal(kernel_); + if (kernel_->ref(field) != value) { + kernel_->put(field, value); + kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); + } + return true; +} + +bool MutableEntry::Put(IdField field, const Id& value) { + DCHECK(kernel_); + write_transaction_->SaveOriginal(kernel_); + if (kernel_->ref(field) != value) { + if (ID == field) { + if (!dir()->ReindexId(write_transaction(), kernel_, value)) + return false; + } else if (PARENT_ID == field) { + PutParentIdPropertyOnly(value); // Makes sibling order inconsistent. + // Fixes up the sibling order inconsistency. + if (!PutPredecessor(Id())) { + // TODO(lipalani) : Propagate the error to caller. crbug.com/100444. + NOTREACHED(); + } + } else { + kernel_->put(field, value); + } + kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); + } + return true; +} + +void MutableEntry::PutParentIdPropertyOnly(const Id& parent_id) { + write_transaction_->SaveOriginal(kernel_); + dir()->ReindexParentId(write_transaction(), kernel_, parent_id); + kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); +} + +bool MutableEntry::Put(BaseVersion field, int64 value) { + DCHECK(kernel_); + write_transaction_->SaveOriginal(kernel_); + if (kernel_->ref(field) != value) { + kernel_->put(field, value); + kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); + } + return true; +} + +bool MutableEntry::Put(StringField field, const string& value) { + DCHECK(kernel_); + write_transaction_->SaveOriginal(kernel_); + if (field == UNIQUE_CLIENT_TAG) { + return PutUniqueClientTag(value); + } + + if (kernel_->ref(field) != value) { + kernel_->put(field, value); + kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); + } + return true; +} + +bool MutableEntry::Put(ProtoField field, + const sync_pb::EntitySpecifics& value) { + DCHECK(kernel_); + write_transaction_->SaveOriginal(kernel_); + // TODO(ncarter): This is unfortunately heavyweight. Can we do + // better? + if (kernel_->ref(field).SerializeAsString() != value.SerializeAsString()) { + const bool update_unapplied_updates_index = + (field == SERVER_SPECIFICS) && kernel_->ref(IS_UNAPPLIED_UPDATE); + if (update_unapplied_updates_index) { + // Remove ourselves from unapplied_update_metahandles with our + // old server type. + const syncable::ModelType old_server_type = + kernel_->GetServerModelType(); + const int64 metahandle = kernel_->ref(META_HANDLE); + size_t erase_count = + dir()->kernel_->unapplied_update_metahandles[old_server_type] + .erase(metahandle); + DCHECK_EQ(erase_count, 1u); + } + + kernel_->put(field, value); + kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); + + if (update_unapplied_updates_index) { + // Add ourselves back into unapplied_update_metahandles with our + // new server type. + const syncable::ModelType new_server_type = + kernel_->GetServerModelType(); + const int64 metahandle = kernel_->ref(META_HANDLE); + dir()->kernel_->unapplied_update_metahandles[new_server_type] + .insert(metahandle); + } + } + return true; +} + +bool MutableEntry::Put(BitField field, bool value) { + DCHECK(kernel_); + write_transaction_->SaveOriginal(kernel_); + if (kernel_->ref(field) != value) { + kernel_->put(field, value); + kernel_->mark_dirty(GetDirtyIndexHelper()); + } + return true; +} + +MetahandleSet* MutableEntry::GetDirtyIndexHelper() { + return dir()->kernel_->dirty_metahandles; +} + +bool MutableEntry::PutUniqueClientTag(const string& new_tag) { + write_transaction_->SaveOriginal(kernel_); + // There is no SERVER_UNIQUE_CLIENT_TAG. This field is similar to ID. + string old_tag = kernel_->ref(UNIQUE_CLIENT_TAG); + if (old_tag == new_tag) { + return true; + } + + ScopedKernelLock lock(dir()); + if (!new_tag.empty()) { + // Make sure your new value is not in there already. + EntryKernel lookup_kernel_ = *kernel_; + lookup_kernel_.put(UNIQUE_CLIENT_TAG, new_tag); + bool new_tag_conflicts = + (dir()->kernel_->client_tag_index->count(&lookup_kernel_) > 0); + if (new_tag_conflicts) { + return false; + } + } + + { + ScopedIndexUpdater<ClientTagIndexer> index_updater(lock, kernel_, + dir()->kernel_->client_tag_index); + kernel_->put(UNIQUE_CLIENT_TAG, new_tag); + kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); + } + return true; +} + +bool MutableEntry::Put(IndexedBitField field, bool value) { + DCHECK(kernel_); + write_transaction_->SaveOriginal(kernel_); + if (kernel_->ref(field) != value) { + MetahandleSet* index; + if (IS_UNSYNCED == field) { + index = dir()->kernel_->unsynced_metahandles; + } else { + // Use kernel_->GetServerModelType() instead of + // GetServerModelType() as we may trigger some DCHECKs in the + // latter. + index = + &dir()->kernel_->unapplied_update_metahandles[ + kernel_->GetServerModelType()]; + } + + ScopedKernelLock lock(dir()); + if (value) { + if (!SyncAssert(index->insert(kernel_->ref(META_HANDLE)).second, + FROM_HERE, + "Could not insert", + write_transaction())) { + return false; + } + } else { + if (!SyncAssert(1U == index->erase(kernel_->ref(META_HANDLE)), + FROM_HERE, + "Entry Not succesfully erased", + write_transaction())) { + return false; + } + } + kernel_->put(field, value); + kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); + } + return true; +} + +bool MutableEntry::UnlinkFromOrder() { + ScopedKernelLock lock(dir()); + return dir()->UnlinkEntryFromOrder(kernel_, + write_transaction(), + &lock, + NODE_MANIPULATION); +} + +bool MutableEntry::PutPredecessor(const Id& predecessor_id) { + if (!UnlinkFromOrder()) + return false; + + if (Get(IS_DEL)) { + DCHECK(predecessor_id.IsNull()); + return true; + } + + // TODO(ncarter): It should be possible to not maintain position for + // non-bookmark items. However, we'd need to robustly handle all possible + // permutations of setting IS_DEL and the SPECIFICS to identify the + // object type; or else, we'd need to add a ModelType to the + // MutableEntry's Create ctor. + // if (!ShouldMaintainPosition()) { + // return false; + // } + + // This is classic insert-into-doubly-linked-list from CS 101 and your last + // job interview. An "IsRoot" Id signifies the head or tail. + Id successor_id; + if (!predecessor_id.IsRoot()) { + MutableEntry predecessor(write_transaction(), GET_BY_ID, predecessor_id); + if (!predecessor.good()) { + LOG(ERROR) << "Predecessor is not good : " + << predecessor_id.GetServerId(); + return false; + } + if (predecessor.Get(PARENT_ID) != Get(PARENT_ID)) + return false; + successor_id = predecessor.Get(NEXT_ID); + predecessor.Put(NEXT_ID, Get(ID)); + } else { + syncable::Directory* dir = trans()->directory(); + if (!dir->GetFirstChildId(trans(), Get(PARENT_ID), &successor_id)) { + return false; + } + } + if (!successor_id.IsRoot()) { + MutableEntry successor(write_transaction(), GET_BY_ID, successor_id); + if (!successor.good()) { + LOG(ERROR) << "Successor is not good: " + << successor_id.GetServerId(); + return false; + } + if (successor.Get(PARENT_ID) != Get(PARENT_ID)) + return false; + successor.Put(PREV_ID, Get(ID)); + } + DCHECK(predecessor_id != Get(ID)); + DCHECK(successor_id != Get(ID)); + Put(PREV_ID, predecessor_id); + Put(NEXT_ID, successor_id); + return true; +} + +bool MutableEntry::Put(BitTemp field, bool value) { + DCHECK(kernel_); + kernel_->put(field, value); + return true; +} + +} diff --git a/sync/syncable/mutable_entry.h b/sync/syncable/mutable_entry.h new file mode 100644 index 0000000..4ead080 --- /dev/null +++ b/sync/syncable/mutable_entry.h @@ -0,0 +1,119 @@ +// 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 SYNC_SYNCABLE_MUTABLE_ENTRY_H_ +#define SYNC_SYNCABLE_MUTABLE_ENTRY_H_ +#pragma once + +#include "sync/syncable/entry.h" +#include "sync/syncable/metahandle_set.h" + +namespace sync_api { +class WriteNode; +} + +namespace syncable { + +class WriteTransaction; + +enum Create { + CREATE +}; + +enum CreateNewUpdateItem { + CREATE_NEW_UPDATE_ITEM +}; + +// A mutable meta entry. Changes get committed to the database when the +// WriteTransaction is destroyed. +class MutableEntry : public Entry { + void Init(WriteTransaction* trans, const Id& parent_id, + const std::string& name); + + public: + MutableEntry(WriteTransaction* trans, Create, const Id& parent_id, + const std::string& name); + MutableEntry(WriteTransaction* trans, CreateNewUpdateItem, const Id& id); + MutableEntry(WriteTransaction* trans, GetByHandle, int64); + MutableEntry(WriteTransaction* trans, GetById, const Id&); + MutableEntry(WriteTransaction* trans, GetByClientTag, const std::string& tag); + MutableEntry(WriteTransaction* trans, GetByServerTag, const std::string& tag); + + inline WriteTransaction* write_transaction() const { + return write_transaction_; + } + + // Field Accessors. Some of them trigger the re-indexing of the entry. + // Return true on success, return false on failure, which means + // that putting the value would have caused a duplicate in the index. + // TODO(chron): Remove some of these unecessary return values. + bool Put(Int64Field field, const int64& value); + bool Put(TimeField field, const base::Time& value); + bool Put(IdField field, const Id& value); + + // Do a simple property-only update if the PARENT_ID field. Use with caution. + // + // The normal Put(IS_PARENT) call will move the item to the front of the + // sibling order to maintain the linked list invariants when the parent + // changes. That's usually what you want to do, but it's inappropriate + // when the caller is trying to change the parent ID of a the whole set + // of children (e.g. because the ID changed during a commit). For those + // cases, there's this function. It will corrupt the sibling ordering + // if you're not careful. + void PutParentIdPropertyOnly(const Id& parent_id); + + bool Put(StringField field, const std::string& value); + bool Put(BaseVersion field, int64 value); + + bool Put(ProtoField field, const sync_pb::EntitySpecifics& value); + bool Put(BitField field, bool value); + inline bool Put(IsDelField field, bool value) { + return PutIsDel(value); + } + bool Put(IndexedBitField field, bool value); + + // Sets the position of this item, and updates the entry kernels of the + // adjacent siblings so that list invariants are maintained. Returns false + // and fails if |predecessor_id| does not identify a sibling. Pass the root + // ID to put the node in first position. + bool PutPredecessor(const Id& predecessor_id); + + bool Put(BitTemp field, bool value); + + protected: + syncable::MetahandleSet* GetDirtyIndexHelper(); + + bool PutIsDel(bool value); + + private: + friend class Directory; + friend class WriteTransaction; + friend class sync_api::WriteNode; + + // Don't allow creation on heap, except by sync API wrappers. + void* operator new(size_t size) { return (::operator new)(size); } + + bool PutUniqueClientTag(const std::string& value); + + // Adjusts the successor and predecessor entries so that they no longer + // refer to this entry. + bool UnlinkFromOrder(); + + // Kind of redundant. We should reduce the number of pointers + // floating around if at all possible. Could we store this in Directory? + // Scope: Set on construction, never changed after that. + WriteTransaction* const write_transaction_; + + protected: + MutableEntry(); + + DISALLOW_COPY_AND_ASSIGN(MutableEntry); +}; + +// This function sets only the flags needed to get this entry to sync. +bool MarkForSyncing(syncable::MutableEntry* e); + +} // namespace syncable + +#endif // SYNC_SYNCABLE_MUTABLE_ENTRY_H_ diff --git a/sync/syncable/read_transaction.cc b/sync/syncable/read_transaction.cc new file mode 100644 index 0000000..a71f1b3 --- /dev/null +++ b/sync/syncable/read_transaction.cc @@ -0,0 +1,20 @@ +// 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 "sync/syncable/read_transaction.h" + +namespace syncable { + +ReadTransaction::ReadTransaction(const tracked_objects::Location& location, + Directory* directory) + : BaseTransaction(location, "ReadTransaction", INVALID, directory) { + Lock(); +} + +ReadTransaction::~ReadTransaction() { + HandleUnrecoverableErrorIfSet(); + Unlock(); +} + +} // namespace syncable diff --git a/sync/syncable/read_transaction.h b/sync/syncable/read_transaction.h new file mode 100644 index 0000000..37241ff --- /dev/null +++ b/sync/syncable/read_transaction.h @@ -0,0 +1,34 @@ +// 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 SYNC_SYNCABLE_READ_TRANSACTION_H_ +#define SYNC_SYNCABLE_READ_TRANSACTION_H_ +#pragma once + +#include "sync/syncable/base_transaction.h" + +namespace sync_api { +class ReadTransaction; +} + +namespace syncable { + +// Locks db in constructor, unlocks in destructor. +class ReadTransaction : public BaseTransaction { + public: + ReadTransaction(const tracked_objects::Location& from_here, + Directory* directory); + + virtual ~ReadTransaction(); + + protected: // Don't allow creation on heap, except by sync API wrapper. + friend class sync_api::ReadTransaction; + void* operator new(size_t size) { return (::operator new)(size); } + + DISALLOW_COPY_AND_ASSIGN(ReadTransaction); +}; + +} + +#endif // SYNC_SYNCABLE_READ_TRANSACTION_H_ diff --git a/sync/syncable/scoped_index_updater.h b/sync/syncable/scoped_index_updater.h new file mode 100644 index 0000000..f24d0d2 --- /dev/null +++ b/sync/syncable/scoped_index_updater.h @@ -0,0 +1,55 @@ +// 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 SYNC_SYNCABLE_SCOPED_INDEX_UPDATER_H_ +#define SYNC_SYNCABLE_SCOPED_INDEX_UPDATER_H_ + +#include "sync/syncable/directory.h" + +namespace syncable { +class ScopedKernelLock; + +// A ScopedIndexUpdater temporarily removes an entry from an index, +// and restores it to the index when the scope exits. This simplifies +// the common pattern where items need to be removed from an index +// before updating the field. +// +// This class is parameterized on the Indexer traits type, which +// must define a Comparator and a static bool ShouldInclude +// function for testing whether the item ought to be included +// in the index. +template<typename Indexer> +class ScopedIndexUpdater { + public: + ScopedIndexUpdater(const ScopedKernelLock& proof_of_lock, + EntryKernel* entry, + typename Index<Indexer>::Set* index) + : entry_(entry), + index_(index) { + // First call to ShouldInclude happens before the field is updated. + if (Indexer::ShouldInclude(entry_)) { + // TODO(lipalani): Replace this CHECK with |SyncAssert| by refactorting + // this class into a function. + CHECK(index_->erase(entry_)); + } + } + + ~ScopedIndexUpdater() { + // Second call to ShouldInclude happens after the field is updated. + if (Indexer::ShouldInclude(entry_)) { + // TODO(lipalani): Replace this CHECK with |SyncAssert| by refactorting + // this class into a function. + CHECK(index_->insert(entry_).second); + } + } + private: + // The entry that was temporarily removed from the index. + EntryKernel* entry_; + // The index which we are updating. + typename Index<Indexer>::Set* const index_; +}; + +} // namespace syncable + +#endif // SYNC_SYNCABLE_SCOPED_INDEX_UPDATER_H_ diff --git a/sync/syncable/scoped_kernel_lock.h b/sync/syncable/scoped_kernel_lock.h new file mode 100644 index 0000000..aaac150 --- /dev/null +++ b/sync/syncable/scoped_kernel_lock.h @@ -0,0 +1,28 @@ +// 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 SYNC_SYNCABLE_SCOPED_KERNEL_LOCK_H_ +#define SYNC_SYNCABLE_SCOPED_KERNEL_LOCK_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/synchronization/lock.h" + +namespace syncable { + +class Directory; + +class ScopedKernelLock { + public: + explicit ScopedKernelLock(const Directory* dir); + ~ScopedKernelLock() {} + + base::AutoLock scoped_lock_; + Directory* const dir_; + DISALLOW_COPY_AND_ASSIGN(ScopedKernelLock); +}; + +} // namespace syncable + +#endif // SYNC_SYNCABLE_SCOPED_KERNEL_LOCK_H_ diff --git a/sync/syncable/syncable-inl.h b/sync/syncable/syncable-inl.h index 1b28139..5b8046b 100644 --- a/sync/syncable/syncable-inl.h +++ b/sync/syncable/syncable-inl.h @@ -6,7 +6,7 @@ #define SYNC_SYNCABLE_SYNCABLE_INL_H_ #pragma once -#include "sync/syncable/syncable.h" +#include "sync/syncable/entry_kernel.h" namespace syncable { diff --git a/sync/syncable/syncable_columns.h b/sync/syncable/syncable_columns.h index 18a0215..faf0da7 100644 --- a/sync/syncable/syncable_columns.h +++ b/sync/syncable/syncable_columns.h @@ -6,7 +6,7 @@ #define SYNC_SYNCABLE_SYNCABLE_COLUMNS_H_ #pragma once -#include "sync/syncable/syncable.h" +#include "sync/syncable/entry_kernel.h" #include "sync/syncable/syncable_changes_version.h" namespace syncable { diff --git a/sync/syncable/syncable_enum_conversions.cc b/sync/syncable/syncable_enum_conversions.cc index eaf5edd..e3131c3 100644 --- a/sync/syncable/syncable_enum_conversions.cc +++ b/sync/syncable/syncable_enum_conversions.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Keep this file in sync with syncable.h. +// Keep this file in sync with entry_kernel.h. #include "sync/syncable/syncable_enum_conversions.h" diff --git a/sync/syncable/syncable_enum_conversions.h b/sync/syncable/syncable_enum_conversions.h index ae251c8..1c058408 100644 --- a/sync/syncable/syncable_enum_conversions.h +++ b/sync/syncable/syncable_enum_conversions.h @@ -6,9 +6,9 @@ #define SYNC_SYNCABLE_SYNCABLE_ENUM_CONVERSIONS_H_ #pragma once -// Keep this file in sync with syncable.h. +// Keep this file in sync with entry_kernel.h. -#include "sync/syncable/syncable.h" +#include "sync/syncable/entry_kernel.h" // Utility functions to get the string equivalent for some syncable // enums. diff --git a/sync/syncable/syncable_enum_conversions_unittest.cc b/sync/syncable/syncable_enum_conversions_unittest.cc index 8ce4a40..bba88ea 100644 --- a/sync/syncable/syncable_enum_conversions_unittest.cc +++ b/sync/syncable/syncable_enum_conversions_unittest.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Keep this file in sync with syncable.h. +// Keep this file in sync with entry_kernel.h. #include "sync/syncable/syncable_enum_conversions.h" diff --git a/sync/syncable/syncable_mock.h b/sync/syncable/syncable_mock.h index 27fcee8..0dec075 100644 --- a/sync/syncable/syncable_mock.h +++ b/sync/syncable/syncable_mock.h @@ -8,7 +8,8 @@ #include <string> -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/write_transaction.h" #include "sync/test/fake_encryptor.h" #include "sync/test/null_directory_change_delegate.h" #include "testing/gmock/include/gmock/gmock.h" diff --git a/sync/syncable/syncable_unittest.cc b/sync/syncable/syncable_unittest.cc index c6f44d3..bc821bf 100644 --- a/sync/syncable/syncable_unittest.cc +++ b/sync/syncable/syncable_unittest.cc @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "sync/syncable/syncable.h" - #include <string> #include "base/compiler_specific.h" @@ -20,16 +18,21 @@ #include "base/threading/platform_thread.h" #include "base/values.h" #include "sync/engine/syncproto.h" -#include "sync/util/test_unrecoverable_error_handler.h" +#include "sync/protocol/bookmark_specifics.pb.h" #include "sync/syncable/directory_backing_store.h" #include "sync/syncable/directory_change_delegate.h" +#include "sync/syncable/metahandle_set.h" +#include "sync/syncable/mutable_entry.h" #include "sync/syncable/on_disk_directory_backing_store.h" +#include "sync/syncable/read_transaction.h" +#include "sync/syncable/syncable_util.h" +#include "sync/syncable/write_transaction.h" #include "sync/test/engine/test_id_factory.h" #include "sync/test/engine/test_syncable_utils.h" #include "sync/test/fake_encryptor.h" #include "sync/test/null_directory_change_delegate.h" #include "sync/test/null_transaction_observer.h" -#include "sync/protocol/bookmark_specifics.pb.h" +#include "sync/util/test_unrecoverable_error_handler.h" #include "testing/gtest/include/gtest/gtest.h" using base::ExpectDictBooleanValue; diff --git a/sync/syncable/syncable_util.cc b/sync/syncable/syncable_util.cc new file mode 100644 index 0000000..cf5af08 --- /dev/null +++ b/sync/syncable/syncable_util.cc @@ -0,0 +1,104 @@ +// 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 "sync/syncable/syncable_util.h" + +#include "base/location.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/entry.h" +#include "sync/syncable/mutable_entry.h" +#include "sync/syncable/syncable_id.h" +#include "sync/syncable/write_transaction.h" + +namespace syncable { + +bool IsLegalNewParent(BaseTransaction* trans, const Id& entry_id, + const Id& new_parent_id) { + if (entry_id.IsRoot()) + return false; + // we have to ensure that the entry is not an ancestor of the new parent. + Id ancestor_id = new_parent_id; + while (!ancestor_id.IsRoot()) { + if (entry_id == ancestor_id) + return false; + Entry new_parent(trans, GET_BY_ID, ancestor_id); + if (!SyncAssert(new_parent.good(), + FROM_HERE, + "Invalid new parent", + trans)) + return false; + ancestor_id = new_parent.Get(PARENT_ID); + } + return true; +} + +// This function sets only the flags needed to get this entry to sync. +bool MarkForSyncing(syncable::MutableEntry* e) { + DCHECK_NE(static_cast<MutableEntry*>(NULL), e); + DCHECK(!e->IsRoot()) << "We shouldn't mark a permanent object for syncing."; + if (!(e->Put(IS_UNSYNCED, true))) + return false; + e->Put(SYNCING, false); + return true; +} + +void ChangeEntryIDAndUpdateChildren( + syncable::WriteTransaction* trans, + syncable::MutableEntry* entry, + const syncable::Id& new_id) { + syncable::Id old_id = entry->Get(ID); + if (!entry->Put(ID, new_id)) { + Entry old_entry(trans, GET_BY_ID, new_id); + CHECK(old_entry.good()); + LOG(FATAL) << "Attempt to change ID to " << new_id + << " conflicts with existing entry.\n\n" + << *entry << "\n\n" << old_entry; + } + if (entry->Get(IS_DIR)) { + // Get all child entries of the old id. + syncable::Directory::ChildHandles children; + trans->directory()->GetChildHandlesById(trans, old_id, &children); + Directory::ChildHandles::iterator i = children.begin(); + while (i != children.end()) { + MutableEntry child_entry(trans, GET_BY_HANDLE, *i++); + CHECK(child_entry.good()); + // Use the unchecked setter here to avoid touching the child's NEXT_ID + // and PREV_ID fields (which Put(PARENT_ID) would normally do to + // maintain linked-list invariants). In this case, NEXT_ID and PREV_ID + // among the children will be valid after the loop, since we update all + // the children at once. + child_entry.PutParentIdPropertyOnly(new_id); + } + } + // Update Id references on the previous and next nodes in the sibling + // order. Do this by reinserting into the linked list; the first + // step in PutPredecessor is to Unlink from the existing order, which + // will overwrite the stale Id value from the adjacent nodes. + if (entry->Get(PREV_ID) == entry->Get(NEXT_ID) && + entry->Get(PREV_ID) == old_id) { + // We just need a shallow update to |entry|'s fields since it is already + // self looped. + entry->Put(NEXT_ID, new_id); + entry->Put(PREV_ID, new_id); + } else { + entry->PutPredecessor(entry->Get(PREV_ID)); + } +} + +// Function to handle runtime failures on syncable code. Rather than crashing, +// if the |condition| is false the following will happen: +// 1. Sets unrecoverable error on transaction. +// 2. Returns false. +bool SyncAssert(bool condition, + const tracked_objects::Location& location, + const char* msg, + BaseTransaction* trans) { + if (!condition) { + trans->OnUnrecoverableError(location, msg); + return false; + } + return true; +} + +} // namespace syncable diff --git a/sync/syncable/syncable_util.h b/sync/syncable/syncable_util.h new file mode 100644 index 0000000..133f5ad --- /dev/null +++ b/sync/syncable/syncable_util.h @@ -0,0 +1,31 @@ +// 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 SYNC_SYNCABLE_SYNCABLE_UTIL_H_ +#define SYNC_SYNCABLE_SYNCABLE_UTIL_H_ + +namespace tracked_objects { +class Location; +} + +namespace syncable { + +class BaseTransaction; +class WriteTransaction; +class MutableEntry; +class Id; + +void ChangeEntryIDAndUpdateChildren(syncable::WriteTransaction* trans, + syncable::MutableEntry* entry, + const syncable::Id& new_id); + +bool IsLegalNewParent(BaseTransaction* trans, const Id& id, const Id& parentid); + +bool SyncAssert(bool condition, + const tracked_objects::Location& location, + const char* msg, + BaseTransaction* trans); +} // namespace syncable + +#endif // SYNC_SYNCABLE_SYNCABLE_UTIL_H_ diff --git a/sync/syncable/transaction_observer.h b/sync/syncable/transaction_observer.h index 28db7a0..f5430ba 100644 --- a/sync/syncable/transaction_observer.h +++ b/sync/syncable/transaction_observer.h @@ -7,7 +7,7 @@ #pragma once #include "sync/internal_api/public/syncable/model_type.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/write_transaction_info.h" namespace syncable { diff --git a/sync/syncable/write_transaction.cc b/sync/syncable/write_transaction.cc new file mode 100644 index 0000000..d624ae6 --- /dev/null +++ b/sync/syncable/write_transaction.cc @@ -0,0 +1,158 @@ +// 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 "sync/syncable/write_transaction.h" + +#include "sync/syncable/directory.h" +#include "sync/syncable/directory_change_delegate.h" +#include "sync/syncable/transaction_observer.h" +#include "sync/syncable/write_transaction_info.h" + +namespace syncable { + +enum InvariantCheckLevel { + OFF = 0, + VERIFY_IN_MEMORY = 1, + FULL_DB_VERIFICATION = 2 +}; + +const InvariantCheckLevel kInvariantCheckLevel = VERIFY_IN_MEMORY; + +WriteTransaction::WriteTransaction(const tracked_objects::Location& location, + WriterTag writer, Directory* directory) + : BaseTransaction(location, "WriteTransaction", writer, directory) { + Lock(); +} + +void WriteTransaction::SaveOriginal(const EntryKernel* entry) { + if (!entry) { + return; + } + // Insert only if it's not already there. + const int64 handle = entry->ref(META_HANDLE); + EntryKernelMutationMap::iterator it = mutations_.lower_bound(handle); + if (it == mutations_.end() || it->first != handle) { + EntryKernelMutation mutation; + mutation.original = *entry; + ignore_result(mutations_.insert(it, std::make_pair(handle, mutation))); + } +} + +ImmutableEntryKernelMutationMap WriteTransaction::RecordMutations() { + directory_->kernel_->transaction_mutex.AssertAcquired(); + for (syncable::EntryKernelMutationMap::iterator it = mutations_.begin(); + it != mutations_.end();) { + EntryKernel* kernel = directory()->GetEntryByHandle(it->first); + if (!kernel) { + NOTREACHED(); + continue; + } + if (kernel->is_dirty()) { + it->second.mutated = *kernel; + ++it; + } else { + DCHECK(!it->second.original.is_dirty()); + // Not actually mutated, so erase from |mutations_|. + mutations_.erase(it++); + } + } + return ImmutableEntryKernelMutationMap(&mutations_); +} + +void WriteTransaction::UnlockAndNotify( + const ImmutableEntryKernelMutationMap& mutations) { + // Work while transaction mutex is held. + ModelTypeSet models_with_changes; + bool has_mutations = !mutations.Get().empty(); + if (has_mutations) { + models_with_changes = NotifyTransactionChangingAndEnding(mutations); + } + Unlock(); + + // Work after mutex is relased. + if (has_mutations) { + NotifyTransactionComplete(models_with_changes); + } +} + +ModelTypeSet WriteTransaction::NotifyTransactionChangingAndEnding( + const ImmutableEntryKernelMutationMap& mutations) { + directory_->kernel_->transaction_mutex.AssertAcquired(); + DCHECK(!mutations.Get().empty()); + + WriteTransactionInfo write_transaction_info( + directory_->kernel_->next_write_transaction_id, + from_here_, writer_, mutations); + ++directory_->kernel_->next_write_transaction_id; + + ImmutableWriteTransactionInfo immutable_write_transaction_info( + &write_transaction_info); + DirectoryChangeDelegate* const delegate = directory_->kernel_->delegate; + if (writer_ == syncable::SYNCAPI) { + delegate->HandleCalculateChangesChangeEventFromSyncApi( + immutable_write_transaction_info, this); + } else { + delegate->HandleCalculateChangesChangeEventFromSyncer( + immutable_write_transaction_info, this); + } + + ModelTypeSet models_with_changes = + delegate->HandleTransactionEndingChangeEvent( + immutable_write_transaction_info, this); + + directory_->kernel_->transaction_observer.Call(FROM_HERE, + &TransactionObserver::OnTransactionWrite, + immutable_write_transaction_info, models_with_changes); + + return models_with_changes; +} + +void WriteTransaction::NotifyTransactionComplete( + ModelTypeSet models_with_changes) { + directory_->kernel_->delegate->HandleTransactionCompleteChangeEvent( + models_with_changes); +} + +WriteTransaction::~WriteTransaction() { + const ImmutableEntryKernelMutationMap& mutations = RecordMutations(); + + if (!unrecoverable_error_set_) { + if (OFF != kInvariantCheckLevel) { + const bool full_scan = (FULL_DB_VERIFICATION == kInvariantCheckLevel); + if (full_scan) + directory()->CheckTreeInvariants(this, full_scan); + else + directory()->CheckTreeInvariants(this, mutations.Get()); + } + } + + // |CheckTreeInvariants| could have thrown an unrecoverable error. + if (unrecoverable_error_set_) { + HandleUnrecoverableErrorIfSet(); + Unlock(); + return; + } + + UnlockAndNotify(mutations); +} + +#define ENUM_CASE(x) case x: return #x; break + +std::string WriterTagToString(WriterTag writer_tag) { + switch (writer_tag) { + ENUM_CASE(INVALID); + ENUM_CASE(SYNCER); + ENUM_CASE(AUTHWATCHER); + ENUM_CASE(UNITTEST); + ENUM_CASE(VACUUM_AFTER_SAVE); + ENUM_CASE(PURGE_ENTRIES); + ENUM_CASE(SYNCAPI); + }; + NOTREACHED(); + return ""; +} + +#undef ENUM_CASE + +} // namespace syncable diff --git a/sync/syncable/write_transaction.h b/sync/syncable/write_transaction.h new file mode 100644 index 0000000..685e447 --- /dev/null +++ b/sync/syncable/write_transaction.h @@ -0,0 +1,48 @@ +// 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 SYNC_SYNCABLE_WRITE_TRANSACTION_H_ +#define SYNC_SYNCABLE_WRITE_TRANSACTION_H_ +#pragma once + +#include "sync/syncable/base_transaction.h" +#include "sync/syncable/entry_kernel.h" + +namespace syncable { + +// Locks db in constructor, unlocks in destructor. +class WriteTransaction : public BaseTransaction { + public: + WriteTransaction(const tracked_objects::Location& from_here, + WriterTag writer, Directory* directory); + + virtual ~WriteTransaction(); + + void SaveOriginal(const EntryKernel* entry); + + protected: + // Overridden by tests. + virtual void NotifyTransactionComplete(ModelTypeSet models_with_changes); + + private: + friend class MutableEntry; + + // Clears |mutations_|. + ImmutableEntryKernelMutationMap RecordMutations(); + + void UnlockAndNotify(const ImmutableEntryKernelMutationMap& mutations); + + ModelTypeSet NotifyTransactionChangingAndEnding( + const ImmutableEntryKernelMutationMap& mutations); + + // Only the original fields are filled in until |RecordMutations()|. + // We use a mutation map instead of a kernel set to avoid copying. + EntryKernelMutationMap mutations_; + + DISALLOW_COPY_AND_ASSIGN(WriteTransaction); +}; + +} // namespace syncable + +#endif // SYNC_SYNCABLE_WRITE_TRANSACTION_H_ diff --git a/sync/syncable/write_transaction_info.cc b/sync/syncable/write_transaction_info.cc new file mode 100644 index 0000000..0d6cef2 --- /dev/null +++ b/sync/syncable/write_transaction_info.cc @@ -0,0 +1,46 @@ +// 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 "sync/syncable/write_transaction_info.h" + +#include "base/string_number_conversions.h" + +namespace syncable { + +WriteTransactionInfo::WriteTransactionInfo( + int64 id, + tracked_objects::Location location, + WriterTag writer, + ImmutableEntryKernelMutationMap mutations) + : id(id), + location_string(location.ToString()), + writer(writer), + mutations(mutations) {} + +WriteTransactionInfo::WriteTransactionInfo() + : id(-1), writer(INVALID) {} + +WriteTransactionInfo::~WriteTransactionInfo() {} + +base::DictionaryValue* WriteTransactionInfo::ToValue( + size_t max_mutations_size) const { + DictionaryValue* dict = new DictionaryValue(); + dict->SetString("id", base::Int64ToString(id)); + dict->SetString("location", location_string); + dict->SetString("writer", WriterTagToString(writer)); + Value* mutations_value = NULL; + const size_t mutations_size = mutations.Get().size(); + if (mutations_size <= max_mutations_size) { + mutations_value = EntryKernelMutationMapToValue(mutations.Get()); + } else { + mutations_value = + Value::CreateStringValue( + base::Uint64ToString(static_cast<uint64>(mutations_size)) + + " mutations"); + } + dict->Set("mutations", mutations_value); + return dict; +} + +} // namespace syncable diff --git a/sync/syncable/write_transaction_info.h b/sync/syncable/write_transaction_info.h new file mode 100644 index 0000000..9441bf5 --- /dev/null +++ b/sync/syncable/write_transaction_info.h @@ -0,0 +1,40 @@ +// 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 SYNC_SYNCABLE_WRITE_TRANSACTION_INFO_H_ +#define SYNC_SYNCABLE_WRITE_TRANSACTION_INFO_H_ +#pragma once + +#include "sync/syncable/base_transaction.h" +#include "sync/syncable/entry_kernel.h" + +namespace syncable { + +// A struct describing the changes made during a transaction. +struct WriteTransactionInfo { + WriteTransactionInfo(int64 id, + tracked_objects::Location location, + WriterTag writer, + ImmutableEntryKernelMutationMap mutations); + WriteTransactionInfo(); + ~WriteTransactionInfo(); + + // Caller owns the return value. + base::DictionaryValue* ToValue(size_t max_mutations_size) const; + + int64 id; + // If tracked_objects::Location becomes assignable, we can use that + // instead. + std::string location_string; + WriterTag writer; + ImmutableEntryKernelMutationMap mutations; +}; + +typedef + browser_sync::Immutable<WriteTransactionInfo> + ImmutableWriteTransactionInfo; + +} // namespace syncable + +#endif // SYNC_SYNCABLE_WRITE_TRANSACTION_INFO_H_ diff --git a/sync/test/engine/mock_connection_manager.cc b/sync/test/engine/mock_connection_manager.cc index 84a07dd..f7ecac0 100644 --- a/sync/test/engine/mock_connection_manager.cc +++ b/sync/test/engine/mock_connection_manager.cc @@ -12,7 +12,8 @@ #include "base/stringprintf.h" #include "sync/engine/syncer_proto_util.h" #include "sync/protocol/bookmark_specifics.pb.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/write_transaction.h" #include "sync/test/engine/test_id_factory.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/sync/test/engine/test_directory_setter_upper.cc b/sync/test/engine/test_directory_setter_upper.cc index 01fef5d..5303bea 100644 --- a/sync/test/engine/test_directory_setter_upper.cc +++ b/sync/test/engine/test_directory_setter_upper.cc @@ -8,7 +8,8 @@ #include "base/file_util.h" #include "base/location.h" #include "base/string_util.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/read_transaction.h" #include "sync/test/null_transaction_observer.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/sync/test/engine/test_syncable_utils.cc b/sync/test/engine/test_syncable_utils.cc index ab07e4c..c899ad3 100644 --- a/sync/test/engine/test_syncable_utils.cc +++ b/sync/test/engine/test_syncable_utils.cc @@ -6,7 +6,9 @@ #include "sync/test/engine/test_syncable_utils.h" -#include "sync/syncable/syncable.h" +#include "sync/syncable/base_transaction.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/entry.h" using std::string; diff --git a/sync/test/engine/test_syncable_utils.h b/sync/test/engine/test_syncable_utils.h index 1095fc0..c10b5e2 100644 --- a/sync/test/engine/test_syncable_utils.h +++ b/sync/test/engine/test_syncable_utils.h @@ -11,8 +11,6 @@ #include <string> -#include "sync/syncable/syncable.h" - namespace syncable { class BaseTransaction; |