Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / sync / sessions / nudge_tracker.cc
1 // Copyright (c) 2013 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.
4
5 #include "sync/sessions/nudge_tracker.h"
6
7 #include "base/basictypes.h"
8 #include "sync/internal_api/public/base/invalidation.h"
9 #include "sync/notifier/invalidation_util.h"
10 #include "sync/notifier/object_id_invalidation_map.h"
11 #include "sync/protocol/sync.pb.h"
12
13 namespace syncer {
14 namespace sessions {
15
16 size_t NudgeTracker::kDefaultMaxPayloadsPerType = 10;
17
18 NudgeTracker::NudgeTracker()
19     : updates_source_(sync_pb::GetUpdatesCallerInfo::UNKNOWN),
20       invalidations_enabled_(false),
21       invalidations_out_of_sync_(true) {
22   ModelTypeSet protocol_types = ProtocolTypes();
23   // Default initialize all the type trackers.
24   for (ModelTypeSet::Iterator it = protocol_types.First(); it.Good();
25        it.Inc()) {
26     invalidation::ObjectId id;
27     if (!RealModelTypeToObjectId(it.Get(), &id)) {
28       NOTREACHED();
29     } else {
30       type_trackers_.insert(std::make_pair(it.Get(), DataTypeTracker(id)));
31     }
32   }
33 }
34
35 NudgeTracker::~NudgeTracker() { }
36
37 bool NudgeTracker::IsSyncRequired() const {
38   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
39        it != type_trackers_.end(); ++it) {
40     if (it->second.IsSyncRequired()) {
41       return true;
42     }
43   }
44   return false;
45 }
46
47 bool NudgeTracker::IsGetUpdatesRequired() const {
48   if (invalidations_out_of_sync_)
49     return true;
50
51   if (IsRetryRequired())
52     return true;
53
54   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
55        it != type_trackers_.end(); ++it) {
56     if (it->second.IsGetUpdatesRequired()) {
57       return true;
58     }
59   }
60   return false;
61 }
62
63 bool NudgeTracker::IsRetryRequired() const {
64   if (sync_cycle_start_time_.is_null())
65     return false;
66
67   if (current_retry_time_.is_null())
68     return false;
69
70   return current_retry_time_ < sync_cycle_start_time_;
71 }
72
73 void NudgeTracker::RecordSuccessfulSyncCycle() {
74   updates_source_ = sync_pb::GetUpdatesCallerInfo::UNKNOWN;
75
76   // If a retry was required, we've just serviced it.  Unset the flag.
77   if (IsRetryRequired())
78     current_retry_time_ = base::TimeTicks();
79
80   // A successful cycle while invalidations are enabled puts us back into sync.
81   invalidations_out_of_sync_ = !invalidations_enabled_;
82
83   for (TypeTrackerMap::iterator it = type_trackers_.begin();
84        it != type_trackers_.end(); ++it) {
85     it->second.RecordSuccessfulSyncCycle();
86   }
87 }
88
89 void NudgeTracker::RecordLocalChange(ModelTypeSet types) {
90   // Don't overwrite an NOTIFICATION or DATATYPE_REFRESH source.  The server
91   // makes some assumptions about the source; overriding these sources with
92   // LOCAL could lead to incorrect behaviour.  This is part of the reason why
93   // we're deprecating 'source' in favor of 'origin'.
94   if (updates_source_ != sync_pb::GetUpdatesCallerInfo::NOTIFICATION
95       && updates_source_ != sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH) {
96     updates_source_ = sync_pb::GetUpdatesCallerInfo::LOCAL;
97   }
98
99   for (ModelTypeSet::Iterator type_it = types.First(); type_it.Good();
100        type_it.Inc()) {
101     TypeTrackerMap::iterator tracker_it = type_trackers_.find(type_it.Get());
102     DCHECK(tracker_it != type_trackers_.end());
103     tracker_it->second.RecordLocalChange();
104   }
105 }
106
107 void NudgeTracker::RecordLocalRefreshRequest(ModelTypeSet types) {
108   // Don't overwrite an NOTIFICATION source.  The server makes some assumptions
109   // about the source.  Overriding this source with LOCAL could lead to
110   // incorrect behaviour.  This is part of the reason why we're deprecating
111   // 'source' in favor of 'origin'.
112   if (updates_source_ != sync_pb::GetUpdatesCallerInfo::NOTIFICATION) {
113     updates_source_ = sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH;
114   }
115
116   for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) {
117     TypeTrackerMap::iterator tracker_it = type_trackers_.find(it.Get());
118     DCHECK(tracker_it != type_trackers_.end());
119     tracker_it->second.RecordLocalRefreshRequest();
120   }
121 }
122
123 void NudgeTracker::RecordRemoteInvalidation(
124     const ObjectIdInvalidationMap& invalidation_map) {
125   updates_source_ = sync_pb::GetUpdatesCallerInfo::NOTIFICATION;
126
127   // Be very careful here.  The invalidations acknowledgement system requires a
128   // sort of manual memory management.  We'll leak a small amount of memory if
129   // we fail to acknowledge or drop any of these incoming invalidations.
130
131   ObjectIdSet id_set = invalidation_map.GetObjectIds();
132   for (ObjectIdSet::iterator it = id_set.begin(); it != id_set.end(); ++it) {
133     ModelType type;
134
135     // This should never happen.  If it does, we'll start to leak memory.
136     if (!ObjectIdToRealModelType(*it, &type)) {
137       NOTREACHED()
138           << "Object ID " << ObjectIdToString(*it)
139           << " does not map to valid model type";
140       continue;
141     }
142
143     // Forward the invalidations to the proper recipient.
144     TypeTrackerMap::iterator tracker_it = type_trackers_.find(type);
145     DCHECK(tracker_it != type_trackers_.end());
146     tracker_it->second.RecordRemoteInvalidations(
147         invalidation_map.ForObject(*it));
148   }
149 }
150
151 void NudgeTracker::OnInvalidationsEnabled() {
152   invalidations_enabled_ = true;
153 }
154
155 void NudgeTracker::OnInvalidationsDisabled() {
156   invalidations_enabled_ = false;
157   invalidations_out_of_sync_ = true;
158 }
159
160 void NudgeTracker::SetTypesThrottledUntil(
161     ModelTypeSet types,
162     base::TimeDelta length,
163     base::TimeTicks now) {
164   for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) {
165     TypeTrackerMap::iterator tracker_it = type_trackers_.find(it.Get());
166     tracker_it->second.ThrottleType(length, now);
167   }
168 }
169
170 void NudgeTracker::UpdateTypeThrottlingState(base::TimeTicks now) {
171   for (TypeTrackerMap::iterator it = type_trackers_.begin();
172        it != type_trackers_.end(); ++it) {
173     it->second.UpdateThrottleState(now);
174   }
175 }
176
177 bool NudgeTracker::IsAnyTypeThrottled() const {
178   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
179        it != type_trackers_.end(); ++it) {
180     if (it->second.IsThrottled()) {
181       return true;
182     }
183   }
184   return false;
185 }
186
187 bool NudgeTracker::IsTypeThrottled(ModelType type) const {
188   DCHECK(type_trackers_.find(type) != type_trackers_.end());
189   return type_trackers_.find(type)->second.IsThrottled();
190 }
191
192 base::TimeDelta NudgeTracker::GetTimeUntilNextUnthrottle(
193     base::TimeTicks now) const {
194   DCHECK(IsAnyTypeThrottled()) << "This function requires a pending unthrottle";
195   const base::TimeDelta kMaxTimeDelta =
196       base::TimeDelta::FromInternalValue(kint64max);
197
198   // Return min of GetTimeUntilUnthrottle() values for all IsThrottled() types.
199   base::TimeDelta time_until_next_unthrottle = kMaxTimeDelta;
200   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
201        it != type_trackers_.end(); ++it) {
202     if (it->second.IsThrottled()) {
203       time_until_next_unthrottle =
204           std::min(time_until_next_unthrottle,
205                    it->second.GetTimeUntilUnthrottle(now));
206     }
207   }
208   DCHECK(kMaxTimeDelta != time_until_next_unthrottle);
209
210   return time_until_next_unthrottle;
211 }
212
213 ModelTypeSet NudgeTracker::GetThrottledTypes() const {
214   ModelTypeSet result;
215   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
216        it != type_trackers_.end(); ++it) {
217     if (it->second.IsThrottled()) {
218       result.Put(it->first);
219     }
220   }
221   return result;
222 }
223
224 void NudgeTracker::SetLegacyNotificationHint(
225     ModelType type,
226     sync_pb::DataTypeProgressMarker* progress) const {
227   DCHECK(type_trackers_.find(type) != type_trackers_.end());
228   type_trackers_.find(type)->second.SetLegacyNotificationHint(progress);
229 }
230
231 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource NudgeTracker::updates_source()
232     const {
233   return updates_source_;
234 }
235
236 void NudgeTracker::FillProtoMessage(
237     ModelType type,
238     sync_pb::GetUpdateTriggers* msg) const {
239   DCHECK(type_trackers_.find(type) != type_trackers_.end());
240
241   // Fill what we can from the global data.
242   msg->set_invalidations_out_of_sync(invalidations_out_of_sync_);
243
244   // Delegate the type-specific work to the DataTypeTracker class.
245   type_trackers_.find(type)->second.FillGetUpdatesTriggersMessage(msg);
246 }
247
248 void NudgeTracker::SetSyncCycleStartTime(base::TimeTicks now) {
249   sync_cycle_start_time_ = now;
250
251   // If current_retry_time_ is still set, that means we have an old retry time
252   // left over from a previous cycle.  For example, maybe we tried to perform
253   // this retry, hit a network connection error, and now we're in exponential
254   // backoff.  In that case, we want this sync cycle to include the GU retry
255   // flag so we leave this variable set regardless of whether or not there is an
256   // overwrite pending.
257   if (!current_retry_time_.is_null()) {
258     return;
259   }
260
261   // If do not have a current_retry_time_, but we do have a next_retry_time_ and
262   // it is ready to go, then we set it as the current_retry_time_.  It will stay
263   // there until a GU retry has succeeded.
264   if (!next_retry_time_.is_null() &&
265       next_retry_time_ < sync_cycle_start_time_) {
266     current_retry_time_ = next_retry_time_;
267     next_retry_time_ = base::TimeTicks();
268   }
269 }
270
271 void NudgeTracker::SetHintBufferSize(size_t size) {
272   for (TypeTrackerMap::iterator it = type_trackers_.begin();
273        it != type_trackers_.end(); ++it) {
274     it->second.UpdatePayloadBufferSize(size);
275   }
276 }
277
278 void NudgeTracker::SetNextRetryTime(base::TimeTicks retry_time) {
279   next_retry_time_ = retry_time;
280 }
281
282 }  // namespace sessions
283 }  // namespace syncer