Import Cobalt 4.13403
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsAnonymousIndexedGetterInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsAnonymousIndexedGetterInterface.cc index 9a486e9..ff82636 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsAnonymousIndexedGetterInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsAnonymousIndexedGetterInterface.cc
@@ -86,6 +86,7 @@ bool IsSupportedIndexProperty(JSContext* context, JS::HandleObject object, uint32_t index) { + TRACE_EVENT0("cobalt::bindings", "IsSupportedIndexProperty"); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromObject(context, object); AnonymousIndexedGetterInterface* impl = @@ -108,6 +109,7 @@ JSBool GetIndexedProperty( JSContext* context, JS::HandleObject object, JS::HandleId id, JS::MutableHandleValue vp) { + TRACE_EVENT0("cobalt::bindings", "GetIndexedProperty"); JS::RootedValue id_value(context); if (!JS_IdToValue(context, id, id_value.address())) { NOTREACHED();
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsAnonymousNamedGetterInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsAnonymousNamedGetterInterface.cc index 5431665..a896d35 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsAnonymousNamedGetterInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsAnonymousNamedGetterInterface.cc
@@ -86,6 +86,7 @@ bool IsSupportedNamedProperty(JSContext* context, JS::HandleObject object, const std::string& property_name) { + TRACE_EVENT0("cobalt::bindings", "IsSupportedNamedProperty"); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromObject(context, object); AnonymousNamedGetterInterface* impl =
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsAnonymousNamedIndexedGetterInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsAnonymousNamedIndexedGetterInterface.cc index 0d132c4..8f883ff 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsAnonymousNamedIndexedGetterInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsAnonymousNamedIndexedGetterInterface.cc
@@ -86,6 +86,7 @@ bool IsSupportedNamedProperty(JSContext* context, JS::HandleObject object, const std::string& property_name) { + TRACE_EVENT0("cobalt::bindings", "IsSupportedNamedProperty"); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromObject(context, object); AnonymousNamedIndexedGetterInterface* impl = @@ -177,6 +178,7 @@ bool IsSupportedIndexProperty(JSContext* context, JS::HandleObject object, uint32_t index) { + TRACE_EVENT0("cobalt::bindings", "IsSupportedIndexProperty"); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromObject(context, object); AnonymousNamedIndexedGetterInterface* impl = @@ -199,6 +201,7 @@ JSBool GetIndexedProperty( JSContext* context, JS::HandleObject object, JS::HandleId id, JS::MutableHandleValue vp) { + TRACE_EVENT0("cobalt::bindings", "GetIndexedProperty"); JS::RootedValue id_value(context); if (!JS_IdToValue(context, id, id_value.address())) { NOTREACHED();
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsDerivedGetterSetterInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsDerivedGetterSetterInterface.cc index 286e8b8..647ef55 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsDerivedGetterSetterInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsDerivedGetterSetterInterface.cc
@@ -86,6 +86,7 @@ bool IsSupportedNamedProperty(JSContext* context, JS::HandleObject object, const std::string& property_name) { + TRACE_EVENT0("cobalt::bindings", "IsSupportedNamedProperty"); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromObject(context, object); DerivedGetterSetterInterface* impl = @@ -177,6 +178,7 @@ bool IsSupportedIndexProperty(JSContext* context, JS::HandleObject object, uint32_t index) { + TRACE_EVENT0("cobalt::bindings", "IsSupportedIndexProperty"); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromObject(context, object); DerivedGetterSetterInterface* impl = @@ -199,6 +201,7 @@ JSBool GetIndexedProperty( JSContext* context, JS::HandleObject object, JS::HandleId id, JS::MutableHandleValue vp) { + TRACE_EVENT0("cobalt::bindings", "GetIndexedProperty"); JS::RootedValue id_value(context); if (!JS_IdToValue(context, id, id_value.address())) { NOTREACHED();
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsIndexedGetterInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsIndexedGetterInterface.cc index 66f6497..b362b42 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsIndexedGetterInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsIndexedGetterInterface.cc
@@ -86,6 +86,7 @@ bool IsSupportedIndexProperty(JSContext* context, JS::HandleObject object, uint32_t index) { + TRACE_EVENT0("cobalt::bindings", "IsSupportedIndexProperty"); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromObject(context, object); IndexedGetterInterface* impl = @@ -108,6 +109,7 @@ JSBool GetIndexedProperty( JSContext* context, JS::HandleObject object, JS::HandleId id, JS::MutableHandleValue vp) { + TRACE_EVENT0("cobalt::bindings", "GetIndexedProperty"); JS::RootedValue id_value(context); if (!JS_IdToValue(context, id, id_value.address())) { NOTREACHED();
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedGetterInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedGetterInterface.cc index 51f1f54..79fb735 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedGetterInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedGetterInterface.cc
@@ -86,6 +86,7 @@ bool IsSupportedNamedProperty(JSContext* context, JS::HandleObject object, const std::string& property_name) { + TRACE_EVENT0("cobalt::bindings", "IsSupportedNamedProperty"); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromObject(context, object); NamedGetterInterface* impl =
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedIndexedGetterInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedIndexedGetterInterface.cc index b1d1234..7f0e6b7 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedIndexedGetterInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedIndexedGetterInterface.cc
@@ -86,6 +86,7 @@ bool IsSupportedNamedProperty(JSContext* context, JS::HandleObject object, const std::string& property_name) { + TRACE_EVENT0("cobalt::bindings", "IsSupportedNamedProperty"); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromObject(context, object); NamedIndexedGetterInterface* impl = @@ -177,6 +178,7 @@ bool IsSupportedIndexProperty(JSContext* context, JS::HandleObject object, uint32_t index) { + TRACE_EVENT0("cobalt::bindings", "IsSupportedIndexProperty"); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromObject(context, object); NamedIndexedGetterInterface* impl = @@ -199,6 +201,7 @@ JSBool GetIndexedProperty( JSContext* context, JS::HandleObject object, JS::HandleId id, JS::MutableHandleValue vp) { + TRACE_EVENT0("cobalt::bindings", "GetIndexedProperty"); JS::RootedValue id_value(context); if (!JS_IdToValue(context, id, id_value.address())) { NOTREACHED();
diff --git a/src/cobalt/bindings/mozjs/templates/interface.cc.template b/src/cobalt/bindings/mozjs/templates/interface.cc.template index 23575c7..2dfc9eb 100644 --- a/src/cobalt/bindings/mozjs/templates/interface.cc.template +++ b/src/cobalt/bindings/mozjs/templates/interface.cc.template
@@ -126,6 +126,7 @@ {% if named_property_getter %} bool IsSupportedNamedProperty(JSContext* context, JS::HandleObject object, const std::string& property_name) { + TRACE_EVENT0("cobalt::bindings", "IsSupportedNamedProperty"); {{ get_impl_class_instance(impl_class) }} return impl->CanQueryNamedProperty(property_name); } @@ -214,6 +215,7 @@ {% if indexed_property_getter %} bool IsSupportedIndexProperty(JSContext* context, JS::HandleObject object, uint32_t index) { + TRACE_EVENT0("cobalt::bindings", "IsSupportedIndexProperty"); {{ get_impl_class_instance(impl_class) }} return index < impl->length(); } @@ -230,6 +232,7 @@ JSBool GetIndexedProperty( JSContext* context, JS::HandleObject object, JS::HandleId id, JS::MutableHandleValue vp) { + TRACE_EVENT0("cobalt::bindings", "GetIndexedProperty"); JS::RootedValue id_value(context); if (!JS_IdToValue(context, id, id_value.address())) { NOTREACHED();
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc index 438692d..9385cc0 100644 --- a/src/cobalt/browser/application.cc +++ b/src/cobalt/browser/application.cc
@@ -60,6 +60,7 @@ namespace { const int kStatUpdatePeriodMs = 1000; +const int kLiteStatUpdatePeriodMs = 16; const char kDefaultURL[] = "https://www.youtube.com/tv"; @@ -292,7 +293,8 @@ : message_loop_(MessageLoop::current()), quit_closure_(quit_closure), start_time_(base::TimeTicks::Now()), - stats_update_timer_(true, true) { + stats_update_timer_(true, true), + lite_stats_update_timer_(true, true) { DCHECK(MessageLoop::current()); DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type()); @@ -307,6 +309,10 @@ stats_update_timer_.Start( FROM_HERE, base::TimeDelta::FromMilliseconds(kStatUpdatePeriodMs), base::Bind(&Application::UpdatePeriodicStats, base::Unretained(this))); + lite_stats_update_timer_.Start( + FROM_HERE, base::TimeDelta::FromMilliseconds(kLiteStatUpdatePeriodMs), + base::Bind(&Application::UpdatePeriodicLiteStats, + base::Unretained(this))); // Check to see if a timed_trace has been set, indicating that we should // begin a timed trace upon startup. @@ -692,6 +698,10 @@ } } +void Application::UpdatePeriodicLiteStats() { + c_val_stats_.app_lifetime = base::TimeTicks::Now() - start_time_; +} + void Application::UpdatePeriodicStats() { #if defined(__LB_SHELL__) bool memory_stats_updated = false; @@ -731,8 +741,6 @@ *c_val_stats_.used_gpu_memory = used_gpu_memory; } #endif - - c_val_stats_.app_lifetime = base::TimeTicks::Now() - start_time_; } } // namespace browser
diff --git a/src/cobalt/browser/application.h b/src/cobalt/browser/application.h index dc4a362..da9f8b1 100644 --- a/src/cobalt/browser/application.h +++ b/src/cobalt/browser/application.h
@@ -154,6 +154,7 @@ void UpdateAndMaybeRegisterUserAgent(); void UpdatePeriodicStats(); + void UpdatePeriodicLiteStats(); static ssize_t available_memory_; static int64 lifetime_in_ms_; @@ -171,6 +172,7 @@ CValStats c_val_stats_; base::Timer stats_update_timer_; + base::Timer lite_stats_update_timer_; }; // Factory method for creating an application. It should be implemented
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc index 58405df..72f1dd8 100644 --- a/src/cobalt/browser/browser_module.cc +++ b/src/cobalt/browser/browser_module.cc
@@ -179,8 +179,6 @@ h5vcc_settings.account_manager = account_manager; h5vcc_settings.event_dispatcher = system_window->event_dispatcher(); h5vcc_settings.initial_deep_link = options.initial_deep_link; - h5vcc_settings.on_set_record_stats = base::Bind( - &BrowserModule::OnSetRecordStats, base::Unretained(this)); web_module_options_.injected_window_attributes["h5vcc"] = base::Bind(&CreateH5VCC, h5vcc_settings); @@ -708,11 +706,5 @@ } #endif // OS_STARBOARD -void BrowserModule::OnSetRecordStats(bool set) { - if (web_module_) { - web_module_->OnSetRecordStats(set); - } -} - } // namespace browser } // namespace cobalt
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h index 677aa32..82322b3 100644 --- a/src/cobalt/browser/browser_module.h +++ b/src/cobalt/browser/browser_module.h
@@ -210,9 +210,6 @@ // Process all messages queued into the |render_tree_submission_queue_|. void ProcessRenderTreeSubmissionQueue(); - // Called when h5vcc.system.record_stats is set - void OnSetRecordStats(bool set); - // TODO: // WeakPtr usage here can be avoided if BrowserModule has a thread to // own where it can ensure that its tasks are all resolved when it is
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc index 29a4b1d..a23cdb6 100644 --- a/src/cobalt/browser/web_module.cc +++ b/src/cobalt/browser/web_module.cc
@@ -144,10 +144,6 @@ void Suspend(); void Resume(render_tree::ResourceProvider* resource_provider); - void OnSetRecordStats(bool set) { - web_module_stat_tracker_->OnSetRecordStats(set); - } - private: class DocumentLoadedObserver; @@ -394,7 +390,7 @@ environment_settings_.reset(new dom::DOMSettings( kDOMMaxElementDepth, fetcher_factory_.get(), data.network_module, window_, - media_source_registry_.get(), javascript_engine_.get(), + media_source_registry_.get(), data.media_module, javascript_engine_.get(), global_environment_.get(), data.options.dom_settings_options)); DCHECK(environment_settings_); @@ -887,12 +883,5 @@ base::Unretained(impl_.get()), resource_provider)); } -void WebModule::OnSetRecordStats(bool set) { - DCHECK(message_loop()); - DCHECK(impl_); - DCHECK_EQ(MessageLoop::current(), message_loop()); - impl_->OnSetRecordStats(set); -} - } // namespace browser } // namespace cobalt
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h index 5b6c8c2..b82f6f7 100644 --- a/src/cobalt/browser/web_module.h +++ b/src/cobalt/browser/web_module.h
@@ -199,9 +199,6 @@ // can only be called if we have previously suspended the WebModule. void Resume(render_tree::ResourceProvider* resource_provider); - // Called when h5vcc.system.recordStats is set. - void OnSetRecordStats(bool set); - #if defined(COBALT_BUILD_TYPE_DEBUG) // Non-optimized builds require a bigger stack size. static const size_t kBaseStackSize = 2 * 1024 * 1024;
diff --git a/src/cobalt/browser/web_module_stat_tracker.cc b/src/cobalt/browser/web_module_stat_tracker.cc index dc94278..aa4d634 100644 --- a/src/cobalt/browser/web_module_stat_tracker.cc +++ b/src/cobalt/browser/web_module_stat_tracker.cc
@@ -20,9 +20,6 @@ #include "cobalt/base/tokens.h" #include "cobalt/dom/event.h" -// The maximum allowed string size of any recorded stat -const std::string::size_type kMaxRecordedStatsBytes = 64 * 1024; - namespace cobalt { namespace browser { @@ -90,17 +87,6 @@ layout_stat_tracker_->FlushPeriodicTracking(); } -void WebModuleStatTracker::OnSetRecordStats(bool set) { - record_stats_ = set; - - // Every time this variable is set, we clear out our stats - for (ScopedVector<EventStats>::iterator it = event_stats_.begin(); - it != event_stats_.end(); - ++it) { - (*it)->event_durations = "[]"; - } -} - WebModuleStatTracker::EventStats::EventStats(const std::string& name) : count_dom_html_elements_created( StringPrintf("Event.Count.%s.DOM.HtmlElement.Created", name.c_str()), @@ -157,11 +143,7 @@ StringPrintf("Event.Duration.%s.Layout.RenderAndAnimate", name.c_str()), base::TimeDelta(), - "RenderAndAnimate duration for event (in microseconds)."), - event_durations(StringPrintf("Event.Durations.%s", name.c_str()), - "[]", - "JSON array of all event durations (in microseconds) " - "since reset.") {} + "RenderAndAnimate duration for event (in microseconds).") {} bool WebModuleStatTracker::IsStopWatchEnabled(int /*id*/) const { return true; } @@ -209,25 +191,9 @@ // misleading as it merely indicates how long the user waited to initiate the // next event. When this occurs, the injection duration provides a much more // accurate picture of how long the event takes. - base::TimeDelta duration_total = was_render_tree_produced + event_stats->duration_total = was_render_tree_produced ? stop_watch_durations_[kStopWatchTypeEvent] : event_injection_duration; - event_stats->duration_total = duration_total; - - if (record_stats_) { - std::string prev_durations = event_stats->event_durations.value(); - if (prev_durations.size() <= 2) { - event_stats->event_durations = - StringPrintf("[%ld]", duration_total.InMicroseconds()); - } else if (prev_durations.size() < kMaxRecordedStatsBytes) { - event_stats->event_durations - = StringPrintf("%s,%ld]", - prev_durations.substr( - 0, prev_durations.size() - 1).c_str(), - duration_total.InMicroseconds()); - } - } - event_stats->duration_dom_inject_event = event_injection_duration; event_stats->duration_dom_update_computed_style = dom_stat_tracker_->GetStopWatchTypeDuration(
diff --git a/src/cobalt/browser/web_module_stat_tracker.h b/src/cobalt/browser/web_module_stat_tracker.h index 8ebf73d..5b317e7 100644 --- a/src/cobalt/browser/web_module_stat_tracker.h +++ b/src/cobalt/browser/web_module_stat_tracker.h
@@ -54,9 +54,6 @@ // triggers flushing of periodic counts within the stat trackers. void OnRenderTreeProduced(); - // Called when h5vcc.system.record_stats is set - void OnSetRecordStats(bool set); - private: enum EventType { kEventTypeInvalid = -1, @@ -93,9 +90,6 @@ duration_layout_update_used_sizes; base::CVal<base::TimeDelta, base::CValPublic> duration_layout_render_and_animate; - - // Time series-related - base::CVal<std::string, base::CValPublic> event_durations; }; // From base::StopWatchOwner @@ -122,9 +116,6 @@ std::vector<base::StopWatch> stop_watches_; std::vector<base::TimeDelta> stop_watch_durations_; - // Time series-related - bool record_stats_; - std::string name_; base::CVal<int, base::CValPublic> event_is_processing_;
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id index a658c30..d690671 100644 --- a/src/cobalt/build/build.id +++ b/src/cobalt/build/build.id
@@ -1 +1 @@ -13245 \ No newline at end of file +13403 \ No newline at end of file
diff --git a/src/cobalt/build/config/base.gypi b/src/cobalt/build/config/base.gypi index 36b2f95..8ba37eb 100644 --- a/src/cobalt/build/config/base.gypi +++ b/src/cobalt/build/config/base.gypi
@@ -236,9 +236,6 @@ 'werror': '', # Cobalt doesn't currently support tcmalloc. 'linux_use_tcmalloc': 0, - - 'enable_webdriver%': 0, - # The event polling mechanism available on this platform to support libevent. # Platforms may redefine to 'poll' if necessary. # Other mechanisms, e.g. devpoll, kqueue, select, are not yet supported. @@ -456,6 +453,7 @@ 'enable_network_logging': 1, 'enable_remote_debugging%': 1, 'enable_screenshot': 1, + 'enable_webdriver%': 1, }, }, { @@ -467,6 +465,7 @@ 'enable_network_logging': 0, 'enable_remote_debugging%': 0, 'enable_screenshot': 0, + 'enable_webdriver': 0, }, }], ],
diff --git a/src/cobalt/build/config/win.gypi b/src/cobalt/build/config/win.gypi index 6a8cab4..8504e8b 100644 --- a/src/cobalt/build/config/win.gypi +++ b/src/cobalt/build/config/win.gypi
@@ -34,6 +34,9 @@ # there for acceptable values for this variable. 'javascript_engine': 'javascriptcore', + # Webdriver won't compile on MSVC because of UTF8 string constant issues + 'enable_webdriver': 0, + # Compile with "PREfast" on by default. 'static_analysis%': 'true',
diff --git a/src/cobalt/css_parser/parser.cc b/src/cobalt/css_parser/parser.cc index c3ff81a..25f4d35 100644 --- a/src/cobalt/css_parser/parser.cc +++ b/src/cobalt/css_parser/parser.cc
@@ -24,6 +24,7 @@ #include <string> #include "base/bind.h" +#include "base/debug/trace_event.h" #include "base/hash_tables.h" #include "base/lazy_instance.h" #include "base/optional.h" @@ -365,6 +366,7 @@ bool ParserImpl::Parse() { // For more information on error codes // see http://www.gnu.org/software/bison/manual/html_node/Parser-Function.html + TRACE_EVENT0("cobalt::css_parser", "ParseImpl::Parse"); last_syntax_error_location_ = base::nullopt; int error_code(yyparse(this)); switch (error_code) {
diff --git a/src/cobalt/cssom/css_declared_style_declaration.cc b/src/cobalt/cssom/css_declared_style_declaration.cc index c1d455c..6c4557e 100644 --- a/src/cobalt/cssom/css_declared_style_declaration.cc +++ b/src/cobalt/cssom/css_declared_style_declaration.cc
@@ -16,6 +16,7 @@ #include "cobalt/cssom/css_declared_style_declaration.h" +#include "base/debug/trace_event.h" #include "base/lazy_instance.h" #include "cobalt/cssom/css_declared_style_data.h" #include "cobalt/cssom/css_parser.h" @@ -63,6 +64,7 @@ void CSSDeclaredStyleDeclaration::set_css_text( const std::string& css_text, script::ExceptionState* /*exception_state*/) { + TRACE_EVENT0("cobalt::cssom", "CSSDeclaredStyleDeclaration::set_css_text"); DCHECK(css_parser_); scoped_refptr<CSSDeclaredStyleData> declaration = css_parser_->ParseStyleDeclarationList(
diff --git a/src/cobalt/dom/MediaSource.idl b/src/cobalt/dom/MediaSource.idl index a53419c..65eeb35 100644 --- a/src/cobalt/dom/MediaSource.idl +++ b/src/cobalt/dom/MediaSource.idl
@@ -28,4 +28,6 @@ readonly attribute DOMString readyState; [RaisesException] void endOfStream(optional DOMString error); + + [CallWith=EnvironmentSettings] static boolean isTypeSupported(DOMString type); };
diff --git a/src/cobalt/dom/dom_settings.cc b/src/cobalt/dom/dom_settings.cc index 6588e93..1eca2a2 100644 --- a/src/cobalt/dom/dom_settings.cc +++ b/src/cobalt/dom/dom_settings.cc
@@ -26,6 +26,7 @@ network::NetworkModule* network_module, const scoped_refptr<Window>& window, MediaSource::Registry* media_source_registry, + media::CanPlayTypeHandler* can_play_type_handler, script::JavaScriptEngine* engine, script::GlobalEnvironment* global_environment, const Options& options) @@ -36,6 +37,7 @@ array_buffer_allocator_(options.array_buffer_allocator), array_buffer_cache_(options.array_buffer_cache), media_source_registry_(media_source_registry), + can_play_type_handler_(can_play_type_handler), javascript_engine_(engine), global_environment_(global_environment) { if (array_buffer_allocator_) {
diff --git a/src/cobalt/dom/dom_settings.h b/src/cobalt/dom/dom_settings.h index 8366e67..5bd39e1 100644 --- a/src/cobalt/dom/dom_settings.h +++ b/src/cobalt/dom/dom_settings.h
@@ -22,6 +22,7 @@ #include "cobalt/dom/array_buffer.h" #include "cobalt/dom/media_source.h" #include "cobalt/dom/window.h" +#include "cobalt/media/can_play_type_handler.h" #include "cobalt/script/environment_settings.h" namespace cobalt { @@ -62,6 +63,7 @@ network::NetworkModule* network_module, const scoped_refptr<Window>& window, MediaSource::Registry* media_source_registry, + media::CanPlayTypeHandler* can_play_type_handler, script::JavaScriptEngine* engine, script::GlobalEnvironment* global_environment_proxy, const Options& options = Options()); @@ -95,6 +97,9 @@ MediaSource::Registry* media_source_registry() const { return media_source_registry_; } + media::CanPlayTypeHandler* can_play_type_handler() const { + return can_play_type_handler_; + } // An absolute URL used to resolve relative URLs. virtual GURL base_url() const; @@ -107,6 +112,7 @@ ArrayBuffer::Allocator* array_buffer_allocator_; ArrayBuffer::Cache* array_buffer_cache_; MediaSource::Registry* media_source_registry_; + media::CanPlayTypeHandler* can_play_type_handler_; script::JavaScriptEngine* javascript_engine_; script::GlobalEnvironment* global_environment_;
diff --git a/src/cobalt/dom/media_source.cc b/src/cobalt/dom/media_source.cc index fa881bf..f20ffb0 100644 --- a/src/cobalt/dom/media_source.cc +++ b/src/cobalt/dom/media_source.cc
@@ -26,9 +26,12 @@ #include "base/logging.h" #include "base/string_split.h" #include "base/string_util.h" +#include "cobalt/base/polymorphic_downcast.h" #include "cobalt/base/tokens.h" #include "cobalt/dom/dom_exception.h" +#include "cobalt/dom/dom_settings.h" #include "cobalt/dom/event.h" +#include "cobalt/media/can_play_type_handler.h" namespace cobalt { namespace dom { @@ -252,6 +255,19 @@ player_->SourceEndOfStream(eos_status); } +// static +bool MediaSource::IsTypeSupported(script::EnvironmentSettings* settings, + const std::string& type) { + DOMSettings* dom_settings = + base::polymorphic_downcast<DOMSettings*>(settings); + DCHECK(dom_settings); + media::CanPlayTypeHandler* handler = dom_settings->can_play_type_handler(); + DCHECK(handler); + std::string result = handler->CanPlayType(type, ""); + DLOG(INFO) << "MediaSource::IsTypeSupported(" << type << ") -> " << result; + return result == "probably"; +} + void MediaSource::SetPlayer(WebMediaPlayer* player) { // It is possible to reuse a MediaSource object but unlikely. DCHECK it until // it is used in this way.
diff --git a/src/cobalt/dom/media_source.h b/src/cobalt/dom/media_source.h index ec769e1..a30e5c3 100644 --- a/src/cobalt/dom/media_source.h +++ b/src/cobalt/dom/media_source.h
@@ -25,6 +25,7 @@ #include "cobalt/dom/event_target.h" #include "cobalt/dom/source_buffer.h" #include "cobalt/dom/source_buffer_list.h" +#include "cobalt/script/environment_settings.h" #include "cobalt/script/exception_state.h" #include "media/player/web_media_player.h" @@ -87,6 +88,9 @@ void EndOfStream(const std::string& error, script::ExceptionState* exception_state); + static bool IsTypeSupported(script::EnvironmentSettings* settings, + const std::string& type); + // Custom, not in any spec. // // The player is set when the media source is attached to a media element.
diff --git a/src/cobalt/h5vcc/H5vccSystem.idl b/src/cobalt/h5vcc/H5vccSystem.idl index abf2017..25bbe3b 100644 --- a/src/cobalt/h5vcc/H5vccSystem.idl +++ b/src/cobalt/h5vcc/H5vccSystem.idl
@@ -20,7 +20,6 @@ readonly attribute DOMString platform; readonly attribute DOMString region; readonly attribute DOMString version; - attribute boolean recordStats; boolean triggerHelp(); DOMString getVideoContainerSizeOverride(); };
diff --git a/src/cobalt/h5vcc/h5vcc.cc b/src/cobalt/h5vcc/h5vcc.cc index 16cb6c9..9792fcb 100644 --- a/src/cobalt/h5vcc/h5vcc.cc +++ b/src/cobalt/h5vcc/h5vcc.cc
@@ -27,7 +27,7 @@ new H5vccRuntime(settings.event_dispatcher, settings.initial_deep_link); settings_ = new H5vccSettings(settings.media_module); storage_ = new H5vccStorage(settings.network_module); - system_ = new H5vccSystem(settings.on_set_record_stats); + system_ = new H5vccSystem(); } } // namespace h5vcc
diff --git a/src/cobalt/h5vcc/h5vcc.h b/src/cobalt/h5vcc/h5vcc.h index 8f46d2f..369cad3 100644 --- a/src/cobalt/h5vcc/h5vcc.h +++ b/src/cobalt/h5vcc/h5vcc.h
@@ -45,7 +45,6 @@ account::AccountManager* account_manager; base::EventDispatcher* event_dispatcher; std::string initial_deep_link; - base::Callback<void(bool)> on_set_record_stats; }; explicit H5vcc(const Settings& config);
diff --git a/src/cobalt/h5vcc/h5vcc_system.cc b/src/cobalt/h5vcc/h5vcc_system.cc index 5b668cd..3bf8cf3 100644 --- a/src/cobalt/h5vcc/h5vcc_system.cc +++ b/src/cobalt/h5vcc/h5vcc_system.cc
@@ -24,9 +24,7 @@ namespace cobalt { namespace h5vcc { -H5vccSystem::H5vccSystem(const base::Callback<void(bool)>& on_set_record_stats) - : on_set_record_stats_(on_set_record_stats) { -} +H5vccSystem::H5vccSystem() {} bool H5vccSystem::are_keys_reversed() const { return deprecated::PlatformDelegate::Get()->AreKeysReversed();
diff --git a/src/cobalt/h5vcc/h5vcc_system.h b/src/cobalt/h5vcc/h5vcc_system.h index 3ce8764..be0db79 100644 --- a/src/cobalt/h5vcc/h5vcc_system.h +++ b/src/cobalt/h5vcc/h5vcc_system.h
@@ -26,21 +26,13 @@ class H5vccSystem : public script::Wrappable { public: - explicit H5vccSystem(const base::Callback<void(bool)>& on_set_record_stats); + H5vccSystem(); bool are_keys_reversed() const; std::string build_id() const; std::string platform() const; std::string region() const; std::string version() const; - bool record_stats() const { - return record_stats_; - } - void set_record_stats(bool record_stats) { - record_stats_ = record_stats; - - on_set_record_stats_.Run(record_stats); - } bool TriggerHelp() const; std::string GetVideoContainerSizeOverride() const; @@ -48,8 +40,6 @@ DEFINE_WRAPPABLE_TYPE(H5vccSystem); private: - base::Callback<void(bool)> on_set_record_stats_; - bool record_stats_; DISALLOW_COPY_AND_ASSIGN(H5vccSystem); };
diff --git a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc index 748aefd..e7225dc 100644 --- a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc +++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
@@ -366,7 +366,22 @@ void RenderTreeNodeVisitor::Visit(render_tree::FilterNode* filter_node) { if (filter_node->data().map_to_mesh_filter) { - // TODO: Implement support for MapToMeshFilter. + // TODO: Implement support for MapToMeshFilter instead of punching out + // the area that it occupies. + SkPaint paint; + paint.setXfermodeMode(SkXfermode::kSrc_Mode); + paint.setARGB(0, 0, 0, 0); + + math::RectF bounds = filter_node->GetBounds(); + SkRect sk_rect = SkRect::MakeXYWH(bounds.x(), bounds.y(), bounds.width(), + bounds.height()); + + draw_state_.render_target->drawRect(sk_rect, paint); + +#if ENABLE_FLUSH_AFTER_EVERY_NODE + draw_state_.render_target->flush(); +#endif + return; }
diff --git a/src/cobalt/script/mozjs/mozjs_callback_function.h b/src/cobalt/script/mozjs/mozjs_callback_function.h index cb3abc6..71d8914 100644 --- a/src/cobalt/script/mozjs/mozjs_callback_function.h +++ b/src/cobalt/script/mozjs/mozjs_callback_function.h
@@ -57,6 +57,7 @@ CallbackResult<R> Run() const OVERRIDE { + TRACE_EVENT0("cobalt::script::mozjs", "MozjsCallbackFunction::Run"); CallbackResult<R> callback_result; JSAutoRequest auto_request(context_); JS::RootedObject function(context_, weak_function_.Get()); @@ -110,6 +111,7 @@ CallbackResult<R> Run( typename base::internal::CallbackParamTraits<A1>::ForwardType a1) const OVERRIDE { + TRACE_EVENT0("cobalt::script::mozjs", "MozjsCallbackFunction::Run"); CallbackResult<R> callback_result; JSAutoRequest auto_request(context_); JS::RootedObject function(context_, weak_function_.Get()); @@ -169,6 +171,7 @@ typename base::internal::CallbackParamTraits<A1>::ForwardType a1, typename base::internal::CallbackParamTraits<A2>::ForwardType a2) const OVERRIDE { + TRACE_EVENT0("cobalt::script::mozjs", "MozjsCallbackFunction::Run"); CallbackResult<R> callback_result; JSAutoRequest auto_request(context_); JS::RootedObject function(context_, weak_function_.Get()); @@ -230,6 +233,7 @@ typename base::internal::CallbackParamTraits<A2>::ForwardType a2, typename base::internal::CallbackParamTraits<A3>::ForwardType a3) const OVERRIDE { + TRACE_EVENT0("cobalt::script::mozjs", "MozjsCallbackFunction::Run"); CallbackResult<R> callback_result; JSAutoRequest auto_request(context_); JS::RootedObject function(context_, weak_function_.Get()); @@ -293,6 +297,7 @@ typename base::internal::CallbackParamTraits<A3>::ForwardType a3, typename base::internal::CallbackParamTraits<A4>::ForwardType a4) const OVERRIDE { + TRACE_EVENT0("cobalt::script::mozjs", "MozjsCallbackFunction::Run"); CallbackResult<R> callback_result; JSAutoRequest auto_request(context_); JS::RootedObject function(context_, weak_function_.Get()); @@ -359,6 +364,7 @@ typename base::internal::CallbackParamTraits<A4>::ForwardType a4, typename base::internal::CallbackParamTraits<A5>::ForwardType a5) const OVERRIDE { + TRACE_EVENT0("cobalt::script::mozjs", "MozjsCallbackFunction::Run"); CallbackResult<R> callback_result; JSAutoRequest auto_request(context_); JS::RootedObject function(context_, weak_function_.Get()); @@ -427,6 +433,7 @@ typename base::internal::CallbackParamTraits<A5>::ForwardType a5, typename base::internal::CallbackParamTraits<A6>::ForwardType a6) const OVERRIDE { + TRACE_EVENT0("cobalt::script::mozjs", "MozjsCallbackFunction::Run"); CallbackResult<R> callback_result; JSAutoRequest auto_request(context_); JS::RootedObject function(context_, weak_function_.Get()); @@ -497,6 +504,7 @@ typename base::internal::CallbackParamTraits<A6>::ForwardType a6, typename base::internal::CallbackParamTraits<A7>::ForwardType a7) const OVERRIDE { + TRACE_EVENT0("cobalt::script::mozjs", "MozjsCallbackFunction::Run"); CallbackResult<R> callback_result; JSAutoRequest auto_request(context_); JS::RootedObject function(context_, weak_function_.Get());
diff --git a/src/cobalt/script/mozjs/mozjs_callback_function.h.pump b/src/cobalt/script/mozjs/mozjs_callback_function.h.pump index 14067e9..861dcd3 100644 --- a/src/cobalt/script/mozjs/mozjs_callback_function.h.pump +++ b/src/cobalt/script/mozjs/mozjs_callback_function.h.pump
@@ -76,6 +76,7 @@ typename base::internal::CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) const OVERRIDE { + TRACE_EVENT0("cobalt::script::mozjs", "MozjsCallbackFunction::Run"); CallbackResult<R> callback_result; JSAutoRequest auto_request(context_); JS::RootedObject function(context_, weak_function_.Get());
diff --git a/src/cobalt/webdriver_benchmarks/tests/guide.py b/src/cobalt/webdriver_benchmarks/tests/guide.py index 442eef1..2ebf58e 100755 --- a/src/cobalt/webdriver_benchmarks/tests/guide.py +++ b/src/cobalt/webdriver_benchmarks/tests/guide.py
@@ -26,12 +26,11 @@ class GuideTest(tv_testcase.TvTestCase): def test_simple(self): + layout_times_us = [] + self.load_tv() self.assert_displayed(tv.FOCUSED_SHELF) - print(str(self.get_webdriver().execute_script( - "h5vcc.system.recordStats = true"))) - for _ in xrange(REPEAT_COUNT): self.send_keys(tv.FOCUSED_SHELF, keys.Keys.ARROW_LEFT) self.assert_displayed(tv.FOCUSED_GUIDE) @@ -40,8 +39,9 @@ self.poll_until_found(tv.FOCUSED_SHELF) self.assert_displayed(tv.FOCUSED_SHELF_TITLE) self.wait_for_layout_complete() + layout_times_us.append(self.get_keyup_layout_duration_us()) - self.record_results("GuideTest.test_simple") + self.record_results("GuideTest.test_simple", layout_times_us) if __name__ == "__main__":
diff --git a/src/cobalt/webdriver_benchmarks/tests/shelf.py b/src/cobalt/webdriver_benchmarks/tests/shelf.py index b47e379..7372e55 100755 --- a/src/cobalt/webdriver_benchmarks/tests/shelf.py +++ b/src/cobalt/webdriver_benchmarks/tests/shelf.py
@@ -27,25 +27,25 @@ class ShelfTest(tv_testcase.TvTestCase): def test_simple(self): + layout_times_us = [] self.load_tv() self.assert_displayed(tv.FOCUSED_SHELF) - print(str(self.get_webdriver().execute_script( - "h5vcc.system.recordStats = true"))) - for _ in xrange(DEFAULT_SHELVES_COUNT): self.send_keys(tv.FOCUSED_SHELF, keys.Keys.ARROW_DOWN) self.poll_until_found(tv.FOCUSED_SHELF) self.assert_displayed(tv.FOCUSED_SHELF_TITLE) self.wait_for_layout_complete() + layout_times_us.append(self.get_keyup_layout_duration_us()) for _ in xrange(SHELF_ITEMS_COUNT): self.send_keys(tv.FOCUSED_TILE, keys.Keys.ARROW_RIGHT) self.poll_until_found(tv.FOCUSED_TILE) self.assert_displayed(tv.FOCUSED_SHELF_TITLE) self.wait_for_layout_complete() + layout_times_us.append(self.get_keyup_layout_duration_us()) - self.record_results("ShelfTest.test_simple") + self.record_results("ShelfTest.test_simple", layout_times_us) if __name__ == "__main__":
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase.py b/src/cobalt/webdriver_benchmarks/tv_testcase.py index 23649ec..3189523 100644 --- a/src/cobalt/webdriver_benchmarks/tv_testcase.py +++ b/src/cobalt/webdriver_benchmarks/tv_testcase.py
@@ -4,6 +4,7 @@ from __future__ import division from __future__ import print_function +import json import os import sys import time @@ -25,7 +26,7 @@ partial_layout_benchmark.ImportSeleniumModule( submodule="common.exceptions").ElementNotVisibleException) -BASE_URL = "https://www.youtube.com/tv" +BASE_URL = "https://www.youtube.com/tv?env_forcedOffAllExperiments=true" PAGE_LOAD_WAIT_SECONDS = 30 LAYOUT_TIMEOUT_SECONDS = 5 @@ -163,18 +164,21 @@ time.sleep(0.1) - def record_results(self, name): + def get_keyup_layout_duration_us(self): + return int(self.get_webdriver().execute_script( + "return h5vcc.cVal.getValue('Event.Duration.MainWebModule.KeyUp')")) + + def record_results(self, name, results): """Records results of benchmark. The duration of KeyUp events will be recorded. Args: name: name of test case + results: Test results. Must be JSON encodable """ print("tv_testcase RESULT: " + name + " " - + str(self.get_webdriver().execute_script( - "return h5vcc.cVal.getValue(" - "'Event.Durations.MainWebModule.KeyUp')"))) + + json.JSONEncoder().encode(results)) def main():
diff --git a/src/cobalt/xhr/xml_http_request_test.cc b/src/cobalt/xhr/xml_http_request_test.cc index ac3b5c1..e39fe5f 100644 --- a/src/cobalt/xhr/xml_http_request_test.cc +++ b/src/cobalt/xhr/xml_http_request_test.cc
@@ -92,7 +92,8 @@ class FakeSettings : public dom::DOMSettings { public: - FakeSettings() : dom::DOMSettings(0, NULL, NULL, NULL, NULL, NULL, NULL) {} + FakeSettings() + : dom::DOMSettings(0, NULL, NULL, NULL, NULL, NULL, NULL, NULL) {} GURL base_url() const OVERRIDE { return GURL("http://example.com"); } };
diff --git a/src/nb/nb.gyp b/src/nb/nb.gyp index b3b9a0d..3780eb9 100644 --- a/src/nb/nb.gyp +++ b/src/nb/nb.gyp
@@ -63,6 +63,7 @@ 'fixed_no_free_allocator_test.cc', 'reuse_allocator_test.cc', 'run_all_unittests.cc', + 'thread_local_object_test.cc', ], 'dependencies': [ 'nb',
diff --git a/src/nb/thread_local_object.h b/src/nb/thread_local_object.h new file mode 100644 index 0000000..4b6a7ab --- /dev/null +++ b/src/nb/thread_local_object.h
@@ -0,0 +1,214 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BASE_THREADING_THREAD_LOCAL_OBJECT_H_ +#define BASE_THREADING_THREAD_LOCAL_OBJECT_H_ + +#include <set> + +#include "starboard/configuration.h" +#include "starboard/log.h" +#include "starboard/mutex.h" +#include "starboard/thread.h" + +namespace base { + +// Like base::ThreadLocalPointer<T> but destroys objects. This is important +// for using ThreadLocalObjects that aren't tied to a singleton, or for +// access by threads which will call join(). +// +// FEATURE COMPARISON TABLE: +// | Thread Join | Container Destroyed +// ------------------------------------------------------------------ +// ThreadLocalPointer<T> | LEAKS | LEAKS +// ThreadLocalObject<T> | Object Destroyed | Objects Destroyed +// +// EXAMPLE: +// ThreadLocalObject<std::map<std::string, int> > map_tls; +// Map* map = map_tls->GetOrCreate(); +// (*map)["my string"] = 15; +// Thread t = new Thread(&map_tls); +// t->start(); +// // t creates it's own thread local map. +// t->join(); // Make sure that thread joins before map_tls is destroyed! +// +// OBJECT DESTRUCTION: +// There are two ways for an object to be destroyed by the ThreadLocalObject. +// The first way is via a thread join. In this case only the object +// associated with the thread is deleted. +// The second way an object is destroyed is by the ThreadLocalObject +// container to be destroyed, in this case ALL thread local objects are +// destroyed. +// +// PERFORMANCE: +// ThreadLocalObject is fast for the Get() function if the object has +// has already been created, requiring one extra pointer dereference +// over ThreadLocalPointer<T>. +template <typename Type> +class ThreadLocalObject { + public: + ThreadLocalObject() : slot_() { + slot_ = SbThreadCreateLocalKey(DeleteEntry); + SB_DCHECK(kSbThreadLocalKeyInvalid != slot_); + constructing_thread_id_ = SbThreadGetId(); + } + + // Enables destruction by any other thread. Otherwise, the class instance + // will warn when a different thread than the constructing destroys this. + void EnableDestructionByAnyThread() { + constructing_thread_id_ = kSbThreadInvalidId; + } + + // Thread Local Objects are destroyed after this call. + ~ThreadLocalObject() { + CheckCurrentThreadAllowedToDestruct(); + SB_DCHECK(entry_set_.size() < 2) + << "Logic error: Some threads may still be accessing the objects that " + << "are about to be destroyed. Only one object is expected and that " + << "should be for the main thread. The caller should ensure that " + << "other threads that access this object are externally " + << "synchronized."; + // No locking is done because the entries should not be accessed by + // different threads while this object is shutting down. If access is + // occuring then the caller has a race condition, external to this class. + typedef typename Set::iterator Iter; + for (Iter it = entry_set_.begin(); it != entry_set_.end(); ++it) { + Entry* entry = *it; + SB_DCHECK(entry->owner_ == this); + delete entry->ptr_; + delete entry; + } + + // Cleanup the thread local key. + SbThreadDestroyLocalKey(slot_); + } + + // Warns if there is a misuse of this object. + void CheckCurrentThreadAllowedToDestruct() const { + if (kSbThreadInvalidId == constructing_thread_id_) { + return; // EnableDestructionByAnyThread() called. + } + const SbThreadId curr_thread_id = SbThreadGetId(); + if (curr_thread_id == constructing_thread_id_) { + return; // Same thread that constructed this. + } + + if (SB_DLOG_IS_ON(FATAL)) { + SB_DCHECK(false) + << "ThreadLocalObject<T> was created in thread " + << constructing_thread_id_ << "\nbut was destroyed by " + << curr_thread_id << ". If this is intentional then call " + << "EnableDestructionByAnyThread() to silence this " + << "warning."; + } + } + + // Either returns the created pointer for the current thread, or otherwise + // constructs the object using the default constructor and returns it. + Type* GetOrCreate() { + Type* object = GetIfExists(); + if (!object) { // create object. + object = new Type(); + Entry* entry = new Entry(this, object); + // Insert into the set of tls entries. + // Performance: Its assumed that creation of objects is much less + // frequent than getting an object. + { + starboard::ScopedLock lock(entry_set_mutex_); + entry_set_.insert(entry); + } + SbThreadSetLocalValue(slot_, entry); + } + return object; + } + + // Returns the pointer if it exists in the current thread, otherwise NULL. + Type* GetIfExists() const { + Entry* entry = GetEntryIfExists(); + if (!entry) { return NULL; } + return entry->ptr_; + } + + // Releases ownership of the pointer FROM THE CURRENT THREAD. + // The caller has responsibility to make sure that the pointer is destroyed. + Type* Release() { + if (Entry* entry = GetEntryIfExists()) { + // The entry will no longer run it's destructor on thread join. + SbThreadSetLocalValue(slot_, NULL); // NULL out pointer for TLS. + Type* object = entry->ptr_; + RemoveEntry(entry); + return object; + } else { + return NULL; + } + } + + private: + struct Entry { + Entry(ThreadLocalObject* own, Type* ptr) : owner_(own), ptr_(ptr) { + } + ~Entry() { + ptr_ = NULL; + owner_ = NULL; + } + ThreadLocalObject* owner_; + Type* ptr_; + }; + + // Deletes the TLSEntry. + static void DeleteEntry(void* ptr) { + if (!ptr) { + SB_NOTREACHED(); + return; + } + Entry* entry = reinterpret_cast<Entry*>(ptr); + ThreadLocalObject* tls = entry->owner_; + Type* object = entry->ptr_; + tls->RemoveEntry(entry); + delete object; + } + + void RemoveEntry(Entry* entry) { + { + starboard::ScopedLock lock(entry_set_mutex_); + entry_set_.erase(entry); + } + delete entry; + } + + Entry* GetEntryIfExists() const { + void* ptr = SbThreadGetLocalValue(slot_); + Entry* entry = static_cast<Entry*>(ptr); + return entry; + } + + typedef std::set<Entry*> Set; + // Allows GetIfExists() to be const. + mutable SbThreadLocalKey slot_; + // entry_set_ contains all the outstanding entries for the thread local + // objects that have been created. + Set entry_set_; + mutable starboard::Mutex entry_set_mutex_; + // Used to warn when there is a mismatch between thread that constructed and + // thread that destroyed this object. + SbThreadId constructing_thread_id_; + + SB_DISALLOW_COPY_AND_ASSIGN(ThreadLocalObject<Type>); +}; + +} // namespace base + +#endif // BASE_THREADING_THREAD_LOCAL_H_
diff --git a/src/nb/thread_local_object_test.cc b/src/nb/thread_local_object_test.cc new file mode 100644 index 0000000..000c6f4 --- /dev/null +++ b/src/nb/thread_local_object_test.cc
@@ -0,0 +1,346 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <map> +#include <string> + +#include "nb/thread_local_object.h" +#include "nb/scoped_ptr.h" +#include "starboard/mutex.h" +#include "starboard/thread.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace { + +// Similar to C++11 std::atomic<T>. +// Atomic<T> may be instantiated with any TriviallyCopyable type T. +// Atomic<T> is neither copyable nor movable. +// TODO: Lift this class out into the library. +template <typename T> +class Atomic { + public: + // C++11 forbids a copy constructor for std::atomic<T>, it also forbids + // a move operation. + Atomic() : value_() {} + explicit Atomic(T v) : value_(v) {} + + // Checks whether the atomic operations on all objects of this type + // are lock-free. + // Returns true if the atomic operations on the objects of this type + // are lock-free, false otherwise. + // + // All atomic types may be implemented using mutexes or other locking + // operations, rather than using the lock-free atomic CPU instructions. + // Atomic types are also allowed to be sometimes lock-free, e.g. if only + // aligned memory accesses are naturally atomic on a given architecture, + // misaligned objects of the same type have to use locks. + bool is_lock_free() const { return false; } + bool is_lock_free() const volatile { return false; } + + // Atomically replaces the value of the atomic object + // and returns the value held previously. + T Swap(T new_val) { + int old_value = -1; + { + starboard::ScopedLock lock(mutex_); + old_value = value_; + value_ = new_val; + } + return old_value; + } + + // Atomically obtains the value of the atomic object. + T Get() const { + starboard::ScopedLock lock(mutex_); + return value_; + } + + // Returns the new updated value after the operation has been applied. + T Add(T val) { + starboard::ScopedLock lock(mutex_); + value_ += val; + return value_; + } + + // TrySwap(...) sets the new value if and only if "expected_old_value" + // matches the actual value during the atomic assignment operation. If this + // succeeds then true is returned. If there is a mismatch then the value is + // left unchanged and false is returned. + // Inputs: + // new_value: Attempt to set the value to this new value. + // expected_old_value: A test condition for success. If the actual value + // matches the expected_old_value then the swap will succeed. + // optional_actual_value: If non-null, then the actual value at the time + // of the attempted operation is set to this value. + bool TrySwap(T new_value, T expected_old_value, + T* optional_actual_value) { + starboard::ScopedLock lock(mutex_); + if (optional_actual_value) { + *optional_actual_value = value_; + } + if (expected_old_value == value_) { + value_ = new_value; + return true; + } + return false; + } + + private: + T value_; + starboard::Mutex mutex_; +}; + +// Simple atomic int class. This could be optimized for speed using +// compiler intrinsics for concurrent integer modification. +class AtomicInt : public Atomic<int> { + public: + AtomicInt() : Atomic<int>(0) {} + explicit AtomicInt(int initial_val) : Atomic<int>(initial_val) {} + void Increment() { Add(1); } + void Decrement() { Add(-1); } +}; + +// Simple atomic bool class. This could be optimized for speed using +// compiler intrinsics for concurrent integer modification. +class AtomicBool : public Atomic<bool> { + public: + AtomicBool() : Atomic<bool>(false) {} + explicit AtomicBool(bool initial_val) : Atomic<bool>(initial_val) {} +}; + +// AbstractTestThread that is a bare bones class wrapper around Starboard +// thread. Subclasses must override Run(). +// TODO: Move this to nplb/thread_helpers.h +class AbstractTestThread { + public: + explicit AbstractTestThread() : thread_(kSbThreadInvalid) {} + virtual ~AbstractTestThread() {} + + // Subclasses should override the Run method. + virtual void Run() = 0; + + // Calls SbThreadCreate() with default parameters. + void Start() { + SbThreadEntryPoint entry_point = ThreadEntryPoint; + + thread_ = SbThreadCreate( + 0, // default stack_size. + kSbThreadNoPriority, // default priority. + kSbThreadNoAffinity, // default affinity. + true, // joinable. + "AbstractTestThread", + entry_point, + this); + + if (kSbThreadInvalid == thread_) { + ADD_FAILURE_AT(__FILE__, __LINE__) << "Invalid thread."; + } + return; + } + + void Join() { + if (!SbThreadJoin(thread_, NULL)) { + ADD_FAILURE_AT(__FILE__, __LINE__) << "Could not join thread."; + } + } + + private: + static void* ThreadEntryPoint(void* ptr) { + AbstractTestThread* this_ptr = static_cast<AbstractTestThread*>(ptr); + this_ptr->Run(); + return NULL; + } + + SbThread thread_; +}; + +// Simple class that counts the number of instances alive. +struct CountsInstances { + CountsInstances() { s_instances_.Increment(); } + ~CountsInstances() { s_instances_.Decrement(); } + static AtomicInt s_instances_; + static int NumInstances() { return s_instances_.Get(); } + static void ResetNumInstances() { s_instances_.Swap(0); } +}; +AtomicInt CountsInstances::s_instances_(0); + +// A simple thread that just creates the an object from the supplied +// ThreadLocalObject<T> and then exits. +template <typename TYPE> +class CreateThreadLocalObjectThenExit : public AbstractTestThread { + public: + explicit CreateThreadLocalObjectThenExit( + ThreadLocalObject<TYPE>* tlo) : tlo_(tlo) {} + + virtual void Run() { + // volatile as a defensive measure to prevent compiler from optimizing this + // statement out. + volatile TYPE* val = tlo_->GetOrCreate(); + } + ThreadLocalObject<TYPE>* tlo_; +}; + +// A simple thread that just deletes the object supplied on a thread and then +// exists. +template <typename TYPE> +class DestroyTypeOnThread : public AbstractTestThread { + public: + explicit DestroyTypeOnThread(TYPE* ptr) + : ptr_(ptr) {} + virtual void Run() { + ptr_.reset(NULL); // Destroys the object. + } + private: + nb::scoped_ptr<TYPE> ptr_; +}; + +// Tests the expectation that a ThreadLocalObject can be simply used by +// the main thread. +TEST(ThreadLocalObject, MainThread) { + ThreadLocalObject<bool> tlo_bool; + EXPECT_TRUE(NULL == tlo_bool.GetIfExists()); + bool* the_bool = tlo_bool.GetOrCreate(); + EXPECT_TRUE(the_bool != NULL); + EXPECT_FALSE(*the_bool); + *the_bool = true; + EXPECT_TRUE(*(tlo_bool.GetIfExists())); +} + +// Tests the expectation that a ThreadLocalObject can be used on +// complex objects type (i.e. non pod types). +TEST(ThreadLocalObject, MainThreadComplexObject) { + typedef std::map<std::string, int> Map; + ThreadLocalObject<Map> map_tlo; + EXPECT_FALSE(map_tlo.GetIfExists()); + ASSERT_TRUE(map_tlo.GetOrCreate()); + Map* map = map_tlo.GetIfExists(); + const Map* const_map = map_tlo.GetIfExists(); + ASSERT_TRUE(map); + ASSERT_TRUE(const_map); + // If the object is properly constructed then this find operation + // should succeed. + (*map)["my string"] = 15; + ASSERT_EQ(15, (*map)["my string"]); +} + +// Tests that when a ThreadLocalObject is destroyed on the main thread that +// the pointers it contained are also destroyed. +TEST(ThreadLocalObject, DestroysObjectOnTLODestruction) { + CountsInstances::ResetNumInstances(); + typedef ThreadLocalObject<CountsInstances> TLO; + + // Create the TLO object and then immediately destroy it. + nb::scoped_ptr<TLO> tlo_ptr(new TLO); + tlo_ptr->GetOrCreate(); // Instantiate the internal object. + EXPECT_EQ(1, CountsInstances::NumInstances()); + tlo_ptr.reset(NULL); // Should destroy all outstanding allocs. + // Now the TLO is destroyed and therefore the destructor should run on the + // internal object. + EXPECT_EQ(0, CountsInstances::NumInstances()); + + CountsInstances::ResetNumInstances(); +} + +// Tests the expectation that the object can be released and that the pointer +// won't be deleted when the ThreadLocalObject that created it is destroyed. +TEST(ThreadLocalObject, ReleasesObject) { + CountsInstances::ResetNumInstances(); + typedef ThreadLocalObject<CountsInstances> TLO; + + nb::scoped_ptr<TLO> tlo_ptr(new TLO); + // Instantiate the internal object. + tlo_ptr->GetOrCreate(); + // Now release the pointer into the container. + nb::scoped_ptr<CountsInstances> last_ref(tlo_ptr->Release()); + // Destroying the TLO should not trigger the destruction of the object, + // because it was released. + tlo_ptr.reset(NULL); + // 1 instance left, which is held in last_ref. + EXPECT_EQ(1, CountsInstances::NumInstances()); + last_ref.reset(NULL); // Now the object should be destroyed and the + // instance count drops to 0. + EXPECT_EQ(0, CountsInstances::NumInstances()); + CountsInstances::ResetNumInstances(); +} + +// Tests the expectation that a thread that creates an object from +// the ThreadLocalObject store will automatically be destroyed by the +// thread joining. +TEST(ThreadLocalObject, ThreadJoinDestroysObject) { + CountsInstances::ResetNumInstances(); + typedef ThreadLocalObject<CountsInstances> TLO; + + nb::scoped_ptr<TLO> tlo(new TLO); + { + AbstractTestThread* thread = + new CreateThreadLocalObjectThenExit<CountsInstances>(tlo.get()); + thread->Start(); + thread->Join(); + // Once the thread joins, the object should be deleted and the instance + // counter falls to 0. + EXPECT_EQ(0, CountsInstances::NumInstances()); + delete thread; + } + + tlo.reset(NULL); // Now TLO destructor runs. + EXPECT_EQ(0, CountsInstances::NumInstances()); + CountsInstances::ResetNumInstances(); +} + +// Tests the expectation that objects created on the main thread are not +// leaked. +TEST(ThreadLocalObject, NoLeaksOnMainThread) { + CountsInstances::ResetNumInstances(); + + ThreadLocalObject<CountsInstances>* tlo = + new ThreadLocalObject<CountsInstances>; + tlo->EnableDestructionByAnyThread(); + + // Creates the object on the main thread. This is important because the + // main thread will never join and therefore at-exit functions won't get + // run. + CountsInstances* main_thread_object = tlo->GetOrCreate(); + + EXPECT_EQ(1, CountsInstances::NumInstances()); + + // Thread will simply create the thread local object (CountsInstances) + // and then return. + nb::scoped_ptr<AbstractTestThread> thread_ptr( + new CreateThreadLocalObjectThenExit<CountsInstances>(tlo)); + thread_ptr->Start(); // Object is now created. + thread_ptr->Join(); // ...then destroyed. + thread_ptr.reset(NULL); + + // Only main_thread_object should be alive now, therefore the count is 1. + EXPECT_EQ(1, CountsInstances::NumInstances()); + + // We COULD destroy the TLO on the main thread, but to be even fancier lets + // create a thread that will destroy the object on a back ground thread. + // The end result is that the TLO entry should be cleared out. + thread_ptr.reset( + new DestroyTypeOnThread<ThreadLocalObject<CountsInstances> >(tlo)); + thread_ptr->Start(); + thread_ptr->Join(); + thread_ptr.reset(NULL); + + // Now we expect that number of instances to be 0. + EXPECT_EQ(0, CountsInstances::NumInstances()); + CountsInstances::ResetNumInstances(); +} + +} // anonymous namespace +} // namespace base +
diff --git a/src/starboard/file.h b/src/starboard/file.h index bda6f3a..3f15daf 100644 --- a/src/starboard/file.h +++ b/src/starboard/file.h
@@ -117,6 +117,9 @@ // can happen with kSbFileCreateAlways), and false otherwise. |out_error| can // be NULL. If creation failed, it will return kSbFileInvalid. The read/write // position is at the beginning of the file. +// +// It only guarantees the correct behavior when |path| points to a file. If +// |path| points to directory, the behavior is undefined. SB_EXPORT SbFile SbFileOpen(const char* path, int flags, bool* out_created,
diff --git a/src/starboard/linux/shared/gyp_configuration.gypi b/src/starboard/linux/shared/gyp_configuration.gypi index e4ca809..af152fe 100644 --- a/src/starboard/linux/shared/gyp_configuration.gypi +++ b/src/starboard/linux/shared/gyp_configuration.gypi
@@ -21,7 +21,6 @@ 'target_os': 'linux', #'starboard_path%': 'starboard/linux/x64x11', - 'enable_webdriver': '1', 'in_app_dial%': 1, 'gl_type%': 'system_gles3',
diff --git a/src/starboard/nplb/file_get_info_test.cc b/src/starboard/nplb/file_get_info_test.cc index 1722577..fa61d8b 100644 --- a/src/starboard/nplb/file_get_info_test.cc +++ b/src/starboard/nplb/file_get_info_test.cc
@@ -40,6 +40,11 @@ // Assuming platforms have at least 1 second precision on filesystem // timestamps, we need to go back two seconds to avoid rounding issues. SbTime time = SbTimeGetNow() - (2 * kSbTimeSecond); +#if SB_HAS_QUIRK(FILESYSTEM_COARSE_ACCESS_TIME) + // On platforms with coarse access time, we assume 1 day precision and go + // back 2 days to avoid rounding issues. + SbTime coarse_time = SbTimeGetNow() - (2 * kSbTimeDay); +#endif const int kFileSize = 12; starboard::nplb::ScopedRandomFile random_file(kFileSize); @@ -56,7 +61,11 @@ EXPECT_FALSE(info.is_directory); EXPECT_FALSE(info.is_symbolic_link); EXPECT_LE(time, info.last_modified); +#if SB_HAS_QUIRK(FILESYSTEM_COARSE_ACCESS_TIME) + EXPECT_LE(coarse_time, info.last_accessed); +#else EXPECT_LE(time, info.last_accessed); +#endif EXPECT_LE(time, info.creation_time); } @@ -65,30 +74,6 @@ } } -TEST(SbFileGetInfoTest, WorksOnADirectory) { - char path[SB_FILE_MAX_PATH] = {0}; - bool result = - SbSystemGetPath(kSbSystemPathTempDirectory, path, SB_FILE_MAX_PATH); - EXPECT_TRUE(result); - - SbFile file = SbFileOpen(path, kSbFileOpenOnly | kSbFileRead, NULL, NULL); - ASSERT_TRUE(SbFileIsValid(file)); - - { - SbFileInfo info = {0}; - bool result = SbFileGetInfo(file, &info); - EXPECT_LE(0, info.size); - EXPECT_TRUE(info.is_directory); - EXPECT_FALSE(info.is_symbolic_link); - EXPECT_LE(0, info.last_modified); - EXPECT_LE(0, info.last_accessed); - EXPECT_LE(0, info.creation_time); - } - - result = SbFileClose(file); - EXPECT_TRUE(result); -} - } // namespace } // namespace nplb } // namespace starboard
diff --git a/src/starboard/nplb/file_get_path_info_test.cc b/src/starboard/nplb/file_get_path_info_test.cc index f96e205..fc024ff 100644 --- a/src/starboard/nplb/file_get_path_info_test.cc +++ b/src/starboard/nplb/file_get_path_info_test.cc
@@ -52,6 +52,11 @@ // Assuming platforms have at least 1 second precision on filesystem // timestamps, we need to go back two seconds to avoid rounding issues. SbTime time = SbTimeGetNow() - (2 * kSbTimeSecond); +#if SB_HAS_QUIRK(FILESYSTEM_COARSE_ACCESS_TIME) + // On platforms with coarse access time, we assume 1 day precision and go + // back 2 days to avoid rounding issues. + SbTime coarse_time = SbTimeGetNow() - (2 * kSbTimeDay); +#endif const int kFileSize = 12; ScopedRandomFile random_file(kFileSize); @@ -64,7 +69,11 @@ EXPECT_FALSE(info.is_directory); EXPECT_FALSE(info.is_symbolic_link); EXPECT_LE(time, info.last_modified); +#if SB_HAS_QUIRK(FILESYSTEM_COARSE_ACCESS_TIME) + EXPECT_LE(coarse_time, info.last_accessed); +#else EXPECT_LE(time, info.last_accessed); +#endif EXPECT_LE(time, info.creation_time); } }
diff --git a/src/starboard/nplb/thread_helpers.h b/src/starboard/nplb/thread_helpers.h index 0ebbd03..aa4e32d 100644 --- a/src/starboard/nplb/thread_helpers.h +++ b/src/starboard/nplb/thread_helpers.h
@@ -16,10 +16,14 @@ #define STARBOARD_NPLB_THREAD_HELPERS_H_ #include "starboard/condition_variable.h" +#include "starboard/configuration.h" #include "starboard/mutex.h" +#include "starboard/thread.h" #include "starboard/time.h" #include "starboard/types.h" +#include "testing/gtest/include/gtest/gtest.h" + namespace starboard { namespace nplb { @@ -93,6 +97,53 @@ SbTime delay_after_signal; }; +// AbstractTestThread that is a bare bones class wrapper around Starboard +// thread. Subclasses must override Run(). +class AbstractTestThread { + public: + AbstractTestThread() : thread_(kSbThreadInvalid) {} + virtual ~AbstractTestThread() {} + + // Subclasses should override the Run method. + virtual void Run() = 0; + + // Calls SbThreadCreate() with default parameters. + void Start() { + SbThreadEntryPoint entry_point = ThreadEntryPoint; + + thread_ = SbThreadCreate( + 0, // default stack_size. + kSbThreadNoPriority, // default priority. + kSbThreadNoAffinity, // default affinity. + true, // joinable. + "AbstractTestThread", + entry_point, + this); + + if (kSbThreadInvalid == thread_) { + ADD_FAILURE_AT(__FILE__, __LINE__) << "Invalid thread."; + } + return; + } + + void Join() { + if (!SbThreadJoin(thread_, NULL)) { + ADD_FAILURE_AT(__FILE__, __LINE__) << "Could not join thread."; + } + } + + private: + static void* ThreadEntryPoint(void* ptr) { + AbstractTestThread* this_ptr = static_cast<AbstractTestThread*>(ptr); + this_ptr->Run(); + return NULL; + } + + SbThread thread_; + + SB_DISALLOW_COPY_AND_ASSIGN(AbstractTestThread); +}; + } // namespace nplb } // namespace starboard
diff --git a/src/starboard/nplb/thread_join_test.cc b/src/starboard/nplb/thread_join_test.cc index 42a2b6c..8b93e32 100644 --- a/src/starboard/nplb/thread_join_test.cc +++ b/src/starboard/nplb/thread_join_test.cc
@@ -28,6 +28,45 @@ EXPECT_EQ(NULL, result); } +// Tests the expectation that SbThreadJoin() will block until +// the thread function has been run. +TEST(SbThreadLocalValueTest, ThreadJoinWaitsForFunctionRun) { + // Thread functionality needs to bind to functions. In C++11 we'd use a + // lambda function to tie everything together locally, but this + // function-scoped struct with static function emulates this functionality + // pretty well. + struct LocalStatic { + static void* ThreadEntryPoint(void* input) { + int* value = static_cast<int*>(input); + static const SbTime kSleepTime = 10*kSbTimeMillisecond; // 10 ms. + // Wait to write the value to increase likelyhood of catching + // a race condition. + SbThreadSleep(kSleepTime); + (*value)++; + return NULL; + } + }; + + // Try to increase likelyhood of a race condition by running multiple times. + for (int i = 0; i < 10; ++i) { + int num_times_thread_entry_point_run = 0; + SbThread thread = SbThreadCreate( + 0, // Signals automatic thread stack size. + kSbThreadNoPriority, // Signals default priority. + kSbThreadNoAffinity, // Signals default affinity. + true, // joinable thread. + "TestThread", + LocalStatic::ThreadEntryPoint, + &num_times_thread_entry_point_run); + + ASSERT_NE(kSbThreadInvalid, thread) << "Thread creation not successful"; + ASSERT_TRUE(SbThreadJoin(thread, NULL)); + + ASSERT_EQ(1, num_times_thread_entry_point_run) + << "Expected SbThreadJoin() to be blocked until ThreadFunction runs."; + } +} + } // namespace } // namespace nplb } // namespace starboard
diff --git a/src/starboard/nplb/thread_local_value_test.cc b/src/starboard/nplb/thread_local_value_test.cc index 43d32c4..4083684 100644 --- a/src/starboard/nplb/thread_local_value_test.cc +++ b/src/starboard/nplb/thread_local_value_test.cc
@@ -92,6 +92,73 @@ EXPECT_FALSE(my_value.destroyed); } +// Helper function that ensures that the returned key is not recycled. +SbThreadLocalKey CreateTLSKey_NoRecycle(SbThreadLocalDestructor dtor) { + SbThreadLocalKey key = SbThreadCreateLocalKey(NULL); + EXPECT_EQ(NULL, SbThreadGetLocalValue(key)); + // Some Starboard implementations may recycle the original key, so this test + // ensures that in that case it will be reset to NULL. + SbThreadSetLocalValue(key, reinterpret_cast<void*>(1)); + SbThreadDestroyLocalKey(key); + key = SbThreadCreateLocalKey(DestroyThreadLocalValue); + return key; +} + +// Tests the expectation that thread at-exit destructors don't +// run for ThreadLocal pointers that are set to NULL. +TEST(SbThreadLocalValueTest, NoDestructorsForNullValue) { + static int s_num_destructor_calls = 0; // Must be initialized to 0. + s_num_destructor_calls = 0; // Allows test to be re-run. + + // Thread functionality needs to bind to functions. In C++11 we'd use a + // lambda function to tie everything together locally, but this + // function-scoped struct with static members emulates this functionality + // pretty well. + struct LocalStatic { + // Used as a fake destructor for thread-local-storage objects in this + // test. + static void CountsDestructorCalls(void* /*value*/) { + s_num_destructor_calls++; + } + + // Sets a thread local non-NULL value, and then sets it back to NULL. + static void* ThreadEntryPoint(void* ptr) { + SbThreadLocalKey key = *static_cast<SbThreadLocalKey*>(ptr); + EXPECT_EQ(NULL, SbThreadGetLocalValue(key)); + // Set the value and then NULL it out. We expect that because the final + // value set was NULL, that the destructor attached to the thread's + // at-exit function will not run. + SbThreadSetLocalValue(key, reinterpret_cast<void*>(1)); + SbThreadSetLocalValue(key, NULL); + return NULL; + } + }; + + // Setup the thread key and bind the fake test destructor. + SbThreadLocalKey key = + CreateTLSKey_NoRecycle(LocalStatic::CountsDestructorCalls); + EXPECT_EQ(NULL, SbThreadGetLocalValue(key)); + + // Spawn the thread. + SbThread thread = SbThreadCreate( + 0, // Signals automatic thread stack size. + kSbThreadNoPriority, // Signals default priority. + kSbThreadNoAffinity, // Signals default affinity. + true, // joinable thread. + "TestThread", + LocalStatic::ThreadEntryPoint, + static_cast<void*>(&key)); + + ASSERT_NE(kSbThreadInvalid, thread) << "Thread creation not successful"; + // 2nd param is return value from ThreadEntryPoint, which is always NULL. + ASSERT_TRUE(SbThreadJoin(thread, NULL)); + + // No destructors should have run. + EXPECT_EQ(0, s_num_destructor_calls); + + SbThreadDestroyLocalKey(key); +} + TEST(SbThreadLocalValueTest, SunnyDay) { DoSunnyDayTest(true); } @@ -101,15 +168,9 @@ } TEST(SbThreadLocalValueTest, SunnyDayFreshlyCreatedValuesAreNull) { - SbThreadLocalKey key = SbThreadCreateLocalKey(NULL); + SbThreadLocalKey key = CreateTLSKey_NoRecycle(NULL); // NULL dtor. EXPECT_EQ(NULL, SbThreadGetLocalValue(key)); - // Some Starboard implementations may recycle the original key, so this test - // ensures that in that case it will be reset to NULL. - SbThreadSetLocalValue(key, reinterpret_cast<void*>(1)); - SbThreadDestroyLocalKey(key); - - key = SbThreadCreateLocalKey(NULL); EXPECT_EQ(NULL, SbThreadGetLocalValue(key)); SbThreadDestroyLocalKey(key); }
diff --git a/src/starboard/raspi/1/gyp_configuration.gypi b/src/starboard/raspi/1/gyp_configuration.gypi index 1b41d36..fbccc08 100644 --- a/src/starboard/raspi/1/gyp_configuration.gypi +++ b/src/starboard/raspi/1/gyp_configuration.gypi
@@ -17,7 +17,6 @@ 'target_arch': 'arm', 'target_os': 'linux', - 'enable_webdriver': '1', 'in_app_dial%': 0, 'sysroot%': '/', 'gl_type': 'system_gles2',
diff --git a/src/starboard/stub/configuration_public.h b/src/starboard/stub/configuration_public.h index f243cfa..90b83b7 100644 --- a/src/starboard/stub/configuration_public.h +++ b/src/starboard/stub/configuration_public.h
@@ -241,6 +241,11 @@ // The string form of SB_PATH_SEP_CHAR. #define SB_PATH_SEP_STRING ":" +// On some platforms the file system stores access times at a coarser +// granularity than other times. When this quirk is defined, we assume the +// access time is of 1 day precision. +#undef SB_HAS_QUIRK_FILESYSTEM_COARSE_ACCESS_TIME + // --- Memory Configuration -------------------------------------------------- // The memory page size, which controls the size of chunks on memory that
diff --git a/src/starboard/stub/gyp_configuration.gypi b/src/starboard/stub/gyp_configuration.gypi index 24cabf4..c1dc7d2 100644 --- a/src/starboard/stub/gyp_configuration.gypi +++ b/src/starboard/stub/gyp_configuration.gypi
@@ -16,8 +16,6 @@ 'target_arch': 'x64', 'target_os': 'linux', - 'enable_webdriver': '1', - # Use a stub rasterizer and graphical setup. 'rasterizer_type': 'stub',
diff --git a/src/starboard/thread.h b/src/starboard/thread.h index 4396c6c..2ad1006 100644 --- a/src/starboard/thread.h +++ b/src/starboard/thread.h
@@ -172,8 +172,8 @@ // Yields the currently executing thread, so another thread has a chance to run. SB_EXPORT void SbThreadYield(); -// Sleeps the currently executing thread for at least the given |duration|. A -// negative duration does nothing. +// Sleeps the currently executing thread for at least the given |duration| in +// microseconds. A negative duration does nothing. SB_EXPORT void SbThreadSleep(SbTime duration); // Gets the handle of the currently executing thread. @@ -198,7 +198,8 @@ // // When does |destructor| get called? It can only be called in the owning // thread, and let's just say thread interruption isn't viable. The destructor, -// if specified, is called on every thread's local values when the thread exits. +// if specified, is called on every thread's local values when the thread exits, +// if and only if the value in the key is non-NULL. SB_EXPORT SbThreadLocalKey SbThreadCreateLocalKey(SbThreadLocalDestructor destructor);