Bug 1008453. Add support for navigator.hardwareConcurrency. r=khuey
authorLuke Wagner <luke@mozilla.com>
Wed, 16 Mar 2016 15:41:38 -0400
changeset 289358 d2b13daf01edb6ab59d5f7ed657da40d7a71aace
parent 289357 1bbc2f4a30650d7098af495fe7e5d76b95091ce0
child 289359 476c67fdc36c72c4d7efac7bb4506625093f29b1
push id30102
push userryanvm@gmail.com
push dateSat, 19 Mar 2016 15:23:17 +0000
treeherdermozilla-central@720fb3d55e28 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs1008453
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1008453. Add support for navigator.hardwareConcurrency. r=khuey
dom/base/Navigator.cpp
dom/base/Navigator.h
dom/base/test/mochitest.ini
dom/base/test/test_navigator_hardwareConcurrency.html
dom/webidl/Navigator.webidl
dom/webidl/WorkerNavigator.webidl
dom/workers/Navigator.cpp
dom/workers/Navigator.h
dom/workers/RuntimeService.cpp
dom/workers/RuntimeService.h
dom/workers/moz.build
dom/workers/test/mochitest.ini
dom/workers/test/navigator_worker.js
dom/workers/test/test_navigator_workers_hardwareConcurrency.html
testing/web-platform/tests/workers/interfaces/WorkerUtils/navigator/007.html
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -43,16 +43,17 @@
 #include "mozilla/dom/Permissions.h"
 #include "mozilla/dom/Presentation.h"
 #include "mozilla/dom/ServiceWorkerContainer.h"
 #include "mozilla/dom/TCPSocket.h"
 #include "mozilla/dom/Telephony.h"
 #include "mozilla/dom/Voicemail.h"
 #include "mozilla/dom/TVManager.h"
 #include "mozilla/dom/VRDevice.h"
+#include "mozilla/dom/workers/RuntimeService.h"
 #include "mozilla/Hal.h"
 #include "nsISiteSpecificUserAgent.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StaticPtr.h"
 #include "Connection.h"
 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
 #include "nsGlobalWindow.h"
 #ifdef MOZ_B2G
@@ -745,16 +746,27 @@ Navigator::JavaEnabled(ErrorResult& aRv)
 
   RefreshMIMEArray();
 
   nsMimeType *mimeType = mMimeTypes->NamedItem(javaMIME);
 
   return mimeType && mimeType->GetEnabledPlugin();
 }
 
+uint64_t
+Navigator::HardwareConcurrency()
+{
+  workers::RuntimeService* rts = workers::RuntimeService::GetOrCreateService();
+  if (!rts) {
+    return 1;
+  }
+
+  return rts->ClampedHardwareConcurrency();
+}
+
 void
 Navigator::RefreshMIMEArray()
 {
   if (mMimeTypes) {
     mMimeTypes->Refresh();
   }
 }
 
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -214,16 +214,17 @@ public:
   // The XPCOM GetProductSub is OK
   bool CookieEnabled();
   void GetBuildID(nsString& aBuildID, ErrorResult& aRv)
   {
     aRv = GetBuildID(aBuildID);
   }
   PowerManager* GetMozPower(ErrorResult& aRv);
   bool JavaEnabled(ErrorResult& aRv);
+  uint64_t HardwareConcurrency();
   bool TaintEnabled()
   {
     return false;
   }
   void AddIdleObserver(MozIdleObserver& aObserver, ErrorResult& aRv);
   void RemoveIdleObserver(MozIdleObserver& aObserver, ErrorResult& aRv);
   already_AddRefed<WakeLock> RequestWakeLock(const nsAString &aTopic,
                                              ErrorResult& aRv);
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -321,16 +321,17 @@ skip-if = buildapp == 'mulet' || buildap
 [test_getFeature_without_perm.html]
 [test_hasFeature.html]
 [test_history_document_open.html]
 [test_history_state_null.html]
 [test_Image_constructor.html]
 [test_innersize_scrollport.html]
 [test_messagemanager_targetchain.html]
 [test_named_frames.html]
+[test_navigator_hardwareConcurrency.html]
 [test_navigator_resolve_identity.html]
 [test_navigator_language.html]
 [test_noAudioNotification.html]
 tags = audiochannel
 [test_noAudioNotificationOnMutedElement.html]
 tags = audiochannel
 [test_noAudioNotificationOnMutedOrVolume0Element.html]
 tags = audiochannel
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_navigator_hardwareConcurrency.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Navigator.hardwareConcurrency</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  var x = navigator.hardwareConcurrency;
+  is(typeof x, "number", "hardwareConcurrency should be a number.");
+  ok(x > 0, "hardwareConcurrency should be greater than 0.");
+
+  </script>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -7,16 +7,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#the-navigator-object
  * http://www.w3.org/TR/tracking-dnt/
  * http://www.w3.org/TR/geolocation-API/#geolocation_interface
  * http://www.w3.org/TR/battery-status/#navigatorbattery-interface
  * http://www.w3.org/TR/vibration/#vibration-interface
  * http://www.w3.org/2012/sysapps/runtime/#extension-to-the-navigator-interface-1
  * https://dvcs.w3.org/hg/gamepad/raw-file/default/gamepad.html#navigator-interface-extension
  * http://www.w3.org/TR/beacon/#sec-beacon-method
