Index: base/metrics/field_trial.cc |
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc |
index 911c8e5ee9842759c3c820929e268098e6a6dbd6..b96d15a5d9fb3307d586af6e33c9171cfcffb9ef 100644 |
--- a/base/metrics/field_trial.cc |
+++ b/base/metrics/field_trial.cc |
@@ -309,7 +309,8 @@ FieldTrial::FieldTrial(const std::string& trial_name, |
enable_field_trial_(true), |
forced_(false), |
group_reported_(false), |
- trial_registered_(false) { |
+ trial_registered_(false), |
+ ref_(SharedPersistentMemoryAllocator::kReferenceNull) { |
DCHECK_GT(total_probability, 0); |
DCHECK(!trial_name_.empty()); |
DCHECK(!default_group_name_.empty()); |
@@ -340,6 +341,10 @@ void FieldTrial::FinalizeGroupChoice() { |
// finalized. |
DCHECK(!forced_); |
SetGroupChoice(default_group_name_, kDefaultGroupNumber); |
+ |
+ // Add the field trial to shared memory. |
+ if (kUseSharedMemoryForFieldTrials) |
+ FieldTrialList::AddToAllocator(this); |
} |
bool FieldTrial::GetActiveGroup(ActiveGroup* active_group) const { |
@@ -784,59 +789,99 @@ void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) { |
if (!field_trial->enable_field_trial_) |
return; |
- |
- if (kUseSharedMemoryForFieldTrials) { |
- field_trial->AddToAllocatorWhileLocked( |
- global_->field_trial_allocator_.get()); |
- } |
} |
+ if (kUseSharedMemoryForFieldTrials) |
+ ActivateFieldTrialEntry(field_trial); |
+ |
global_->observer_list_->Notify( |
FROM_HERE, &FieldTrialList::Observer::OnFieldTrialGroupFinalized, |
field_trial->trial_name(), field_trial->group_name_internal()); |
} |
+// static |
+void FieldTrialList::ActivateFieldTrialEntry(FieldTrial* field_trial) { |
+ SharedPersistentMemoryAllocator* allocator = |
+ global_->field_trial_allocator_.get(); |
+ SharedPersistentMemoryAllocator::Reference ref = field_trial->ref_; |
+ if (ref == SharedPersistentMemoryAllocator::kReferenceNull) { |
+ // It's fine to do this even if the allocator hasn't been instantiated |
+ // yet -- it'll just return early. |
+ AddToAllocator(field_trial); |
+ } else { |
+ // It's also okay to do this even though the callee doesn't have a lock -- |
+ // the only thing that happens on a stale read here is a slight performance |
+ // hit from the child re-synchronizing activation state. |
+ FieldTrialEntry* entry = |
+ allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); |
+ entry->activated = true; |
+ } |
+} |
+ |
#if !defined(OS_NACL) |
// static |
void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() { |
- AutoLock auto_lock(global_->lock_); |
- // Create the allocator if not already created and add all existing trials. |
- if (global_->field_trial_allocator_ != nullptr) |
+ if (!global_) |
return; |
+ { |
+ AutoLock auto_lock(global_->lock_); |
+ // Create the allocator if not already created and add all existing trials. |
+ if (global_->field_trial_allocator_ != nullptr) |
+ return; |
- std::unique_ptr<SharedMemory> shm(new SharedMemory()); |
- if (!shm->CreateAndMapAnonymous(kFieldTrialAllocationSize)) |
- TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); |
- |
- // TODO(lawrencewu): call UpdateTrackingHistograms() when all field trials |
- // have been registered (perhaps in the destructor?) |
- global_->field_trial_allocator_.reset(new SharedPersistentMemoryAllocator( |
- std::move(shm), 0, kAllocatorName, false)); |
- global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); |
+ std::unique_ptr<SharedMemory> shm(new SharedMemory()); |
+ if (!shm->CreateAndMapAnonymous(kFieldTrialAllocationSize)) |
+ TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); |
- // Add all existing field trials. |
- for (const auto& registered : global_->registered_) { |
- registered.second->AddToAllocatorWhileLocked( |
- global_->field_trial_allocator_.get()); |
- } |
+ // TODO(lawrencewu): call UpdateTrackingHistograms() when all field trials |
+ // have been registered (perhaps in the destructor?) |
+ global_->field_trial_allocator_.reset(new SharedPersistentMemoryAllocator( |
+ std::move(shm), 0, kAllocatorName, false)); |
+ global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); |
#if defined(OS_WIN) |
- // Set |readonly_allocator_handle_| so we can pass it to be inherited and via |
- // the command line. |
- global_->readonly_allocator_handle_ = |
- CreateReadOnlyHandle(global_->field_trial_allocator_.get()); |
+ // Set |readonly_allocator_handle_| so we can pass it to be inherited and |
+ // via the command line. |
+ global_->readonly_allocator_handle_ = |
+ CreateReadOnlyHandle(global_->field_trial_allocator_.get()); |
#endif |
+ } |
+ |
+ // Add all existing field trials. Needs to be outside the lock since |
+ // AddToAllocator locks as well. |
+ for (const auto& registered : global_->registered_) { |
+ AddToAllocator(registered.second); |
+ } |
} |
#endif |
-void FieldTrial::AddToAllocatorWhileLocked( |
- SharedPersistentMemoryAllocator* allocator) { |
+// static |
+size_t FieldTrialList::GetFieldTrialCount() { |
+ if (!global_) |
+ return 0; |
+ AutoLock auto_lock(global_->lock_); |
+ return global_->registered_.size(); |
+} |
+ |
+// static |
+void FieldTrialList::AddToAllocator(FieldTrial* field_trial) { |
+ if (!global_) |
+ return; |
+ AutoLock auto_lock(global_->lock_); |
+ |
+ SharedPersistentMemoryAllocator* allocator = |
+ global_->field_trial_allocator_.get(); |
+ |
// Don't do anything if the allocator hasn't been instantiated yet. |
if (allocator == nullptr) |
return; |
- State trial_state; |
- if (!GetState(&trial_state)) |
+ // Or if we've already added it. |
+ if (field_trial->ref_ != SharedPersistentMemoryAllocator::kReferenceNull) |
+ return; |
+ |
+ FieldTrial::State trial_state; |
+ if (!field_trial->GetState(&trial_state)) |
return; |
size_t trial_name_size = trial_state.trial_name.size() + 1; |
@@ -869,14 +914,7 @@ void FieldTrial::AddToAllocatorWhileLocked( |
group_name[trial_state.group_name.size()] = '\0'; |
allocator->MakeIterable(ref); |
-} |
- |
-// static |
-size_t FieldTrialList::GetFieldTrialCount() { |
- if (!global_) |
- return 0; |
- AutoLock auto_lock(global_->lock_); |
- return global_->registered_.size(); |
+ field_trial->ref_ = ref; |
} |
// static |