OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // FieldTrial is a class for handling details of statistical experiments | 5 // FieldTrial is a class for handling details of statistical experiments |
6 // performed by actual users in the field (i.e., in a shipped or beta product). | 6 // performed by actual users in the field (i.e., in a shipped or beta product). |
7 // All code is called exclusively on the UI thread currently. | 7 // All code is called exclusively on the UI thread currently. |
8 // | 8 // |
9 // The simplest example is an experiment to see whether one of two options | 9 // The simplest example is an experiment to see whether one of two options |
10 // produces "better" results across our user population. In that scenario, UMA | 10 // produces "better" results across our user population. In that scenario, UMA |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
88 #include "base/time.h" | 88 #include "base/time.h" |
89 | 89 |
90 namespace base { | 90 namespace base { |
91 | 91 |
92 class FieldTrialList; | 92 class FieldTrialList; |
93 | 93 |
94 class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> { | 94 class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> { |
95 public: | 95 public: |
96 typedef int Probability; // Probability type for being selected in a trial. | 96 typedef int Probability; // Probability type for being selected in a trial. |
97 | 97 |
| 98 // EntropyProvider is an interface for providing entropy for one-time |
| 99 // randomized (persistent) field trials. |
| 100 class BASE_EXPORT EntropyProvider { |
| 101 public: |
| 102 virtual ~EntropyProvider(); |
| 103 |
| 104 // Returns a double in the range of [0, 1) based on |trial_name| that will |
| 105 // be used for the dice roll for the specified field trial. A given instance |
| 106 // should always return the same value given the same input |trial_name|. |
| 107 virtual double GetEntropyForTrial(const std::string& trial_name) const = 0; |
| 108 }; |
| 109 |
98 // A pair representing a Field Trial and its selected group. | 110 // A pair representing a Field Trial and its selected group. |
99 struct SelectedGroup { | 111 struct SelectedGroup { |
100 std::string trial; | 112 std::string trial; |
101 std::string group; | 113 std::string group; |
102 }; | 114 }; |
103 | 115 |
104 typedef std::vector<SelectedGroup> SelectedGroups; | 116 typedef std::vector<SelectedGroup> SelectedGroups; |
105 | 117 |
106 // A return value to indicate that a given instance has not yet had a group | 118 // A return value to indicate that a given instance has not yet had a group |
107 // assignment (and hence is not yet participating in the trial). | 119 // assignment (and hence is not yet participating in the trial). |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
196 | 208 |
197 // Return the default group name of the FieldTrial. | 209 // Return the default group name of the FieldTrial. |
198 std::string default_group_name() const { return default_group_name_; } | 210 std::string default_group_name() const { return default_group_name_; } |
199 | 211 |
200 // Sets the group_name as well as group_name_hash to make sure they are sync. | 212 // Sets the group_name as well as group_name_hash to make sure they are sync. |
201 void SetGroupChoice(const std::string& name, int number); | 213 void SetGroupChoice(const std::string& name, int number); |
202 | 214 |
203 // Returns the group_name. A winner need not have been chosen. | 215 // Returns the group_name. A winner need not have been chosen. |
204 std::string group_name_internal() const { return group_name_; } | 216 std::string group_name_internal() const { return group_name_; } |
205 | 217 |
206 // Calculates a uniformly-distributed double between [0.0, 1.0) given | |
207 // a |client_id| and a |trial_name| (the latter is used as salt to avoid | |
208 // separate one-time randomized trials from all having the same results). | |
209 static double HashClientId(const std::string& client_id, | |
210 const std::string& trial_name); | |
211 | |
212 // The name of the field trial, as can be found via the FieldTrialList. | 218 // The name of the field trial, as can be found via the FieldTrialList. |
213 const std::string name_; | 219 const std::string name_; |
214 | 220 |
215 // The maximum sum of all probabilities supplied, which corresponds to 100%. | 221 // The maximum sum of all probabilities supplied, which corresponds to 100%. |
216 // This is the scaling factor used to adjust supplied probabilities. | 222 // This is the scaling factor used to adjust supplied probabilities. |
217 const Probability divisor_; | 223 const Probability divisor_; |
218 | 224 |
219 // The name of the default group. | 225 // The name of the default group. |
220 const std::string default_group_name_; | 226 const std::string default_group_name_; |
221 | 227 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
267 static int kExpirationYearInFuture; | 273 static int kExpirationYearInFuture; |
268 | 274 |
269 // Observer is notified when a FieldTrial's group is selected. | 275 // Observer is notified when a FieldTrial's group is selected. |
270 class BASE_EXPORT Observer { | 276 class BASE_EXPORT Observer { |
271 public: | 277 public: |
272 // Notify observers when FieldTrials's group is selected. | 278 // Notify observers when FieldTrials's group is selected. |
273 virtual void OnFieldTrialGroupFinalized(const std::string& trial_name, | 279 virtual void OnFieldTrialGroupFinalized(const std::string& trial_name, |
274 const std::string& group_name) = 0; | 280 const std::string& group_name) = 0; |
275 | 281 |
276 protected: | 282 protected: |
277 virtual ~Observer() {} | 283 virtual ~Observer(); |
278 }; | 284 }; |
279 | 285 |
280 // This singleton holds the global list of registered FieldTrials. | 286 // This singleton holds the global list of registered FieldTrials. |
281 // | 287 // |
282 // |client_id| should be an opaque, diverse ID for this client that does not | 288 // To support one-time randomized field trials, specify a non-NULL |
283 // change between sessions, to enable one-time randomized trials. The empty | 289 // |entropy_provider| which should be a source of uniformly distributed |
284 // string may be provided, in which case one-time randomized trials will | 290 // entropy values. Takes ownership of |entropy_provider|. If one time |
285 // not be available. | 291 // randomization is not desired, pass in NULL for |entropy_provider|. |
286 explicit FieldTrialList(const std::string& client_id); | 292 explicit FieldTrialList(const FieldTrial::EntropyProvider* entropy_provider); |
| 293 |
287 // Destructor Release()'s references to all registered FieldTrial instances. | 294 // Destructor Release()'s references to all registered FieldTrial instances. |
288 ~FieldTrialList(); | 295 ~FieldTrialList(); |
289 | 296 |
290 // Get a FieldTrial instance from the factory. | 297 // Get a FieldTrial instance from the factory. |
291 // | 298 // |
292 // |name| is used to register the instance with the FieldTrialList class, | 299 // |name| is used to register the instance with the FieldTrialList class, |
293 // and can be used to find the trial (only one trial can be present for each | 300 // and can be used to find the trial (only one trial can be present for each |
294 // name). |default_group_name| is the name of the default group which will | 301 // name). |default_group_name| is the name of the default group which will |
295 // be chosen if none of the subsequent appended groups get to be chosen. | 302 // be chosen if none of the subsequent appended groups get to be chosen. |
296 // |default_group_number| can receive the group number of the default group as | 303 // |default_group_number| can receive the group number of the default group as |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
382 static TimeTicks application_start_time() { | 389 static TimeTicks application_start_time() { |
383 if (global_) | 390 if (global_) |
384 return global_->application_start_time_; | 391 return global_->application_start_time_; |
385 // For testing purposes only, or when we don't yet have a start time. | 392 // For testing purposes only, or when we don't yet have a start time. |
386 return TimeTicks::Now(); | 393 return TimeTicks::Now(); |
387 } | 394 } |
388 | 395 |
389 // Return the number of active field trials. | 396 // Return the number of active field trials. |
390 static size_t GetFieldTrialCount(); | 397 static size_t GetFieldTrialCount(); |
391 | 398 |
| 399 // If one-time randomization is enabled, returns a weak pointer to the |
| 400 // corresponding EntropyProvider. Otherwise, returns NULL. |
| 401 static const FieldTrial::EntropyProvider* |
| 402 GetEntropyProviderForOneTimeRandomization(); |
| 403 |
392 // Returns true if you can call |FieldTrial::UseOneTimeRandomization()| | 404 // Returns true if you can call |FieldTrial::UseOneTimeRandomization()| |
393 // without error, i.e. if a non-empty string was provided as the client_id | 405 // without error, i.e. if a non-NULL entropy provider was specified when |
394 // when constructing the FieldTrialList singleton. | 406 // constructing the FieldTrialList singleton. |
395 static bool IsOneTimeRandomizationEnabled(); | 407 static bool IsOneTimeRandomizationEnabled(); |
396 | 408 |
397 // Returns an opaque, diverse ID for this client that does not change | |
398 // between sessions. | |
399 // | |
400 // Returns the empty string if one-time randomization is not enabled. | |
401 static const std::string& client_id(); | |
402 | |
403 private: | 409 private: |
404 // A map from FieldTrial names to the actual instances. | 410 // A map from FieldTrial names to the actual instances. |
405 typedef std::map<std::string, FieldTrial*> RegistrationList; | 411 typedef std::map<std::string, FieldTrial*> RegistrationList; |
406 | 412 |
407 // Helper function should be called only while holding lock_. | 413 // Helper function should be called only while holding lock_. |
408 FieldTrial* PreLockedFind(const std::string& name); | 414 FieldTrial* PreLockedFind(const std::string& name); |
409 | 415 |
410 // Register() stores a pointer to the given trial in a global map. | 416 // Register() stores a pointer to the given trial in a global map. |
411 // This method also AddRef's the indicated trial. | 417 // This method also AddRef's the indicated trial. |
412 // This should always be called after creating a new FieldTrial instance. | 418 // This should always be called after creating a new FieldTrial instance. |
413 static void Register(FieldTrial* trial); | 419 static void Register(FieldTrial* trial); |
414 | 420 |
415 static FieldTrialList* global_; // The singleton of this class. | 421 static FieldTrialList* global_; // The singleton of this class. |
416 | 422 |
417 // This will tell us if there is an attempt to register a field | 423 // This will tell us if there is an attempt to register a field |
418 // trial or check if one-time randomization is enabled without | 424 // trial or check if one-time randomization is enabled without |
419 // creating the FieldTrialList. This is not an error, unless a | 425 // creating the FieldTrialList. This is not an error, unless a |
420 // FieldTrialList is created after that. | 426 // FieldTrialList is created after that. |
421 static bool used_without_global_; | 427 static bool used_without_global_; |
422 | 428 |
423 // A helper value made available to users, that shows when the FieldTrialList | 429 // A helper value made available to users, that shows when the FieldTrialList |
424 // was initialized. Note that this is a singleton instance, and hence is a | 430 // was initialized. Note that this is a singleton instance, and hence is a |
425 // good approximation to the start of the process. | 431 // good approximation to the start of the process. |
426 TimeTicks application_start_time_; | 432 TimeTicks application_start_time_; |
427 | 433 |
428 // Lock for access to registered_. | 434 // Lock for access to registered_. |
429 base::Lock lock_; | 435 base::Lock lock_; |
430 RegistrationList registered_; | 436 RegistrationList registered_; |
431 | 437 |
432 // An opaque, diverse ID for this client that does not change | 438 // Entropy provider to be used for one-time randomized field trials. If NULL, |
433 // between sessions, or the empty string if not initialized. | 439 // one-time randomization is not supported. |
434 std::string client_id_; | 440 scoped_ptr<const FieldTrial::EntropyProvider> entropy_provider_; |
435 | 441 |
436 // List of observers to be notified when a group is selected for a FieldTrial. | 442 // List of observers to be notified when a group is selected for a FieldTrial. |
437 scoped_refptr<ObserverListThreadSafe<Observer> > observer_list_; | 443 scoped_refptr<ObserverListThreadSafe<Observer> > observer_list_; |
438 | 444 |
439 DISALLOW_COPY_AND_ASSIGN(FieldTrialList); | 445 DISALLOW_COPY_AND_ASSIGN(FieldTrialList); |
440 }; | 446 }; |
441 | 447 |
442 } // namespace base | 448 } // namespace base |
443 | 449 |
444 #endif // BASE_METRICS_FIELD_TRIAL_H_ | 450 #endif // BASE_METRICS_FIELD_TRIAL_H_ |
OLD | NEW |