root/sync/notifier/registration_manager.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. state
  2. DoRegister
  3. Disable
  4. UpdateRegisteredIds
  5. MarkRegistrationLost
  6. MarkAllRegistrationsLost
  7. DisableId
  8. CalculateBackoff
  9. GetRegisteredIdsForTest
  10. GetPendingRegistrationsForTest
  11. FirePendingRegistrationsForTest
  12. GetJitter
  13. TryRegisterId
  14. DoRegisterId
  15. UnregisterId
  16. GetRegisteredIds
  17. IsIdRegistered

// 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/notifier/registration_manager.h"

#include <algorithm>
#include <cstddef>
#include <iterator>
#include <string>
#include <utility>

#include "base/rand_util.h"
#include "base/stl_util.h"
#include "google/cacheinvalidation/include/invalidation-client.h"
#include "google/cacheinvalidation/include/types.h"
#include "sync/notifier/invalidation_util.h"

namespace syncer {

RegistrationManager::PendingRegistrationInfo::PendingRegistrationInfo() {}

RegistrationManager::RegistrationStatus::RegistrationStatus(
    const invalidation::ObjectId& id, RegistrationManager* manager)
    : id(id),
      registration_manager(manager),
      enabled(true),
      state(invalidation::InvalidationListener::UNREGISTERED) {
  DCHECK(registration_manager);
}

RegistrationManager::RegistrationStatus::~RegistrationStatus() {}

void RegistrationManager::RegistrationStatus::DoRegister() {
  CHECK(enabled);
  // We might be called explicitly, so stop the timer manually and
  // reset the delay.
  registration_timer.Stop();
  delay = base::TimeDelta();
  registration_manager->DoRegisterId(id);
  DCHECK(!last_registration_request.is_null());
}

void RegistrationManager::RegistrationStatus::Disable() {
  enabled = false;
  state = invalidation::InvalidationListener::UNREGISTERED;
  registration_timer.Stop();
  delay = base::TimeDelta();
}

const int RegistrationManager::kInitialRegistrationDelaySeconds = 5;
const int RegistrationManager::kRegistrationDelayExponent = 2;
const double RegistrationManager::kRegistrationDelayMaxJitter = 0.5;
const int RegistrationManager::kMinRegistrationDelaySeconds = 1;
// 1 hour.
const int RegistrationManager::kMaxRegistrationDelaySeconds = 60 * 60;

RegistrationManager::RegistrationManager(
    invalidation::InvalidationClient* invalidation_client)
    : invalidation_client_(invalidation_client) {
  DCHECK(invalidation_client_);
}

RegistrationManager::~RegistrationManager() {
  DCHECK(CalledOnValidThread());
  STLDeleteValues(&registration_statuses_);
}

ObjectIdSet RegistrationManager::UpdateRegisteredIds(const ObjectIdSet& ids) {
  DCHECK(CalledOnValidThread());

  const ObjectIdSet& old_ids = GetRegisteredIds();
  const ObjectIdSet& to_register = ids;
  ObjectIdSet to_unregister;
  std::set_difference(old_ids.begin(), old_ids.end(),
                      ids.begin(), ids.end(),
                      std::inserter(to_unregister, to_unregister.begin()),
                      ObjectIdLessThan());

  for (ObjectIdSet::const_iterator it = to_unregister.begin();
       it != to_unregister.end(); ++it) {
    UnregisterId(*it);
  }

  for (ObjectIdSet::const_iterator it = to_register.begin();
       it != to_register.end(); ++it) {
    if (!ContainsKey(registration_statuses_, *it)) {
      registration_statuses_.insert(
          std::make_pair(*it, new RegistrationStatus(*it, this)));
    }
    if (!IsIdRegistered(*it)) {
      TryRegisterId(*it, false /* is-retry */);
    }
  }

  return to_unregister;
}

void RegistrationManager::MarkRegistrationLost(
    const invalidation::ObjectId& id) {
  DCHECK(CalledOnValidThread());
  RegistrationStatusMap::const_iterator it = registration_statuses_.find(id);
  if (it == registration_statuses_.end()) {
    DVLOG(1) << "Attempt to mark non-existent registration for "
             << ObjectIdToString(id) << " as lost";
    return;
  }
  if (!it->second->enabled) {
    return;
  }
  it->second->state = invalidation::InvalidationListener::UNREGISTERED;
  bool is_retry = !it->second->last_registration_request.is_null();
  TryRegisterId(id, is_retry);
}

void RegistrationManager::MarkAllRegistrationsLost() {
  DCHECK(CalledOnValidThread());
  for (RegistrationStatusMap::const_iterator it =
           registration_statuses_.begin();
       it != registration_statuses_.end(); ++it) {
    if (IsIdRegistered(it->first)) {
      MarkRegistrationLost(it->first);
    }
  }
}

void RegistrationManager::DisableId(const invalidation::ObjectId& id) {
  DCHECK(CalledOnValidThread());
  RegistrationStatusMap::const_iterator it = registration_statuses_.find(id);
  if (it == registration_statuses_.end()) {
    DVLOG(1) << "Attempt to disable non-existent registration for "
             << ObjectIdToString(id);
    return;
  }
  it->second->Disable();
}

// static
double RegistrationManager::CalculateBackoff(
    double retry_interval,
    double initial_retry_interval,
    double min_retry_interval,
    double max_retry_interval,
    double backoff_exponent,
    double jitter,
    double max_jitter) {
  // scaled_jitter lies in [-max_jitter, max_jitter].
  double scaled_jitter = jitter * max_jitter;
  double new_retry_interval =
      (retry_interval == 0.0) ?
      (initial_retry_interval * (1.0 + scaled_jitter)) :
      (retry_interval * (backoff_exponent + scaled_jitter));
  return std::max(min_retry_interval,
                  std::min(max_retry_interval, new_retry_interval));
}

ObjectIdSet RegistrationManager::GetRegisteredIdsForTest() const {
  return GetRegisteredIds();
}

RegistrationManager::PendingRegistrationMap
    RegistrationManager::GetPendingRegistrationsForTest() const {
  DCHECK(CalledOnValidThread());
  PendingRegistrationMap pending_registrations;
  for (RegistrationStatusMap::const_iterator it =
           registration_statuses_.begin();
       it != registration_statuses_.end(); ++it) {
    const invalidation::ObjectId& id = it->first;
    RegistrationStatus* status = it->second;
    if (status->registration_timer.IsRunning()) {
      pending_registrations[id].last_registration_request =
          status->last_registration_request;
      pending_registrations[id].registration_attempt =
          status->last_registration_attempt;
      pending_registrations[id].delay = status->delay;
      pending_registrations[id].actual_delay =
          status->registration_timer.GetCurrentDelay();
    }
  }
  return pending_registrations;
}

void RegistrationManager::FirePendingRegistrationsForTest() {
  DCHECK(CalledOnValidThread());
  for (RegistrationStatusMap::const_iterator it =
           registration_statuses_.begin();
       it != registration_statuses_.end(); ++it) {
    if (it->second->registration_timer.IsRunning()) {
      it->second->DoRegister();
    }
  }
}

double RegistrationManager::GetJitter() {
  // |jitter| lies in [-1.0, 1.0), which is low-biased, but only
  // barely.
  //
  // TODO(akalin): Fix the bias.
  return 2.0 * base::RandDouble() - 1.0;
}

void RegistrationManager::TryRegisterId(const invalidation::ObjectId& id,
                                        bool is_retry) {
  DCHECK(CalledOnValidThread());
  RegistrationStatusMap::const_iterator it = registration_statuses_.find(id);
  if (it == registration_statuses_.end()) {
    NOTREACHED() << "TryRegisterId called on " << ObjectIdToString(id)
                 << " which is not in the registration map";
    return;
  }
  RegistrationStatus* status = it->second;
  if (!status->enabled) {
    // Disabled, so do nothing.
    return;
  }
  status->last_registration_attempt = base::Time::Now();
  if (is_retry) {
    // If we're a retry, we must have tried at least once before.
    DCHECK(!status->last_registration_request.is_null());
    // delay = max(0, (now - last request) + next_delay)
    status->delay =
        (status->last_registration_request -
         status->last_registration_attempt) +
        status->next_delay;
    base::TimeDelta delay =
        (status->delay <= base::TimeDelta()) ?
        base::TimeDelta() : status->delay;
    DVLOG(2) << "Registering "
             << ObjectIdToString(id) << " in "
             << delay.InMilliseconds() << " ms";
    status->registration_timer.Stop();
    status->registration_timer.Start(FROM_HERE,
        delay, status, &RegistrationManager::RegistrationStatus::DoRegister);
    double next_delay_seconds =
        CalculateBackoff(static_cast<double>(status->next_delay.InSeconds()),
                         kInitialRegistrationDelaySeconds,
                         kMinRegistrationDelaySeconds,
                         kMaxRegistrationDelaySeconds,
                         kRegistrationDelayExponent,
                         GetJitter(),
                         kRegistrationDelayMaxJitter);
    status->next_delay =
        base::TimeDelta::FromSeconds(static_cast<int64>(next_delay_seconds));
    DVLOG(2) << "New next delay for "
             << ObjectIdToString(id) << " is "
             << status->next_delay.InSeconds() << " seconds";
  } else {
    DVLOG(2) << "Not a retry -- registering "
             << ObjectIdToString(id) << " immediately";
    status->delay = base::TimeDelta();
    status->next_delay = base::TimeDelta();
    status->DoRegister();
  }
}

void RegistrationManager::DoRegisterId(const invalidation::ObjectId& id) {
  DCHECK(CalledOnValidThread());
  invalidation_client_->Register(id);
  RegistrationStatusMap::const_iterator it = registration_statuses_.find(id);
  if (it == registration_statuses_.end()) {
    NOTREACHED() << "DoRegisterId called on " << ObjectIdToString(id)
                 << " which is not in the registration map";
    return;
  }
  it->second->state = invalidation::InvalidationListener::REGISTERED;
  it->second->last_registration_request = base::Time::Now();
}

void RegistrationManager::UnregisterId(const invalidation::ObjectId& id) {
  DCHECK(CalledOnValidThread());
  invalidation_client_->Unregister(id);
  RegistrationStatusMap::iterator it = registration_statuses_.find(id);
  if (it == registration_statuses_.end()) {
    NOTREACHED() << "UnregisterId called on " << ObjectIdToString(id)
                 << " which is not in the registration map";
    return;
  }
  delete it->second;
  registration_statuses_.erase(it);
}


ObjectIdSet RegistrationManager::GetRegisteredIds() const {
  DCHECK(CalledOnValidThread());
  ObjectIdSet ids;
  for (RegistrationStatusMap::const_iterator it =
           registration_statuses_.begin();
       it != registration_statuses_.end(); ++it) {
    if (IsIdRegistered(it->first)) {
      ids.insert(it->first);
    }
  }
  return ids;
}

bool RegistrationManager::IsIdRegistered(
    const invalidation::ObjectId& id) const {
  DCHECK(CalledOnValidThread());
  RegistrationStatusMap::const_iterator it =
      registration_statuses_.find(id);
  return it != registration_statuses_.end() &&
      it->second->state == invalidation::InvalidationListener::REGISTERED;
}

}  // namespace syncer

/* [<][>][^][v][top][bottom][index][help] */