Commit 16483da2 authored by Jeremy Roman's avatar Jeremy Roman Committed by Commit Bot

Replace the origin trials hash map with a static const array.

Instead of producing vectors at runtime, produce spans which view
constexpr data directly, which avoids thread safety issues and
unnecessary runtime memory allocation.

In practice the number of trials which exist is not so large that
a hashtable is likely to significant outperform accessing packed
static data.

The present implementation uses a HashMap which has two potential
issues:
- the initialization is not inside the atomically protected
  critical section, but after it (so it is possible for two threads
  to race to initialize the map)
- it stores WTF::String internally, but WTF::String is not intended
  to be used across threads and it's difficult to prove that the
  HashMap never does any operations which could race on StringImpl
  internals

Change-Id: Ie946c24fe22b17b6a02e2918917595a01b096e17
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2110677Reviewed-by: default avatarIan Clelland <iclelland@chromium.org>
Commit-Queue: Jeremy Roman <jbroman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#752832}
parent e580c0bb
......@@ -47,6 +47,9 @@ class OriginTrialsWriter(make_runtime_features.BaseRuntimeFeatureWriter):
}
self._implied_mappings = self._make_implied_mappings()
self._trial_to_features_map = self._make_trial_to_features_map()
self._max_features_per_trial = max(
len(features)
for features in self._trial_to_features_map.itervalues())
self._set_trial_types()
@property
......@@ -116,6 +119,7 @@ class OriginTrialsWriter(make_runtime_features.BaseRuntimeFeatureWriter):
'origin_trial_features': self._origin_trial_features,
'implied_origin_trial_features': self._implied_mappings,
'trial_to_features_map': self._trial_to_features_map,
'max_features_per_trial': self._max_features_per_trial,
'input_files': self._input_files,
}
......
......@@ -5,49 +5,45 @@
#include "third_party/blink/renderer/core/origin_trials/origin_trials.h"
#include <algorithm>
#include <array>
#include <iterator>
#include "base/stl_util.h"
#include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
namespace blink {
using TrialToFeaturesMap = HashMap<String, Vector<OriginTrialFeature>>;
namespace {
const TrialToFeaturesMap& GetTrialToFeaturesMap() {
// The object needs to be thread-safe because service workers can call this
// function as well.
DEFINE_THREAD_SAFE_STATIC_LOCAL(TrialToFeaturesMap, trial_to_features_map, ());
if (trial_to_features_map.IsEmpty()) {
static constexpr size_t kMaxFeaturesPerTrial = {{max_features_per_trial}};
static constexpr struct {
const char* trial_name;
unsigned feature_count;
std::array<OriginTrialFeature, kMaxFeaturesPerTrial> features;
} kTrialToFeaturesMap[] = {
{% for trial_name, features_list in trial_to_features_map.items() %}
trial_to_features_map.Set("{{trial_name}}",
Vector<OriginTrialFeature>({
{%- for trial_feature in features_list %}
OriginTrialFeature::k{{trial_feature.name}},
{%- endfor %}
}));
{ "{{trial_name}}", {{features_list|length}}, { {%- for trial_feature in features_list %}OriginTrialFeature::k{{trial_feature.name}}, {%- endfor %} } },
{% endfor %}
// For testing
trial_to_features_map.Set("This trial does not exist",
Vector<OriginTrialFeature>({OriginTrialFeature::kNonExisting}));
}
return trial_to_features_map;
}
{ "This trial does not exist", 1, { OriginTrialFeature::kNonExisting } },
};
} // namespace
bool origin_trials::IsTrialValid(const String& trial_name) {
return GetTrialToFeaturesMap().Contains(trial_name);
bool origin_trials::IsTrialValid(const StringView& trial_name) {
return std::any_of(
std::begin(kTrialToFeaturesMap), std::end(kTrialToFeaturesMap),
[&](const auto& entry) { return entry.trial_name == trial_name; });
}
bool origin_trials::IsTrialEnabledForInsecureContext(const String& trial_name) {
{% for feature in origin_trial_features if feature.origin_trial_allows_insecure %}
if (trial_name == "{{feature.origin_trial_feature_name}}") {
return true;
}
bool origin_trials::IsTrialEnabledForInsecureContext(const StringView& trial_name) {
static const char* const kEnabledForInsecureContext[] = {
{% for trial in origin_trial_features|selectattr('origin_trial_allows_insecure')|map(attribute='origin_trial_feature_name')|unique %}
"{{trial}}",
{% endfor %}
return false;
};
return base::Contains(kEnabledForInsecureContext, trial_name);
}
OriginTrialType origin_trials::GetTrialType(OriginTrialFeature feature) {
......@@ -61,15 +57,21 @@ OriginTrialType origin_trials::GetTrialType(OriginTrialFeature feature) {
}
}
const Vector<OriginTrialFeature>& origin_trials::FeaturesForTrial(const String& trial_name) {
DCHECK(IsTrialValid(trial_name));
return GetTrialToFeaturesMap().find(trial_name)->value;
base::span<const OriginTrialFeature> origin_trials::FeaturesForTrial(
const StringView& trial_name) {
auto it = std::find_if(
std::begin(kTrialToFeaturesMap), std::end(kTrialToFeaturesMap),
[&](const auto& entry) { return entry.trial_name == trial_name; });
DCHECK(it != std::end(kTrialToFeaturesMap));
return {it->features.begin(), it->feature_count};
}
Vector<OriginTrialFeature> origin_trials::GetImpliedFeatures(OriginTrialFeature feature) {
base::span<const OriginTrialFeature> origin_trials::GetImpliedFeatures(
OriginTrialFeature feature) {
{% for implied_by_name, implied_list in implied_origin_trial_features.items() %}
if (feature == OriginTrialFeature::k{{implied_by_name}}) {
Vector<OriginTrialFeature> implied_features = {
static constexpr OriginTrialFeature implied_features[] = {
{%- for implied_name in implied_list %}
OriginTrialFeature::k{{implied_name}},
{%- endfor %}
......@@ -77,7 +79,7 @@ OriginTrialFeature::k{{implied_name}},
return implied_features;
}
{% endfor %}
return Vector<OriginTrialFeature>();
return {};
}
bool origin_trials::FeatureEnabledForOS(OriginTrialFeature feature) {
......
......@@ -16,8 +16,7 @@ bool WebOriginTrials::isTrialEnabled(const WebDocument* web_document, const WebS
if (!web_document) return false;
if (!origin_trials::IsTrialValid(trial))
return false;
const Vector<OriginTrialFeature>& features = origin_trials::FeaturesForTrial(trial);
for (OriginTrialFeature feature : features) {
for (OriginTrialFeature feature : origin_trials::FeaturesForTrial(trial)) {
switch (feature) {
{% for feature in features %}
{% if feature.origin_trial_feature_name %}
......
......@@ -7,10 +7,11 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ORIGIN_TRIALS_ORIGIN_TRIALS_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_ORIGIN_TRIALS_ORIGIN_TRIALS_H_
#include "base/containers/span.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
......@@ -22,10 +23,10 @@ enum class OriginTrialType { kDefault = 0, kDeprecation, kIntervention };
namespace origin_trials {
// Return true if there is a feature with the passed |trial_name|.
CORE_EXPORT bool IsTrialValid(const String& trial_name);
CORE_EXPORT bool IsTrialValid(const StringView& trial_name);
// Return true if |trial_name| can be enabled in an insecure context.
CORE_EXPORT bool IsTrialEnabledForInsecureContext(const String& trial_name);
CORE_EXPORT bool IsTrialEnabledForInsecureContext(const StringView& trial_name);
// Returns the trial type of the given |feature|.
CORE_EXPORT OriginTrialType GetTrialType(OriginTrialFeature feature);
......@@ -33,12 +34,13 @@ CORE_EXPORT OriginTrialType GetTrialType(OriginTrialFeature feature);
// Return origin trials features that are enabled by the passed |trial_name|.
// The trial name MUST be valid (call IsTrialValid() before calling this
// function).
CORE_EXPORT const Vector<OriginTrialFeature>& FeaturesForTrial(
const String& trial_name);
CORE_EXPORT base::span<const OriginTrialFeature> FeaturesForTrial(
const StringView& trial_name);
// Return the list of features which will also be enabled if the given
// |feature| is enabled.
Vector<OriginTrialFeature> GetImpliedFeatures(OriginTrialFeature feature);
base::span<const OriginTrialFeature> GetImpliedFeatures(
OriginTrialFeature feature);
bool FeatureEnabledForOS(OriginTrialFeature feature);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment