| // Copyright (c) 2013 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/sessions/nudge_tracker.h" |
| |
| #include "base/basictypes.h" |
| #include "sync/internal_api/public/base/invalidation.h" |
| #include "sync/notifier/invalidation_util.h" |
| #include "sync/notifier/object_id_invalidation_map.h" |
| #include "sync/protocol/sync.pb.h" |
| |
| namespace syncer { |
| namespace sessions { |
| |
| size_t NudgeTracker::kDefaultMaxPayloadsPerType = 10; |
| |
| NudgeTracker::NudgeTracker() |
| : updates_source_(sync_pb::GetUpdatesCallerInfo::UNKNOWN), |
| invalidations_enabled_(false), |
| invalidations_out_of_sync_(true) { |
| ModelTypeSet protocol_types = ProtocolTypes(); |
| // Default initialize all the type trackers. |
| for (ModelTypeSet::Iterator it = protocol_types.First(); it.Good(); |
| it.Inc()) { |
| type_trackers_[it.Get()] = DataTypeTracker(); |
| } |
| } |
| |
| NudgeTracker::~NudgeTracker() { } |
| |
| bool NudgeTracker::IsSyncRequired() const { |
| for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); |
| it != type_trackers_.end(); ++it) { |
| if (it->second.IsSyncRequired()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool NudgeTracker::IsGetUpdatesRequired() const { |
| if (invalidations_out_of_sync_) |
| return true; |
| for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); |
| it != type_trackers_.end(); ++it) { |
| if (it->second.IsGetUpdatesRequired()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void NudgeTracker::RecordSuccessfulSyncCycle() { |
| updates_source_ = sync_pb::GetUpdatesCallerInfo::UNKNOWN; |
| |
| // A successful cycle while invalidations are enabled puts us back into sync. |
| invalidations_out_of_sync_ = !invalidations_enabled_; |
| |
| for (TypeTrackerMap::iterator it = type_trackers_.begin(); |
| it != type_trackers_.end(); ++it) { |
| it->second.RecordSuccessfulSyncCycle(); |
| } |
| } |
| |
| void NudgeTracker::RecordLocalChange(ModelTypeSet types) { |
| // Don't overwrite an NOTIFICATION or DATATYPE_REFRESH source. The server |
| // makes some assumptions about the source; overriding these sources with |
| // LOCAL could lead to incorrect behaviour. This is part of the reason why |
| // we're deprecating 'source' in favor of 'origin'. |
| if (updates_source_ != sync_pb::GetUpdatesCallerInfo::NOTIFICATION |
| && updates_source_ != sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH) { |
| updates_source_ = sync_pb::GetUpdatesCallerInfo::LOCAL; |
| } |
| |
| for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) { |
| DCHECK(type_trackers_.find(it.Get()) != type_trackers_.end()); |
| type_trackers_[it.Get()].RecordLocalChange(); |
| } |
| } |
| |
| void NudgeTracker::RecordLocalRefreshRequest(ModelTypeSet types) { |
| // Don't overwrite an NOTIFICATION source. The server makes some assumptions |
| // about the source. Overriding this source with LOCAL could lead to |
| // incorrect behaviour. This is part of the reason why we're deprecating |
| // 'source' in favor of 'origin'. |
| if (updates_source_ != sync_pb::GetUpdatesCallerInfo::NOTIFICATION) { |
| updates_source_ = sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH; |
| } |
| |
| for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) { |
| DCHECK(type_trackers_.find(it.Get()) != type_trackers_.end()); |
| type_trackers_[it.Get()].RecordLocalRefreshRequest(); |
| } |
| } |
| |
| void NudgeTracker::RecordRemoteInvalidation( |
| const ObjectIdInvalidationMap& invalidation_map) { |
| updates_source_ = sync_pb::GetUpdatesCallerInfo::NOTIFICATION; |
| |
| ObjectIdSet ids = invalidation_map.GetObjectIds(); |
| for (ObjectIdSet::const_iterator it = ids.begin(); it != ids.end(); ++it) { |
| ModelType type; |
| if (!ObjectIdToRealModelType(*it, &type)) { |
| NOTREACHED() |
| << "Object ID " << ObjectIdToString(*it) |
| << " does not map to valid model type"; |
| } |
| DCHECK(type_trackers_.find(type) != type_trackers_.end()); |
| type_trackers_[type].RecordRemoteInvalidations( |
| invalidation_map.ForObject(*it)); |
| } |
| } |
| |
| void NudgeTracker::OnInvalidationsEnabled() { |
| invalidations_enabled_ = true; |
| } |
| |
| void NudgeTracker::OnInvalidationsDisabled() { |
| invalidations_enabled_ = false; |
| invalidations_out_of_sync_ = true; |
| } |
| |
| void NudgeTracker::SetTypesThrottledUntil( |
| ModelTypeSet types, |
| base::TimeDelta length, |
| base::TimeTicks now) { |
| for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) { |
| type_trackers_[it.Get()].ThrottleType(length, now); |
| } |
| } |
| |
| void NudgeTracker::UpdateTypeThrottlingState(base::TimeTicks now) { |
| for (TypeTrackerMap::iterator it = type_trackers_.begin(); |
| it != type_trackers_.end(); ++it) { |
| it->second.UpdateThrottleState(now); |
| } |
| } |
| |
| bool NudgeTracker::IsAnyTypeThrottled() const { |
| for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); |
| it != type_trackers_.end(); ++it) { |
| if (it->second.IsThrottled()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool NudgeTracker::IsTypeThrottled(ModelType type) const { |
| DCHECK(type_trackers_.find(type) != type_trackers_.end()); |
| return type_trackers_.find(type)->second.IsThrottled(); |
| } |
| |
| base::TimeDelta NudgeTracker::GetTimeUntilNextUnthrottle( |
| base::TimeTicks now) const { |
| DCHECK(IsAnyTypeThrottled()) << "This function requires a pending unthrottle"; |
| const base::TimeDelta kMaxTimeDelta = |
| base::TimeDelta::FromInternalValue(kint64max); |
| |
| // Return min of GetTimeUntilUnthrottle() values for all IsThrottled() types. |
| base::TimeDelta time_until_next_unthrottle = kMaxTimeDelta; |
| for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); |
| it != type_trackers_.end(); ++it) { |
| if (it->second.IsThrottled()) { |
| time_until_next_unthrottle = |
| std::min(time_until_next_unthrottle, |
| it->second.GetTimeUntilUnthrottle(now)); |
| } |
| } |
| DCHECK(kMaxTimeDelta != time_until_next_unthrottle); |
| |
| return time_until_next_unthrottle; |
| } |
| |
| ModelTypeSet NudgeTracker::GetThrottledTypes() const { |
| ModelTypeSet result; |
| for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); |
| it != type_trackers_.end(); ++it) { |
| if (it->second.IsThrottled()) { |
| result.Put(it->first); |
| } |
| } |
| return result; |
| } |
| |
| void NudgeTracker::SetLegacyNotificationHint( |
| ModelType type, |
| sync_pb::DataTypeProgressMarker* progress) const { |
| DCHECK(type_trackers_.find(type) != type_trackers_.end()); |
| type_trackers_.find(type)->second.SetLegacyNotificationHint(progress); |
| } |
| |
| sync_pb::GetUpdatesCallerInfo::GetUpdatesSource NudgeTracker::updates_source() |
| const { |
| return updates_source_; |
| } |
| |
| void NudgeTracker::FillProtoMessage( |
| ModelType type, |
| sync_pb::GetUpdateTriggers* msg) const { |
| DCHECK(type_trackers_.find(type) != type_trackers_.end()); |
| |
| // Fill what we can from the global data. |
| msg->set_invalidations_out_of_sync(invalidations_out_of_sync_); |
| |
| // Delegate the type-specific work to the DataTypeTracker class. |
| type_trackers_.find(type)->second.FillGetUpdatesTriggersMessage(msg); |
| } |
| |
| void NudgeTracker::SetHintBufferSize(size_t size) { |
| for (TypeTrackerMap::iterator it = type_trackers_.begin(); |
| it != type_trackers_.end(); ++it) { |
| it->second.UpdatePayloadBufferSize(size); |
| } |
| } |
| |
| } // namespace sessions |
| } // namespace syncer |