• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
FieldTrial(const std::string & name,const Probability total_probability)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 
AppendGroup(const std::string & name,Probability group_probability)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
MakeName(const std::string & name_prefix,const std::string & trial_name)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 
FieldTrialList()73 FieldTrialList::FieldTrialList() : application_start_time_(TimeTicks::Now()) {
74   DCHECK(!global_);
75   DCHECK(!register_without_global_);
76   global_ = this;
77 }
78 
~FieldTrialList()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
Register(FieldTrial * trial)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
FindValue(const std::string & name)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
FindFullName(const std::string & name)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
Find(const std::string & name)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 
PreLockedFind(const std::string & name)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
StatesToString(std::string * output)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
StringAugmentsState(const std::string & prior_state)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