| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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 "base/metrics/field_trial.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/build_time.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/rand_util.h" | |
| 12 #include "base/sha1.h" | |
| 13 #include "base/strings/string_util.h" | |
| 14 #include "base/strings/stringprintf.h" | |
| 15 #include "base/strings/utf_string_conversions.h" | |
| 16 #include "base/sys_byteorder.h" | |
| 17 | |
| 18 namespace base { | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 // Created a time value based on |year|, |month| and |day_of_month| parameters. | |
| 23 Time CreateTimeFromParams(int year, int month, int day_of_month) { | |
| 24 DCHECK_GT(year, 1970); | |
| 25 DCHECK_GT(month, 0); | |
| 26 DCHECK_LT(month, 13); | |
| 27 DCHECK_GT(day_of_month, 0); | |
| 28 DCHECK_LT(day_of_month, 32); | |
| 29 | |
| 30 Time::Exploded exploded; | |
| 31 exploded.year = year; | |
| 32 exploded.month = month; | |
| 33 exploded.day_of_week = 0; // Should be unused. | |
| 34 exploded.day_of_month = day_of_month; | |
| 35 exploded.hour = 0; | |
| 36 exploded.minute = 0; | |
| 37 exploded.second = 0; | |
| 38 exploded.millisecond = 0; | |
| 39 | |
| 40 return Time::FromLocalExploded(exploded); | |
| 41 } | |
| 42 | |
| 43 // Returns the boundary value for comparing against the FieldTrial's added | |
| 44 // groups for a given |divisor| (total probability) and |entropy_value|. | |
| 45 FieldTrial::Probability GetGroupBoundaryValue( | |
| 46 FieldTrial::Probability divisor, | |
| 47 double entropy_value) { | |
| 48 // Add a tiny epsilon value to get consistent results when converting floating | |
| 49 // points to int. Without it, boundary values have inconsistent results, e.g.: | |
| 50 // | |
| 51 // static_cast<FieldTrial::Probability>(100 * 0.56) == 56 | |
| 52 // static_cast<FieldTrial::Probability>(100 * 0.57) == 56 | |
| 53 // static_cast<FieldTrial::Probability>(100 * 0.58) == 57 | |
| 54 // static_cast<FieldTrial::Probability>(100 * 0.59) == 59 | |
| 55 const double kEpsilon = 1e-8; | |
| 56 const FieldTrial::Probability result = | |
| 57 static_cast<FieldTrial::Probability>(divisor * entropy_value + kEpsilon); | |
| 58 // Ensure that adding the epsilon still results in a value < |divisor|. | |
| 59 return std::min(result, divisor - 1); | |
| 60 } | |
| 61 | |
| 62 } // namespace | |
| 63 | |
| 64 // statics | |
| 65 const int FieldTrial::kNotFinalized = -1; | |
| 66 const int FieldTrial::kDefaultGroupNumber = 0; | |
| 67 bool FieldTrial::enable_benchmarking_ = false; | |
| 68 | |
| 69 const char FieldTrialList::kPersistentStringSeparator('/'); | |
| 70 const char FieldTrialList::kActivationMarker('*'); | |
| 71 int FieldTrialList::kNoExpirationYear = 0; | |
| 72 | |
| 73 //------------------------------------------------------------------------------ | |
| 74 // FieldTrial methods and members. | |
| 75 | |
| 76 FieldTrial::EntropyProvider::~EntropyProvider() { | |
| 77 } | |
| 78 | |
| 79 void FieldTrial::Disable() { | |
| 80 DCHECK(!group_reported_); | |
| 81 enable_field_trial_ = false; | |
| 82 | |
| 83 // In case we are disabled after initialization, we need to switch | |
| 84 // the trial to the default group. | |
| 85 if (group_ != kNotFinalized) { | |
| 86 // Only reset when not already the default group, because in case we were | |
| 87 // forced to the default group, the group number may not be | |
| 88 // kDefaultGroupNumber, so we should keep it as is. | |
| 89 if (group_name_ != default_group_name_) | |
| 90 SetGroupChoice(default_group_name_, kDefaultGroupNumber); | |
| 91 } | |
| 92 } | |
| 93 | |
| 94 int FieldTrial::AppendGroup(const std::string& name, | |
| 95 Probability group_probability) { | |
| 96 // When the group choice was previously forced, we only need to return the | |
| 97 // the id of the chosen group, and anything can be returned for the others. | |
| 98 if (forced_) { | |
| 99 DCHECK(!group_name_.empty()); | |
| 100 if (name == group_name_) { | |
| 101 // Note that while |group_| may be equal to |kDefaultGroupNumber| on the | |
| 102 // forced trial, it will not have the same value as the default group | |
| 103 // number returned from the non-forced |FactoryGetFieldTrial()| call, | |
| 104 // which takes care to ensure that this does not happen. | |
| 105 return group_; | |
| 106 } | |
| 107 DCHECK_NE(next_group_number_, group_); | |
| 108 // We still return different numbers each time, in case some caller need | |
| 109 // them to be different. | |
| 110 return next_group_number_++; | |
| 111 } | |
| 112 | |
| 113 DCHECK_LE(group_probability, divisor_); | |
| 114 DCHECK_GE(group_probability, 0); | |
| 115 | |
| 116 if (enable_benchmarking_ || !enable_field_trial_) | |
| 117 group_probability = 0; | |
| 118 | |
| 119 accumulated_group_probability_ += group_probability; | |
| 120 | |
| 121 DCHECK_LE(accumulated_group_probability_, divisor_); | |
| 122 if (group_ == kNotFinalized && accumulated_group_probability_ > random_) { | |
| 123 // This is the group that crossed the random line, so we do the assignment. | |
| 124 SetGroupChoice(name, next_group_number_); | |
| 125 } | |
| 126 return next_group_number_++; | |
| 127 } | |
| 128 | |
| 129 int FieldTrial::group() { | |
| 130 FinalizeGroupChoice(); | |
| 131 if (trial_registered_) | |
| 132 FieldTrialList::NotifyFieldTrialGroupSelection(this); | |
| 133 return group_; | |
| 134 } | |
| 135 | |
| 136 const std::string& FieldTrial::group_name() { | |
| 137 // Call |group()| to ensure group gets assigned and observers are notified. | |
| 138 group(); | |
| 139 DCHECK(!group_name_.empty()); | |
| 140 return group_name_; | |
| 141 } | |
| 142 | |
| 143 void FieldTrial::SetForced() { | |
| 144 // We might have been forced before (e.g., by CreateFieldTrial) and it's | |
| 145 // first come first served, e.g., command line switch has precedence. | |
| 146 if (forced_) | |
| 147 return; | |
| 148 | |
| 149 // And we must finalize the group choice before we mark ourselves as forced. | |
| 150 FinalizeGroupChoice(); | |
| 151 forced_ = true; | |
| 152 } | |
| 153 | |
| 154 // static | |
| 155 void FieldTrial::EnableBenchmarking() { | |
| 156 DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount()); | |
| 157 enable_benchmarking_ = true; | |
| 158 } | |
| 159 | |
| 160 // static | |
| 161 FieldTrial* FieldTrial::CreateSimulatedFieldTrial( | |
| 162 const std::string& trial_name, | |
| 163 Probability total_probability, | |
| 164 const std::string& default_group_name, | |
| 165 double entropy_value) { | |
| 166 return new FieldTrial(trial_name, total_probability, default_group_name, | |
| 167 entropy_value); | |
| 168 } | |
| 169 | |
| 170 FieldTrial::FieldTrial(const std::string& trial_name, | |
| 171 const Probability total_probability, | |
| 172 const std::string& default_group_name, | |
| 173 double entropy_value) | |
| 174 : trial_name_(trial_name), | |
| 175 divisor_(total_probability), | |
| 176 default_group_name_(default_group_name), | |
| 177 random_(GetGroupBoundaryValue(total_probability, entropy_value)), | |
| 178 accumulated_group_probability_(0), | |
| 179 next_group_number_(kDefaultGroupNumber + 1), | |
| 180 group_(kNotFinalized), | |
| 181 enable_field_trial_(true), | |
| 182 forced_(false), | |
| 183 group_reported_(false), | |
| 184 trial_registered_(false) { | |
| 185 DCHECK_GT(total_probability, 0); | |
| 186 DCHECK(!trial_name_.empty()); | |
| 187 DCHECK(!default_group_name_.empty()); | |
| 188 } | |
| 189 | |
| 190 FieldTrial::~FieldTrial() {} | |
| 191 | |
| 192 void FieldTrial::SetTrialRegistered() { | |
| 193 DCHECK_EQ(kNotFinalized, group_); | |
| 194 DCHECK(!trial_registered_); | |
| 195 trial_registered_ = true; | |
| 196 } | |
| 197 | |
| 198 void FieldTrial::SetGroupChoice(const std::string& group_name, int number) { | |
| 199 group_ = number; | |
| 200 if (group_name.empty()) | |
| 201 StringAppendF(&group_name_, "%d", group_); | |
| 202 else | |
| 203 group_name_ = group_name; | |
| 204 DVLOG(1) << "Field trial: " << trial_name_ << " Group choice:" << group_name_; | |
| 205 } | |
| 206 | |
| 207 void FieldTrial::FinalizeGroupChoice() { | |
| 208 if (group_ != kNotFinalized) | |
| 209 return; | |
| 210 accumulated_group_probability_ = divisor_; | |
| 211 // Here it's OK to use |kDefaultGroupNumber| since we can't be forced and not | |
| 212 // finalized. | |
| 213 DCHECK(!forced_); | |
| 214 SetGroupChoice(default_group_name_, kDefaultGroupNumber); | |
| 215 } | |
| 216 | |
| 217 bool FieldTrial::GetActiveGroup(ActiveGroup* active_group) const { | |
| 218 if (!group_reported_ || !enable_field_trial_) | |
| 219 return false; | |
| 220 DCHECK_NE(group_, kNotFinalized); | |
| 221 active_group->trial_name = trial_name_; | |
| 222 active_group->group_name = group_name_; | |
| 223 return true; | |
| 224 } | |
| 225 | |
| 226 bool FieldTrial::GetState(FieldTrialState* field_trial_state) const { | |
| 227 if (!enable_field_trial_) | |
| 228 return false; | |
| 229 field_trial_state->trial_name = trial_name_; | |
| 230 // If the group name is empty (hasn't been finalized yet), use the default | |
| 231 // group name instead. | |
| 232 if (!group_name_.empty()) | |
| 233 field_trial_state->group_name = group_name_; | |
| 234 else | |
| 235 field_trial_state->group_name = default_group_name_; | |
| 236 field_trial_state->activated = group_reported_; | |
| 237 return true; | |
| 238 } | |
| 239 | |
| 240 //------------------------------------------------------------------------------ | |
| 241 // FieldTrialList methods and members. | |
| 242 | |
| 243 // static | |
| 244 FieldTrialList* FieldTrialList::global_ = NULL; | |
| 245 | |
| 246 // static | |
| 247 bool FieldTrialList::used_without_global_ = false; | |
| 248 | |
| 249 FieldTrialList::Observer::~Observer() { | |
| 250 } | |
| 251 | |
| 252 FieldTrialList::FieldTrialList( | |
| 253 const FieldTrial::EntropyProvider* entropy_provider) | |
| 254 : entropy_provider_(entropy_provider), | |
| 255 observer_list_(new ObserverListThreadSafe<FieldTrialList::Observer>( | |
| 256 ObserverListBase<FieldTrialList::Observer>::NOTIFY_EXISTING_ONLY)) { | |
| 257 DCHECK(!global_); | |
| 258 DCHECK(!used_without_global_); | |
| 259 global_ = this; | |
| 260 | |
| 261 Time two_years_from_build_time = GetBuildTime() + TimeDelta::FromDays(730); | |
| 262 Time::Exploded exploded; | |
| 263 two_years_from_build_time.LocalExplode(&exploded); | |
| 264 kNoExpirationYear = exploded.year; | |
| 265 } | |
| 266 | |
| 267 FieldTrialList::~FieldTrialList() { | |
| 268 AutoLock auto_lock(lock_); | |
| 269 while (!registered_.empty()) { | |
| 270 RegistrationMap::iterator it = registered_.begin(); | |
| 271 it->second->Release(); | |
| 272 registered_.erase(it->first); | |
| 273 } | |
| 274 DCHECK_EQ(this, global_); | |
| 275 global_ = NULL; | |
| 276 } | |
| 277 | |
| 278 // static | |
| 279 FieldTrial* FieldTrialList::FactoryGetFieldTrial( | |
| 280 const std::string& trial_name, | |
| 281 FieldTrial::Probability total_probability, | |
| 282 const std::string& default_group_name, | |
| 283 const int year, | |
| 284 const int month, | |
| 285 const int day_of_month, | |
| 286 FieldTrial::RandomizationType randomization_type, | |
| 287 int* default_group_number) { | |
| 288 return FactoryGetFieldTrialWithRandomizationSeed( | |
| 289 trial_name, total_probability, default_group_name, | |
| 290 year, month, day_of_month, randomization_type, 0, default_group_number); | |
| 291 } | |
| 292 | |
| 293 // static | |
| 294 FieldTrial* FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed( | |
| 295 const std::string& trial_name, | |
| 296 FieldTrial::Probability total_probability, | |
| 297 const std::string& default_group_name, | |
| 298 const int year, | |
| 299 const int month, | |
| 300 const int day_of_month, | |
| 301 FieldTrial::RandomizationType randomization_type, | |
| 302 uint32 randomization_seed, | |
| 303 int* default_group_number) { | |
| 304 if (default_group_number) | |
| 305 *default_group_number = FieldTrial::kDefaultGroupNumber; | |
| 306 // Check if the field trial has already been created in some other way. | |
| 307 FieldTrial* existing_trial = Find(trial_name); | |
| 308 if (existing_trial) { | |
| 309 CHECK(existing_trial->forced_); | |
| 310 // If the default group name differs between the existing forced trial | |
| 311 // and this trial, then use a different value for the default group number. | |
| 312 if (default_group_number && | |
| 313 default_group_name != existing_trial->default_group_name()) { | |
| 314 // If the new default group number corresponds to the group that was | |
| 315 // chosen for the forced trial (which has been finalized when it was | |
| 316 // forced), then set the default group number to that. | |
| 317 if (default_group_name == existing_trial->group_name_internal()) { | |
| 318 *default_group_number = existing_trial->group_; | |
| 319 } else { | |
| 320 // Otherwise, use |kNonConflictingGroupNumber| (-2) for the default | |
| 321 // group number, so that it does not conflict with the |AppendGroup()| | |
| 322 // result for the chosen group. | |
| 323 const int kNonConflictingGroupNumber = -2; | |
| 324 COMPILE_ASSERT( | |
| 325 kNonConflictingGroupNumber != FieldTrial::kDefaultGroupNumber, | |
| 326 conflicting_default_group_number); | |
| 327 COMPILE_ASSERT( | |
| 328 kNonConflictingGroupNumber != FieldTrial::kNotFinalized, | |
| 329 conflicting_default_group_number); | |
| 330 *default_group_number = kNonConflictingGroupNumber; | |
| 331 } | |
| 332 } | |
| 333 return existing_trial; | |
| 334 } | |
| 335 | |
| 336 double entropy_value; | |
| 337 if (randomization_type == FieldTrial::ONE_TIME_RANDOMIZED) { | |
| 338 const FieldTrial::EntropyProvider* entropy_provider = | |
| 339 GetEntropyProviderForOneTimeRandomization(); | |
| 340 CHECK(entropy_provider); | |
| 341 entropy_value = entropy_provider->GetEntropyForTrial(trial_name, | |
| 342 randomization_seed); | |
| 343 } else { | |
| 344 DCHECK_EQ(FieldTrial::SESSION_RANDOMIZED, randomization_type); | |
| 345 DCHECK_EQ(0U, randomization_seed); | |
| 346 entropy_value = RandDouble(); | |
| 347 } | |
| 348 | |
| 349 FieldTrial* field_trial = new FieldTrial(trial_name, total_probability, | |
| 350 default_group_name, entropy_value); | |
| 351 if (GetBuildTime() > CreateTimeFromParams(year, month, day_of_month)) | |
| 352 field_trial->Disable(); | |
| 353 FieldTrialList::Register(field_trial); | |
| 354 return field_trial; | |
| 355 } | |
| 356 | |
| 357 // static | |
| 358 FieldTrial* FieldTrialList::Find(const std::string& name) { | |
| 359 if (!global_) | |
| 360 return NULL; | |
| 361 AutoLock auto_lock(global_->lock_); | |
| 362 return global_->PreLockedFind(name); | |
| 363 } | |
| 364 | |
| 365 // static | |
| 366 int FieldTrialList::FindValue(const std::string& name) { | |
| 367 FieldTrial* field_trial = Find(name); | |
| 368 if (field_trial) | |
| 369 return field_trial->group(); | |
| 370 return FieldTrial::kNotFinalized; | |
| 371 } | |
| 372 | |
| 373 // static | |
| 374 std::string FieldTrialList::FindFullName(const std::string& name) { | |
| 375 FieldTrial* field_trial = Find(name); | |
| 376 if (field_trial) | |
| 377 return field_trial->group_name(); | |
| 378 return std::string(); | |
| 379 } | |
| 380 | |
| 381 // static | |
| 382 bool FieldTrialList::TrialExists(const std::string& name) { | |
| 383 return Find(name) != NULL; | |
| 384 } | |
| 385 | |
| 386 // static | |
| 387 void FieldTrialList::StatesToString(std::string* output) { | |
| 388 FieldTrial::ActiveGroups active_groups; | |
| 389 GetActiveFieldTrialGroups(&active_groups); | |
| 390 for (FieldTrial::ActiveGroups::const_iterator it = active_groups.begin(); | |
| 391 it != active_groups.end(); ++it) { | |
| 392 DCHECK_EQ(std::string::npos, | |
| 393 it->trial_name.find(kPersistentStringSeparator)); | |
| 394 DCHECK_EQ(std::string::npos, | |
| 395 it->group_name.find(kPersistentStringSeparator)); | |
| 396 output->append(it->trial_name); | |
| 397 output->append(1, kPersistentStringSeparator); | |
| 398 output->append(it->group_name); | |
| 399 output->append(1, kPersistentStringSeparator); | |
| 400 } | |
| 401 } | |
| 402 | |
| 403 // static | |
| 404 void FieldTrialList::AllStatesToString(std::string* output) { | |
| 405 if (!global_) | |
| 406 return; | |
| 407 AutoLock auto_lock(global_->lock_); | |
| 408 | |
| 409 for (const auto& registered : global_->registered_) { | |
| 410 FieldTrial::FieldTrialState trial; | |
| 411 if (!registered.second->GetState(&trial)) | |
| 412 continue; | |
| 413 DCHECK_EQ(std::string::npos, | |
| 414 trial.trial_name.find(kPersistentStringSeparator)); | |
| 415 DCHECK_EQ(std::string::npos, | |
| 416 trial.group_name.find(kPersistentStringSeparator)); | |
| 417 if (trial.activated) | |
| 418 output->append(1, kActivationMarker); | |
| 419 output->append(trial.trial_name); | |
| 420 output->append(1, kPersistentStringSeparator); | |
| 421 output->append(trial.group_name); | |
| 422 output->append(1, kPersistentStringSeparator); | |
| 423 } | |
| 424 } | |
| 425 | |
| 426 // static | |
| 427 void FieldTrialList::GetActiveFieldTrialGroups( | |
| 428 FieldTrial::ActiveGroups* active_groups) { | |
| 429 DCHECK(active_groups->empty()); | |
| 430 if (!global_) | |
| 431 return; | |
| 432 AutoLock auto_lock(global_->lock_); | |
| 433 | |
| 434 for (RegistrationMap::iterator it = global_->registered_.begin(); | |
| 435 it != global_->registered_.end(); ++it) { | |
| 436 FieldTrial::ActiveGroup active_group; | |
| 437 if (it->second->GetActiveGroup(&active_group)) | |
| 438 active_groups->push_back(active_group); | |
| 439 } | |
| 440 } | |
| 441 | |
| 442 // static | |
| 443 bool FieldTrialList::CreateTrialsFromString( | |
| 444 const std::string& trials_string, | |
| 445 FieldTrialActivationMode mode, | |
| 446 const std::set<std::string>& ignored_trial_names) { | |
| 447 DCHECK(global_); | |
| 448 if (trials_string.empty() || !global_) | |
| 449 return true; | |
| 450 | |
| 451 size_t next_item = 0; | |
| 452 while (next_item < trials_string.length()) { | |
| 453 size_t name_end = trials_string.find(kPersistentStringSeparator, next_item); | |
| 454 if (name_end == trials_string.npos || next_item == name_end) | |
| 455 return false; | |
| 456 size_t group_name_end = trials_string.find(kPersistentStringSeparator, | |
| 457 name_end + 1); | |
| 458 if (name_end + 1 == group_name_end) | |
| 459 return false; | |
| 460 if (group_name_end == trials_string.npos) | |
| 461 group_name_end = trials_string.length(); | |
| 462 | |
| 463 // Verify if the trial should be activated or not. | |
| 464 std::string name; | |
| 465 bool force_activation = false; | |
| 466 if (trials_string[next_item] == kActivationMarker) { | |
| 467 // Name cannot be only the indicator. | |
| 468 if (name_end - next_item == 1) | |
| 469 return false; | |
| 470 next_item++; | |
| 471 force_activation = true; | |
| 472 } | |
| 473 name.append(trials_string, next_item, name_end - next_item); | |
| 474 std::string group_name(trials_string, name_end + 1, | |
| 475 group_name_end - name_end - 1); | |
| 476 next_item = group_name_end + 1; | |
| 477 | |
| 478 if (ignored_trial_names.find(name) != ignored_trial_names.end()) | |
| 479 continue; | |
| 480 | |
| 481 FieldTrial* trial = CreateFieldTrial(name, group_name); | |
| 482 if (!trial) | |
| 483 return false; | |
| 484 if (mode == ACTIVATE_TRIALS || force_activation) { | |
| 485 // Call |group()| to mark the trial as "used" and notify observers, if | |
| 486 // any. This is useful to ensure that field trials created in child | |
| 487 // processes are properly reported in crash reports. | |
| 488 trial->group(); | |
| 489 } | |
| 490 } | |
| 491 return true; | |
| 492 } | |
| 493 | |
| 494 // static | |
| 495 FieldTrial* FieldTrialList::CreateFieldTrial( | |
| 496 const std::string& name, | |
| 497 const std::string& group_name) { | |
| 498 DCHECK(global_); | |
| 499 DCHECK_GE(name.size(), 0u); | |
| 500 DCHECK_GE(group_name.size(), 0u); | |
| 501 if (name.empty() || group_name.empty() || !global_) | |
| 502 return NULL; | |
| 503 | |
| 504 FieldTrial* field_trial = FieldTrialList::Find(name); | |
| 505 if (field_trial) { | |
| 506 // In single process mode, or when we force them from the command line, | |
| 507 // we may have already created the field trial. | |
| 508 if (field_trial->group_name_internal() != group_name) | |
| 509 return NULL; | |
| 510 return field_trial; | |
| 511 } | |
| 512 const int kTotalProbability = 100; | |
| 513 field_trial = new FieldTrial(name, kTotalProbability, group_name, 0); | |
| 514 FieldTrialList::Register(field_trial); | |
| 515 // Force the trial, which will also finalize the group choice. | |
| 516 field_trial->SetForced(); | |
| 517 return field_trial; | |
| 518 } | |
| 519 | |
| 520 // static | |
| 521 void FieldTrialList::AddObserver(Observer* observer) { | |
| 522 if (!global_) | |
| 523 return; | |
| 524 global_->observer_list_->AddObserver(observer); | |
| 525 } | |
| 526 | |
| 527 // static | |
| 528 void FieldTrialList::RemoveObserver(Observer* observer) { | |
| 529 if (!global_) | |
| 530 return; | |
| 531 global_->observer_list_->RemoveObserver(observer); | |
| 532 } | |
| 533 | |
| 534 // static | |
| 535 void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) { | |
| 536 if (!global_) | |
| 537 return; | |
| 538 | |
| 539 { | |
| 540 AutoLock auto_lock(global_->lock_); | |
| 541 if (field_trial->group_reported_) | |
| 542 return; | |
| 543 field_trial->group_reported_ = true; | |
| 544 } | |
| 545 | |
| 546 if (!field_trial->enable_field_trial_) | |
| 547 return; | |
| 548 | |
| 549 global_->observer_list_->Notify( | |
| 550 FROM_HERE, &FieldTrialList::Observer::OnFieldTrialGroupFinalized, | |
| 551 field_trial->trial_name(), field_trial->group_name_internal()); | |
| 552 } | |
| 553 | |
| 554 // static | |
| 555 size_t FieldTrialList::GetFieldTrialCount() { | |
| 556 if (!global_) | |
| 557 return 0; | |
| 558 AutoLock auto_lock(global_->lock_); | |
| 559 return global_->registered_.size(); | |
| 560 } | |
| 561 | |
| 562 // static | |
| 563 const FieldTrial::EntropyProvider* | |
| 564 FieldTrialList::GetEntropyProviderForOneTimeRandomization() { | |
| 565 if (!global_) { | |
| 566 used_without_global_ = true; | |
| 567 return NULL; | |
| 568 } | |
| 569 | |
| 570 return global_->entropy_provider_.get(); | |
| 571 } | |
| 572 | |
| 573 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { | |
| 574 RegistrationMap::iterator it = registered_.find(name); | |
| 575 if (registered_.end() == it) | |
| 576 return NULL; | |
| 577 return it->second; | |
| 578 } | |
| 579 | |
| 580 // static | |
| 581 void FieldTrialList::Register(FieldTrial* trial) { | |
| 582 if (!global_) { | |
| 583 used_without_global_ = true; | |
| 584 return; | |
| 585 } | |
| 586 AutoLock auto_lock(global_->lock_); | |
| 587 DCHECK(!global_->PreLockedFind(trial->trial_name())); | |
| 588 trial->AddRef(); | |
| 589 trial->SetTrialRegistered(); | |
| 590 global_->registered_[trial->trial_name()] = trial; | |
| 591 } | |
| 592 | |
| 593 } // namespace base | |
| OLD | NEW |