1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/sync/sessions/tab_node_pool.h"
7 #include "base/format_macros.h"
8 #include "base/logging.h"
9 #include "base/strings/stringprintf.h"
10 #include "sync/api/sync_change.h"
11 #include "sync/api/sync_data.h"
12 #include "sync/internal_api/public/base/model_type.h"
13 #include "sync/protocol/session_specifics.pb.h"
14 #include "sync/protocol/sync.pb.h"
16 namespace browser_sync
{
18 const size_t TabNodePool::kFreeNodesLowWatermark
= 25;
19 const size_t TabNodePool::kFreeNodesHighWatermark
= 100;
21 TabNodePool::TabNodePool()
22 : max_used_tab_node_id_(kInvalidTabNodeID
) {}
25 // We start vending tab node IDs at 0.
26 const int TabNodePool::kInvalidTabNodeID
= -1;
28 TabNodePool::~TabNodePool() {}
31 std::string
TabNodePool::TabIdToTag(
32 const std::string machine_tag
, int tab_node_id
) {
33 return base::StringPrintf("%s %d", machine_tag
.c_str(), tab_node_id
);
36 void TabNodePool::AddTabNode(int tab_node_id
) {
37 DCHECK_GT(tab_node_id
, kInvalidTabNodeID
);
38 DCHECK(nodeid_tabid_map_
.find(tab_node_id
) == nodeid_tabid_map_
.end());
39 unassociated_nodes_
.insert(tab_node_id
);
40 if (max_used_tab_node_id_
< tab_node_id
)
41 max_used_tab_node_id_
= tab_node_id
;
44 void TabNodePool::AssociateTabNode(int tab_node_id
,
45 SessionID::id_type tab_id
) {
46 DCHECK_GT(tab_node_id
, kInvalidTabNodeID
);
47 // Remove sync node if it is in unassociated nodes pool.
48 std::set
<int>::iterator u_it
= unassociated_nodes_
.find(tab_node_id
);
49 if (u_it
!= unassociated_nodes_
.end()) {
50 unassociated_nodes_
.erase(u_it
);
52 // This is a new node association, the sync node should be free.
53 // Remove node from free node pool and then associate it with the tab.
54 std::set
<int>::iterator it
= free_nodes_pool_
.find(tab_node_id
);
55 DCHECK(it
!= free_nodes_pool_
.end());
56 free_nodes_pool_
.erase(it
);
58 DCHECK(nodeid_tabid_map_
.find(tab_node_id
) == nodeid_tabid_map_
.end());
59 nodeid_tabid_map_
[tab_node_id
] = tab_id
;
62 int TabNodePool::GetFreeTabNode(syncer::SyncChangeList
* append_changes
) {
63 DCHECK_GT(machine_tag_
.length(), 0U);
64 DCHECK(append_changes
);
65 if (free_nodes_pool_
.empty()) {
66 // Tab pool has no free nodes, allocate new one.
67 int tab_node_id
= ++max_used_tab_node_id_
;
68 std::string tab_node_tag
= TabIdToTag(machine_tag_
, tab_node_id
);
70 // We fill the new node with just enough data so that in case of a crash/bug
71 // we can identify the node as our own on re-association and reuse it.
72 sync_pb::EntitySpecifics entity
;
73 sync_pb::SessionSpecifics
* specifics
= entity
.mutable_session();
74 specifics
->set_session_tag(machine_tag_
);
75 specifics
->set_tab_node_id(tab_node_id
);
76 append_changes
->push_back(syncer::SyncChange(
78 syncer::SyncChange::ACTION_ADD
,
79 syncer::SyncData::CreateLocalData(tab_node_tag
,
83 // Grow the pool by 1 since we created a new node.
84 DVLOG(1) << "Adding sync node " << tab_node_id
85 << " to tab node id pool";
86 free_nodes_pool_
.insert(tab_node_id
);
89 // Return the next free node.
90 return *free_nodes_pool_
.begin();
94 void TabNodePool::FreeTabNode(int tab_node_id
,
95 syncer::SyncChangeList
* append_changes
) {
96 DCHECK(append_changes
);
97 TabNodeIDToTabIDMap::iterator it
= nodeid_tabid_map_
.find(tab_node_id
);
98 DCHECK(it
!= nodeid_tabid_map_
.end());
99 nodeid_tabid_map_
.erase(it
);
100 FreeTabNodeInternal(tab_node_id
, append_changes
);
103 void TabNodePool::FreeTabNodeInternal(
105 syncer::SyncChangeList
* append_changes
) {
106 DCHECK(free_nodes_pool_
.find(tab_node_id
) == free_nodes_pool_
.end());
107 DCHECK(append_changes
);
108 free_nodes_pool_
.insert(tab_node_id
);
110 // If number of free nodes exceed kFreeNodesHighWatermark,
111 // delete sync nodes till number reaches kFreeNodesLowWatermark.
112 // Note: This logic is to mitigate temporary disassociation issues with old
113 // clients: http://crbug.com/259918. Newer versions do not need this.
114 if (free_nodes_pool_
.size() > kFreeNodesHighWatermark
) {
115 for (std::set
<int>::iterator free_it
= free_nodes_pool_
.begin();
116 free_it
!= free_nodes_pool_
.end();) {
117 const std::string tab_node_tag
= TabIdToTag(machine_tag_
, *free_it
);
118 append_changes
->push_back(syncer::SyncChange(
120 syncer::SyncChange::ACTION_DELETE
,
121 syncer::SyncData::CreateLocalDelete(tab_node_tag
,
123 free_nodes_pool_
.erase(free_it
++);
124 if (free_nodes_pool_
.size() <= kFreeNodesLowWatermark
) {
131 bool TabNodePool::IsUnassociatedTabNode(int tab_node_id
) {
132 return unassociated_nodes_
.find(tab_node_id
) != unassociated_nodes_
.end();
135 void TabNodePool::ReassociateTabNode(int tab_node_id
,
136 SessionID::id_type tab_id
) {
137 // Remove from list of unassociated sync_nodes if present.
138 std::set
<int>::iterator it
= unassociated_nodes_
.find(tab_node_id
);
139 if (it
!= unassociated_nodes_
.end()) {
140 unassociated_nodes_
.erase(it
);
142 // tab_node_id must be an already associated node.
143 DCHECK(nodeid_tabid_map_
.find(tab_node_id
) != nodeid_tabid_map_
.end());
145 nodeid_tabid_map_
[tab_node_id
] = tab_id
;
148 SessionID::id_type
TabNodePool::GetTabIdFromTabNodeId(
149 int tab_node_id
) const {
150 TabNodeIDToTabIDMap::const_iterator it
= nodeid_tabid_map_
.find(tab_node_id
);
151 if (it
!= nodeid_tabid_map_
.end()) {
154 return kInvalidTabID
;
157 void TabNodePool::DeleteUnassociatedTabNodes(
158 syncer::SyncChangeList
* append_changes
) {
159 for (std::set
<int>::iterator it
= unassociated_nodes_
.begin();
160 it
!= unassociated_nodes_
.end();) {
161 FreeTabNodeInternal(*it
, append_changes
);
162 unassociated_nodes_
.erase(it
++);
164 DCHECK(unassociated_nodes_
.empty());
168 void TabNodePool::Clear() {
169 unassociated_nodes_
.clear();
170 free_nodes_pool_
.clear();
171 nodeid_tabid_map_
.clear();
172 max_used_tab_node_id_
= kInvalidTabNodeID
;
175 size_t TabNodePool::Capacity() const {
176 return nodeid_tabid_map_
.size() + unassociated_nodes_
.size() +
177 free_nodes_pool_
.size();
180 bool TabNodePool::Empty() const { return free_nodes_pool_
.empty(); }
182 bool TabNodePool::Full() { return nodeid_tabid_map_
.empty(); }
184 void TabNodePool::SetMachineTag(const std::string
& machine_tag
) {
185 machine_tag_
= machine_tag
;
188 } // namespace browser_sync