Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2006-2009 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 
      6 #include "base/field_trial.h"
      7 #include "base/logging.h"
      8 #include "base/rand_util.h"
      9 #include "base/string_util.h"
     10 
     11 using base::TimeTicks;
     12 
     13 // static
     14 const int FieldTrial::kNotParticipating = -1;
     15 
     16 // static
     17 const int FieldTrial::kAllRemainingProbability = -2;
     18 
     19 // static
     20 const char FieldTrialList::kPersistentStringSeparator('/');
     21 
     22 //------------------------------------------------------------------------------
     23 // FieldTrial methods and members.
     24 
     25 FieldTrial::FieldTrial(const std::string& name,
     26                        const Probability total_probability)
     27   : name_(name),
     28     divisor_(total_probability),
     29     random_(static_cast<Probability>(divisor_ * base::RandDouble())),
     30     accumulated_group_probability_(0),
     31     next_group_number_(0),
     32     group_(kNotParticipating) {
     33   FieldTrialList::Register(this);
     34 }
     35 
     36 int FieldTrial::AppendGroup(const std::string& name,
     37                             Probability group_probability) {
     38   DCHECK(group_probability <= divisor_);
     39   DCHECK(group_probability >=0 ||
     40          group_probability == kAllRemainingProbability);
     41   if (group_probability == kAllRemainingProbability)
     42     accumulated_group_probability_ = divisor_;
     43   else
     44     accumulated_group_probability_ += group_probability;
     45   DCHECK(accumulated_group_probability_ <= divisor_);
     46   if (group_ == kNotParticipating && accumulated_group_probability_ > random_) {
     47     // This is the group that crossed the random line, so we do the assignment.
     48     group_ = next_group_number_;
     49     if (name.empty())
     50       StringAppendF(&group_name_, "_%d", group_);
     51     else
     52       group_name_ = name;
     53   }
     54   return next_group_number_++;
     55 }
     56 
     57 // static
     58 std::string FieldTrial::MakeName(const std::string& name_prefix,
     59                                  const std::string& trial_name) {
     60   std::string big_string(name_prefix);
     61   return big_string.append(FieldTrialList::FindFullName(trial_name));
     62 }
     63 
     64 //------------------------------------------------------------------------------
     65 // FieldTrialList methods and members.
     66 
     67 // static
     68 FieldTrialList* FieldTrialList::global_ = NULL;
     69 
     70 // static
     71 bool FieldTrialList::register_without_global_ = false;
     72 
     73 FieldTrialList::FieldTrialList() : application_start_time_(TimeTicks::Now()) {
     74   DCHECK(!global_);
     75   DCHECK(!register_without_global_);
     76   global_ = this;
     77 }
     78 
     79 FieldTrialList::~FieldTrialList() {
     80   AutoLock auto_lock(lock_);
     81   while (!registered_.empty()) {
     82     RegistrationList::iterator it = registered_.begin();
     83     it->second->Release();
     84     registered_.erase(it->first);
     85   }
     86   DCHECK(this == global_);
     87   global_ = NULL;
     88 }
     89 
     90 // static
     91 void FieldTrialList::Register(FieldTrial* trial) {
     92   if (!global_) {
     93     register_without_global_ = true;
     94     return;
     95   }
     96   AutoLock auto_lock(global_->lock_);
     97   DCHECK(!global_->PreLockedFind(trial->name()));
     98   trial->AddRef();
     99   global_->registered_[trial->name()] = trial;
    100 }
    101 
    102 // static
    103 int FieldTrialList::FindValue(const std::string& name) {
    104   FieldTrial* field_trial = Find(name);
    105   if (field_trial)
    106     return field_trial->group();
    107   return FieldTrial::kNotParticipating;
    108 }
    109 
    110 // static
    111 std::string FieldTrialList::FindFullName(const std::string& name) {
    112   FieldTrial* field_trial = Find(name);
    113   if (field_trial)
    114     return field_trial->group_name();
    115   return "";
    116 }
    117 
    118 // static
    119 FieldTrial* FieldTrialList::Find(const std::string& name) {
    120   if (!global_)
    121     return NULL;
    122   AutoLock auto_lock(global_->lock_);
    123   return global_->PreLockedFind(name);
    124 }
    125 
    126 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) {
    127   RegistrationList::iterator it = registered_.find(name);
    128   if (registered_.end() == it)
    129     return NULL;
    130   return it->second;
    131 }
    132 
    133 // static
    134 void FieldTrialList::StatesToString(std::string* output) {
    135   if (!global_)
    136     return;
    137   DCHECK(output->empty());
    138   for (RegistrationList::iterator it = global_->registered_.begin();
    139        it != global_->registered_.end(); ++it) {
    140     const std::string name = it->first;
    141     const std::string group_name = it->second->group_name();
    142     if (group_name.empty())
    143       continue;  // No definitive winner in this trial.
    144     DCHECK_EQ(name.find(kPersistentStringSeparator), std::string::npos);
    145     DCHECK_EQ(group_name.find(kPersistentStringSeparator), std::string::npos);
    146     output->append(name);
    147     output->append(1, kPersistentStringSeparator);
    148     output->append(group_name);
    149     output->append(1, kPersistentStringSeparator);
    150   }
    151 }
    152 
    153 // static
    154 bool FieldTrialList::StringAugmentsState(const std::string& prior_state) {
    155   DCHECK(global_);
    156   if (prior_state.empty() || !global_)
    157     return true;
    158 
    159   size_t next_item = 0;
    160   while (next_item < prior_state.length()) {
    161     size_t name_end = prior_state.find(kPersistentStringSeparator, next_item);
    162     if (name_end == prior_state.npos || next_item == name_end)
    163       return false;
    164     size_t group_name_end = prior_state.find(kPersistentStringSeparator,
    165                                              name_end + 1);
    166     if (group_name_end == prior_state.npos || name_end + 1 == group_name_end)
    167       return false;
    168     std::string name(prior_state, next_item, name_end - next_item);
    169     std::string group_name(prior_state, name_end + 1,
    170                            group_name_end - name_end - 1);
    171     next_item = group_name_end + 1;
    172 
    173     FieldTrial *field_trial(FieldTrialList::Find(name));
    174     if (field_trial) {
    175       // In single process mode, we may have already created the field trial.
    176       if (field_trial->group_name() != group_name)
    177         return false;
    178       continue;
    179     }
    180     const int kTotalProbability = 100;
    181     field_trial = new FieldTrial(name, kTotalProbability);
    182     field_trial->AppendGroup(group_name, kTotalProbability);
    183   }
    184   return true;
    185 }
    186 
    187