+ * https://html.spec.whatwg.org/#navigatorconcurrenthardware
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-navigator-object
 [HeaderFile="Navigator.h", NeedResolve]
@@ -24,16 +25,17 @@ interface Navigator {
   // objects implementing this interface also implement the interfaces given below
 };
 Navigator implements NavigatorID;
 Navigator implements NavigatorLanguage;
 Navigator implements NavigatorOnLine;
 Navigator implements NavigatorContentUtils;
 Navigator implements NavigatorStorageUtils;
 Navigator implements NavigatorFeatures;
+Navigator implements NavigatorConcurrentHardware;
 
 [NoInterfaceObject, Exposed=(Window,Worker)]
 interface NavigatorID {
   // WebKit/Blink/Trident/Presto support this (hardcoded "Mozilla").
   [Constant, Cached]
   readonly attribute DOMString appCodeName; // constant "Mozilla"
   [Constant, Cached]
   readonly attribute DOMString appName;
@@ -471,8 +473,13 @@ partial interface Navigator {
   [Throws, NewObject, Pref="dom.mozPay.enabled"]
   // The 'jwts' parameter can be either a single DOMString or an array of
   // DOMStrings. In both cases, it represents the base64url encoded and
   // digitally signed payment information. Each payment provider should
   // define its supported JWT format.
   DOMRequest mozPay(any jwts);
 };
 #endif
+
+[NoInterfaceObject, Exposed=(Window,Worker)]
+interface NavigatorConcurrentHardware {
+  readonly attribute unsigned long long hardwareConcurrency;
+};
--- a/dom/webidl/WorkerNavigator.webidl
+++ b/dom/webidl/WorkerNavigator.webidl
@@ -6,8 +6,9 @@
 [Exposed=Worker]
 interface WorkerNavigator {
 };
 
 WorkerNavigator implements NavigatorID;
 WorkerNavigator implements NavigatorLanguage;
 WorkerNavigator implements NavigatorOnLine;
 WorkerNavigator implements NavigatorDataStore;
+WorkerNavigator implements NavigatorConcurrentHardware;
--- a/dom/workers/Navigator.cpp
+++ b/dom/workers/Navigator.cpp
@@ -394,9 +394,18 @@ WorkerNavigator::GetUserAgent(nsString& 
   MOZ_ASSERT(workerPrivate);
 
   RefPtr<GetUserAgentRunnable> runnable =
     new GetUserAgentRunnable(workerPrivate, aUserAgent);
 
   runnable->Dispatch(aRv);
 }
 
+uint64_t
+WorkerNavigator::HardwareConcurrency() const
+{
+  RuntimeService* rts = RuntimeService::GetService();
+  MOZ_ASSERT(rts);
+
+  return rts->ClampedHardwareConcurrency();
+}
+
 END_WORKERS_NAMESPACE
--- a/dom/workers/Navigator.h
+++ b/dom/workers/Navigator.h
@@ -107,13 +107,15 @@ public:
   }
 
   void SetLanguages(const nsTArray<nsString>& aLanguages);
 
   already_AddRefed<Promise> GetDataStores(JSContext* aCx,
                                           const nsAString& aName,
                                           const nsAString& aOwner,
                                           ErrorResult& aRv);
+
+  uint64_t HardwareConcurrency() const;
 };
 
 END_WORKERS_NAMESPACE
 
 #endif // mozilla_dom_workers_navigator_h__
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -21,16 +21,17 @@
 #include "nsIURI.h"
 #include "nsPIDOMWindow.h"
 
 #include <algorithm>
 #include "BackgroundChild.h"
 #include "GeckoProfiler.h"
 #include "jsfriendapi.h"
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/Atomics.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/AtomList.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ErrorEventBinding.h"
 #include "mozilla/dom/EventTargetBinding.h"
@@ -63,16 +64,17 @@
 
 #include "Principal.h"
 #include "SharedWorker.h"
 #include "WorkerDebuggerManager.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 #include "WorkerThread.h"
+#include "prsystem.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
 USING_WORKERS_NAMESPACE
 
 using mozilla::MutexAutoLock;
@@ -2444,16 +2446,38 @@ RuntimeService::CycleCollectAllWorkers()
 }
 
 void
 RuntimeService::SendOfflineStatusChangeEventToAllWorkers(bool aIsOffline)
 {
   BROADCAST_ALL_WORKERS(OfflineStatusChangeEvent, aIsOffline);
 }
 
