Bug 1008453. Add support for navigator.hardwareConcurrency. r=khuey
☠☠ backed out by cc6eca49bf0f ☠ ☠
authorLuke Wagner <luke@mozilla.com>
Wed, 16 Mar 2016 15:41:38 -0400
changeset 342015 a7040abce944a562d5a1cc1cca298b9da71ef4c5
parent 342014 fbc336fb47f9a138762948e6262a3b0113b5c702
child 342016 238a18a30a15c07db6b71c8586dcd7c434aaa493
push id13342
push userbmo:james@hoppipolla.co.uk
push dateFri, 18 Mar 2016 09:55:58 +0000
reviewerskhuey
bugs1008453
milestone48.0a1
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
--- 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;
@@ -2447,16 +2449,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>