+uint32_t
+RuntimeService::ClampedHardwareConcurrency() const
+{
+  // This needs to be atomic, because multiple workers, and even mainthread,
+  // could race to initialize it at once.
+  static Atomic<uint32_t> clampedHardwareConcurrency;
+
+  // No need to loop here: if compareExchange fails, that just means that some
+  // other worker has initialized numberOfProcessors, so we're good to go.
+  if (!clampedHardwareConcurrency) {
+    int32_t numberOfProcessors = PR_GetNumberOfProcessors();
+    if (numberOfProcessors <= 0) {
+      numberOfProcessors = 1; // Must be one there somewhere
+    }
+    uint32_t clampedValue = std::min(uint32_t(numberOfProcessors),
+                                     gMaxWorkersPerDomain);
+    clampedHardwareConcurrency.compareExchange(0, clampedValue);
+  }
+
+  return clampedHardwareConcurrency;
+}
+
 // nsISupports
 NS_IMPL_ISUPPORTS(RuntimeService, nsIObserver)
 
 // nsIObserver
 NS_IMETHODIMP
 RuntimeService::Observe(nsISupports* aSubject, const char* aTopic,
                         const char16_t* aData)
 {
--- a/dom/workers/RuntimeService.h
+++ b/dom/workers/RuntimeService.h
@@ -244,16 +244,18 @@ public:
   GarbageCollectAllWorkers(bool aShrinking);
 
   void
   CycleCollectAllWorkers();
 
   void
   SendOfflineStatusChangeEventToAllWorkers(bool aIsOffline);
 
+  uint32_t ClampedHardwareConcurrency() const;
+
 private:
   RuntimeService();
   ~RuntimeService();
 
   nsresult
   Init();
 
   void
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -16,16 +16,17 @@ EXPORTS.mozilla.dom += [
     'WorkerLocation.h',
     'WorkerPrefs.h',
     'WorkerPrivate.h',
     'WorkerRunnable.h',
     'WorkerScope.h',
 ]
 
 EXPORTS.mozilla.dom.workers += [
+    'RuntimeService.h',
     'ServiceWorkerManager.h',
     'WorkerDebuggerManager.h',
     'Workers.h',
 ]
 
 # Stuff needed for the bindings, not really public though.
 EXPORTS.mozilla.dom.workers.bindings += [
     'DataStore.h',
--- a/dom/workers/test/mochitest.ini
+++ b/dom/workers/test/mochitest.ini
@@ -253,8 +253,9 @@ skip-if = buildapp == 'b2g'
 [test_xhr_timeout.html]
 skip-if = (os == "win") || (os == "mac") || toolkit == 'android' #bug 798220
 [test_xhrAbort.html]
 [test_referrer.html]
 [test_importScripts_3rdparty.html]
 [test_sharedWorker_ports.html]
 [test_sharedWorker_lifetime.html]
 [test_fileReader.html]
+[test_navigator_workers_hardwareConcurrency.html]
--- a/dom/workers/test/navigator_worker.js
+++ b/dom/workers/test/navigator_worker.js
@@ -10,16 +10,17 @@ var supportedProps = [
   "appVersion",
   { name: "getDataStores", b2g: true },
   "platform",
   "product",
   "userAgent",
   "onLine",
   "language",
   "languages",
+  "hardwareConcurrency",
 ];
 
 self.onmessage = function(event) {
   if (!event || !event.data) {
     return;
   }
 
   startTest(event.data.isB2G);
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_navigator_workers_hardwareConcurrency.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Navigator.hardwareConcurrency</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  SimpleTest.waitForExplicitFinish();
+  var script = "postMessage(navigator.hardwareConcurrency)";
+  var url = URL.createObjectURL(new Blob([script]));
+  var w = new Worker(url);
+  w.onmessage = function(e) {
+    var x = e.data;
+    is(typeof x, "number", "hardwareConcurrency should be a number.");
+    ok(x > 0, "hardwareConcurrency should be greater than 0.");
+    SimpleTest.finish();
+  }
+  </script>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/testing/web-platform/tests/workers/interfaces/WorkerUtils/navigator/007.html
+++ b/testing/web-platform/tests/workers/interfaces/WorkerUtils/navigator/007.html
@@ -1,13 +1,15 @@
 <!--
 var log = [];
+var neverEncounteredValue = "This is not the value you are looking for.";
 for (x in navigator) {
-  navigator[x] = 1; // this should silently fail and not throw per webidl
-  if (navigator[x] === 1)
+  // this should silently fail and not throw per webidl
+  navigator[x] = neverEncounteredValue;
+  if (navigator[x] === neverEncounteredValue)
     log.push(x);
 }
 postMessage(log.join(', '));
 /*
 -->
 <!doctype html>
 <title>readonlyness of members of Navigator</title>
 <script src="/resources/testharness.js"></script>