From 705f2785972a128acb95ca4dbf056558bd49f368 Mon Sep 17 00:00:00 2001 From: Gaia Pushbot Date: Mon, 30 Sep 2013 06:35:25 -0700 Subject: [PATCH 01/23] Bumping gaia.json for 2 gaia-central revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/0f8930a9ed9c Author: Amir Nissim Desc: Merge pull request #12533 from EverythingMe/912386-apps-hold-click [Bug 912386] fix long-tap on app won't prevent normal click [r=crdlc] ======== https://hg.mozilla.org/integration/gaia-central/rev/bde183b340cd Author: Evyatar Amitay Desc: [Bug 912386] fix long-tap on app won't prevent normal click [r=crdlc] --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 54e4f99e3e4e..ab3b097f8659 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "290fc4628fbf4de6c75fd2ea796b1065a6e50043", + "revision": "0f8930a9ed9c857a807020e3a7bc87a06f7f5d9a", "repo_path": "/integration/gaia-central" } From 481ac47355c6167a94fbe81abc014bccbd3764d2 Mon Sep 17 00:00:00 2001 From: Diego Wilson Date: Fri, 27 Sep 2013 13:37:19 -0700 Subject: [PATCH 02/23] Bug 901978 - Query HWC to check for "R/B swap" and Color layer support. r=ncameron --- widget/gonk/HwcComposer2D.cpp | 17 +++++++++++++++++ widget/gonk/HwcComposer2D.h | 1 + 2 files changed, 18 insertions(+) diff --git a/widget/gonk/HwcComposer2D.cpp b/widget/gonk/HwcComposer2D.cpp index d8cb4cf330cb..15aab226f08c 100644 --- a/widget/gonk/HwcComposer2D.cpp +++ b/widget/gonk/HwcComposer2D.cpp @@ -61,6 +61,8 @@ HwcComposer2D::HwcComposer2D() : mMaxLayerCount(0) , mList(nullptr) , mHwc(nullptr) + , mColorFill(false) + , mRBSwapSupport(false) { } @@ -84,9 +86,20 @@ HwcComposer2D::Init(hwc_display_t dpy, hwc_surface_t sur) mozilla::Framebuffer::GetSize(&screenSize); mScreenRect = nsIntRect(nsIntPoint(0, 0), screenSize); +#if ANDROID_VERSION >= 18 + int supported = 0; + if (mHwc->query(mHwc, HwcUtils::HWC_COLOR_FILL, &supported) == NO_ERROR) { + mColorFill = supported ? true : false; + } + if (mHwc->query(mHwc, HwcUtils::HWC_FORMAT_RB_SWAP, &supported) == NO_ERROR) { + mRBSwapSupport = supported ? true : false; + } +#else char propValue[PROPERTY_VALUE_MAX]; property_get("ro.display.colorfill", propValue, "0"); mColorFill = (atoi(propValue) == 1) ? true : false; + mRBSwapSupport = true; +#endif mDpy = dpy; mSur = sur; @@ -270,6 +283,10 @@ HwcComposer2D::PrepareLayerList(Layer* aLayer, if (!fillColor) { if (state.FormatRBSwapped()) { + if (!mRBSwapSupport) { + LOGD("No R/B swap support in H/W Composer"); + return false; + } hwcLayer.flags |= HwcUtils::HWC_FORMAT_RB_SWAP; } diff --git a/widget/gonk/HwcComposer2D.h b/widget/gonk/HwcComposer2D.h index e0bc091c7fec..cfab6929a2af 100644 --- a/widget/gonk/HwcComposer2D.h +++ b/widget/gonk/HwcComposer2D.h @@ -75,6 +75,7 @@ private: nsIntRect mScreenRect; int mMaxLayerCount; bool mColorFill; + bool mRBSwapSupport; //Holds all the dynamically allocated RectVectors needed //to render the current frame std::list mVisibleRegions; From 2345a994cb581a1de1b81b0452d13044b409fb58 Mon Sep 17 00:00:00 2001 From: Viral Wang Date: Mon, 30 Sep 2013 10:40:41 -0400 Subject: [PATCH 03/23] Bug 908058 - Add orientation keyword 'default' to express normal orientation. r=mounir, r=blassey --- dom/base/ScreenOrientation.h | 3 +++ dom/base/nsScreen.cpp | 2 ++ hal/android/AndroidHal.cpp | 1 + mobile/android/base/GeckoScreenOrientationListener.java | 4 ++++ widget/gonk/OrientationObserver.cpp | 9 ++++++++- 5 files changed, 18 insertions(+), 1 deletion(-) diff --git a/dom/base/ScreenOrientation.h b/dom/base/ScreenOrientation.h index a5926b31c833..88425afeba18 100644 --- a/dom/base/ScreenOrientation.h +++ b/dom/base/ScreenOrientation.h @@ -17,6 +17,9 @@ static const ScreenOrientation eScreenOrientation_PortraitPrimary = 1u << 0; static const ScreenOrientation eScreenOrientation_PortraitSecondary = 1u << 1; static const ScreenOrientation eScreenOrientation_LandscapePrimary = 1u << 2; static const ScreenOrientation eScreenOrientation_LandscapeSecondary = 1u << 3; +//eScreenOrientation_Default will use the natural orientation for the deivce, +//it could be PortraitPrimary or LandscapePrimary depends on display resolution +static const ScreenOrientation eScreenOrientation_Default = 1u << 4; } // namespace dom } // namespace mozilla diff --git a/dom/base/nsScreen.cpp b/dom/base/nsScreen.cpp index c16e615d764b..705135861523 100644 --- a/dom/base/nsScreen.cpp +++ b/dom/base/nsScreen.cpp @@ -331,6 +331,8 @@ nsScreen::MozLockOrientation(const Sequence& aOrientations, orientation |= eScreenOrientation_LandscapePrimary; } else if (item.EqualsLiteral("landscape-secondary")) { orientation |= eScreenOrientation_LandscapeSecondary; + } else if (item.EqualsLiteral("default")) { + orientation |= eScreenOrientation_Default; } else { // If we don't recognize the token, we should just return 'false' // without throwing. diff --git a/hal/android/AndroidHal.cpp b/hal/android/AndroidHal.cpp index 5ea5d5165dad..932fd5f49a71 100644 --- a/hal/android/AndroidHal.cpp +++ b/hal/android/AndroidHal.cpp @@ -194,6 +194,7 @@ LockScreenOrientation(const ScreenOrientation& aOrientation) case eScreenOrientation_LandscapePrimary: case eScreenOrientation_LandscapeSecondary: case eScreenOrientation_LandscapePrimary | eScreenOrientation_LandscapeSecondary: + case eScreenOrientation_Default: bridge->LockScreenOrientation(aOrientation); return true; default: diff --git a/mobile/android/base/GeckoScreenOrientationListener.java b/mobile/android/base/GeckoScreenOrientationListener.java index 6af26f3fcc77..1c4fcd8fc3ed 100644 --- a/mobile/android/base/GeckoScreenOrientationListener.java +++ b/mobile/android/base/GeckoScreenOrientationListener.java @@ -37,6 +37,7 @@ public class GeckoScreenOrientationListener { static public final short eScreenOrientation_PortraitSecondary = 2; // PR_BIT(1) static public final short eScreenOrientation_LandscapePrimary = 4; // PR_BIT(2) static public final short eScreenOrientation_LandscapeSecondary = 8; // PR_BIT(3) + static public final short eScreenOrientation_Default = 16;// PR_BIT(4) static private final short DEFAULT_ORIENTATION = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -199,6 +200,9 @@ public class GeckoScreenOrientationListener { case eScreenOrientation_LandscapePrimary | eScreenOrientation_LandscapeSecondary: orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; break; + case eScreenOrientation_Default: + orientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; + break; default: Log.e(LOGTAG, "Unexpected value received! (" + aOrientation + ")"); return; diff --git a/widget/gonk/OrientationObserver.cpp b/widget/gonk/OrientationObserver.cpp index bc0a8f266112..dd70293fa68f 100644 --- a/widget/gonk/OrientationObserver.cpp +++ b/widget/gonk/OrientationObserver.cpp @@ -264,7 +264,14 @@ OrientationObserver::LockScreenOrientation(ScreenOrientation aOrientation) MOZ_ASSERT(aOrientation | (eScreenOrientation_PortraitPrimary | eScreenOrientation_PortraitSecondary | eScreenOrientation_LandscapePrimary | - eScreenOrientation_LandscapeSecondary)); + eScreenOrientation_LandscapeSecondary | + eScreenOrientation_Default)); + + if (aOrientation == eScreenOrientation_Default) { + aOrientation = (sOrientationOffset == sDefaultPortrait) ? + eScreenOrientation_PortraitPrimary : + eScreenOrientation_LandscapePrimary; + } // If there are multiple orientations allowed, we should enable the // auto-rotation. From e90d1c3145fa184524f8cf7a77047a8ba3da130c Mon Sep 17 00:00:00 2001 From: Martijn Wargers Date: Mon, 30 Sep 2013 13:53:40 +0200 Subject: [PATCH 04/23] Bug 915437 - "System JS : ERROR chrome://browser/content/forms.js:942 TypeError: Argument 1 of Range.setEnd is not an object." on all B2G mochitest-9 runs. r=yxl --- b2g/chrome/content/forms.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/chrome/content/forms.js b/b2g/chrome/content/forms.js index 4d9c86453d86..373cf11dc885 100644 --- a/b2g/chrome/content/forms.js +++ b/b2g/chrome/content/forms.js @@ -925,7 +925,7 @@ function getSelectionRange(element) { // Get the selection range of contenteditable elements let win = element.ownerDocument.defaultView; let sel = win.getSelection(); - if (sel) { + if (sel && sel.rangeCount > 0) { start = getContentEditableSelectionStart(element, sel); end = start + getContentEditableSelectionLength(element, sel); } else { From 7e55df38e5b07c41f95c077dcbf4f3666db4a4b2 Mon Sep 17 00:00:00 2001 From: Ethan Tseng Date: Mon, 30 Sep 2013 16:31:12 +0800 Subject: [PATCH 05/23] Bug 915529 - Add null pointer checking in stringToIP() of systemlibs.js. r=vchang --- dom/system/gonk/systemlibs.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dom/system/gonk/systemlibs.js b/dom/system/gonk/systemlibs.js index df917b924f25..00e90acc6359 100644 --- a/dom/system/gonk/systemlibs.js +++ b/dom/system/gonk/systemlibs.js @@ -448,12 +448,15 @@ this.netHelpers = { /** * Convert string representation of an IP address to the integer - * representation. + * representation (network byte order). * * @param string * String containing the IP address. */ stringToIP: function stringToIP(string) { + if (!string) { + return null; + } let ip = 0; let start, end = -1; for (let i = 0; i < 4; i++) { From a7b122fc0af92338e486df6fbd793b6f2c2af116 Mon Sep 17 00:00:00 2001 From: Yuan Xulei Date: Thu, 26 Sep 2013 15:10:56 +0800 Subject: [PATCH 06/23] Bug 920831 - MozInputMethod's init should only return undefined. r=bz --- dom/base/Navigator.cpp | 10 ++++++++++ dom/base/Navigator.h | 2 ++ dom/inputmethod/MozKeyboard.js | 20 ------------------- .../mochitest/general/test_interfaces.html | 1 - dom/webidl/InputMethod.webidl | 2 +- 5 files changed, 13 insertions(+), 22 deletions(-) diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index 2830eba137b1..24a5dc575c2d 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -1783,6 +1783,16 @@ bool Navigator::HasPushNotificationsSupport(JSContext* /* unused */, return win && Preferences::GetBool("services.push.enabled", false) && CheckPermission(win, "push"); } +/* static */ +bool Navigator::HasInputMethodSupport(JSContext* /* unused */, + JSObject* aGlobal) +{ + nsCOMPtr win = GetWindowFromGlobal(aGlobal); + return Preferences::GetBool("dom.mozInputMethod.testing", false) || + (Preferences::GetBool("dom.mozInputMethod.enabled", false) && + win && CheckPermission(win, "keyboard")); +} + /* static */ already_AddRefed Navigator::GetWindowFromGlobal(JSObject* aGlobal) diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h index 5c22f3fa2b5d..bbd8e85fbe93 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h @@ -300,6 +300,8 @@ public: static bool HasPushNotificationsSupport(JSContext* /* unused */, JSObject* aGlobal); + static bool HasInputMethodSupport(JSContext* /* unused */, JSObject* aGlobal); + nsPIDOMWindow* GetParentObject() const { return GetWindow(); diff --git a/dom/inputmethod/MozKeyboard.js b/dom/inputmethod/MozKeyboard.js index 0462944a5356..c46fbf93b688 100644 --- a/dom/inputmethod/MozKeyboard.js +++ b/dom/inputmethod/MozKeyboard.js @@ -199,8 +199,6 @@ MozKeyboard.prototype = { } }; -const TESTING_ENABLED_PREF = "dom.mozInputMethod.testing"; - /* * A WeakMap to map input method iframe window to its active status. */ @@ -317,24 +315,6 @@ MozInputMethod.prototype = { }), init: function mozInputMethodInit(win) { - // Check if we're in testing mode. - let isTesting = false; - try { - isTesting = Services.prefs.getBoolPref(TESTING_ENABLED_PREF); - } catch (e) {} - - // Don't bypass the permission check if not in testing mode. - if (!isTesting) { - let principal = win.document.nodePrincipal; - let perm = Services.perms - .testExactPermissionFromPrincipal(principal, "keyboard"); - if (perm != Ci.nsIPermissionManager.ALLOW_ACTION) { - dump("No permission to use the keyboard API for " + - principal.origin + "\n"); - return; - } - } - this._window = win; this._mgmt = new MozInputMethodManager(win); this.innerWindowID = win.QueryInterface(Ci.nsIInterfaceRequestor) diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index d7b52c041178..99a29b7024f4 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -324,7 +324,6 @@ var interfaceNamesInGlobalScope = {name: "MozEmergencyCbModeEvent", b2g: true}, {name: "MozIccManager", b2g: true}, {name: "MozInputContext", b2g: true}, - {name: "MozInputMethod", b2g: true}, {name: "MozInputMethodManager", b2g: true}, "MozMmsEvent", "MozMmsMessage", diff --git a/dom/webidl/InputMethod.webidl b/dom/webidl/InputMethod.webidl index c28b3d5e0304..0f3befa1d946 100644 --- a/dom/webidl/InputMethod.webidl +++ b/dom/webidl/InputMethod.webidl @@ -6,7 +6,7 @@ [JSImplementation="@mozilla.org/b2g-inputmethod;1", NavigatorProperty="mozInputMethod", - Pref="dom.mozInputMethod.enabled"] + Func="Navigator::HasInputMethodSupport"] interface MozInputMethod : EventTarget { // Input Method Manager contain a few global methods expose to apps readonly attribute MozInputMethodManager mgmt; From 5046944ad083098116e0dfe65b2b3f291af47866 Mon Sep 17 00:00:00 2001 From: Shih-Chiang Chien Date: Sat, 28 Sep 2013 09:12:39 +0800 Subject: [PATCH 07/23] Bug 918523 - Prevent rec_queue overrun. r=jesup --- .../audio_device/audio_device_opensles.cc | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_opensles.cc b/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_opensles.cc index 64fc99608e0f..85d208527990 100644 --- a/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_opensles.cc +++ b/media/webrtc/trunk/webrtc/modules/audio_device/audio_device_opensles.cc @@ -1443,21 +1443,27 @@ void AudioDeviceAndroidOpenSLES::RecorderSimpleBufferQueueCallbackHandler( } audio = rec_queue_.front(); rec_queue_.pop(); - rec_voe_audio_queue_.push(audio); - rec_timer_.Set(); // wake up record thread to process it + // prevent rec_queue being overrun + if (rec_voe_audio_queue_.size() < N_REC_QUEUE_BUFFERS) { + rec_voe_audio_queue_.push(audio); + rec_timer_.Set(); // wake up record thread to process it - if (rec_voe_ready_queue_.size() <= 0) { - // Log Error. - rec_error_ = 1; - WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, - " Audio Rec thread buffers underrun"); - // This isn't good, but continuing (as it used to) is even worse. - // Worst case we end up with buffers building up at the other end or - // starved. To be fixed in full rewrite from upstream. - return; + if (rec_voe_ready_queue_.size() <= 0) { + // Log Error. + rec_error_ = 1; + WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, + " Audio Rec thread buffers underrun"); + // This isn't good, but continuing (as it used to) is even worse. + // Worst case we end up with buffers building up at the other end or + // starved. To be fixed in full rewrite from upstream. + return; + } else { + audio = rec_voe_ready_queue_.front(); + rec_voe_ready_queue_.pop(); + } } else { - audio = rec_voe_ready_queue_.front(); - rec_voe_ready_queue_.pop(); + // the audio frame will be recycled instead of sending to rec thread. + rec_timer_.Set(); // try wake up record thread } } From 6e45f31a8ea28b92ceec9c010339171ec4c66d1c Mon Sep 17 00:00:00 2001 From: Martijn Wargers Date: Mon, 30 Sep 2013 15:05:21 +0200 Subject: [PATCH 08/23] Bug 914890 - While running a standalone mochitest, prefs and permissions should also reset after the mochitest run. r=jmaher --- testing/mochitest/tests/SimpleTest/SimpleTest.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/testing/mochitest/tests/SimpleTest/SimpleTest.js b/testing/mochitest/tests/SimpleTest/SimpleTest.js index a215b4a0b18e..a6c53a315867 100644 --- a/testing/mochitest/tests/SimpleTest/SimpleTest.js +++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js @@ -779,7 +779,11 @@ SimpleTest.finish = function () { /* We're running in an iframe, and the parent has a TestRunner */ parentRunner.testFinished(SimpleTest._tests); } else { - SimpleTest.showReport(); + SpecialPowers.flushPermissions(function () { + SpecialPowers.flushPrefEnv(function() { + SimpleTest.showReport(); + }); + }); } }; From 1833a2971ee41d97fb13a16d4175a199a1f69db6 Mon Sep 17 00:00:00 2001 From: Gaia Pushbot Date: Mon, 30 Sep 2013 07:45:26 -0700 Subject: [PATCH 09/23] Bumping gaia.json for 4 gaia-central revision(s) a=gaia-bump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ======== https://hg.mozilla.org/integration/gaia-central/rev/3695bad19b70 Author: Ben Kelly Desc: Merge pull request #12526 from wanderview/contacts-loadchunk-no-search Bug 921802: Do not require search while loading contacts. r=jmcf ======== https://hg.mozilla.org/integration/gaia-central/rev/7941ab060beb Author: Ben Kelly Desc: Bug 921802: Do not require search while loading contacts ======== https://hg.mozilla.org/integration/gaia-central/rev/af518ee97e6b Author: Germán Toro del Valle Desc: Merge pull request #12506 from gtorodelvalle/contacts-dialer-916010-duplicate-contact-details-reloaded Bug 916010 - [Contacts] Follow up for visual polish of bug 902399 ======== https://hg.mozilla.org/integration/gaia-central/rev/e23571af8b49 Author: Germán Toro del Valle Desc: Bug 916010 - [Contacts] Follow up for visual polish of bug 902399. --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index ab3b097f8659..5b469a472c78 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "0f8930a9ed9c857a807020e3a7bc87a06f7f5d9a", + "revision": "3695bad19b702266bced572b3af9d24093c56b66", "repo_path": "/integration/gaia-central" } From 16a3a7a851d61802e656a42ed9177e6fc265ec78 Mon Sep 17 00:00:00 2001 From: Gaia Pushbot Date: Mon, 30 Sep 2013 08:00:23 -0700 Subject: [PATCH 10/23] Bumping gaia.json for 2 gaia-central revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/77a0a1d62fb6 Author: Amir Nissim Desc: Merge pull request #12531 from EverythingMe/910355-collection-offline-icon Bug 910355 - [e.me][bug] Collection icon auto update ======== https://hg.mozilla.org/integration/gaia-central/rev/d66d11ee9fdc Author: Amir Nissim Desc: Bug 910355 - [e.me][bug] Collection icon auto update --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 5b469a472c78..5d81fa70d61d 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "3695bad19b702266bced572b3af9d24093c56b66", + "revision": "77a0a1d62fb6d2e24e1767b80dd1df6696add4eb", "repo_path": "/integration/gaia-central" } From 195a54903234328567e4a147058b90198be602d1 Mon Sep 17 00:00:00 2001 From: Gaia Pushbot Date: Mon, 30 Sep 2013 08:10:25 -0700 Subject: [PATCH 11/23] Bumping gaia.json for 2 gaia-central revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/cd1c67863eb1 Author: Francisco Jordano Desc: Merge pull request #12474 from jmcanterafonseca/disable_phone Bug 920925 - [Contacts] Cannot recover when trying to add a contact only... ======== https://hg.mozilla.org/integration/gaia-central/rev/95009d01a6dd Author: Jose M. Cantera Desc: Bug 920925 - [Contacts] Cannot recover when trying to add a contact only with carrier --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 5d81fa70d61d..808780e3e57d 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "77a0a1d62fb6d2e24e1767b80dd1df6696add4eb", + "revision": "cd1c67863eb1285b502bfb7d71ac7964447cfc57", "repo_path": "/integration/gaia-central" } From 7828ac111848361599ba495c4b6054b0333ce48e Mon Sep 17 00:00:00 2001 From: Gaia Pushbot Date: Mon, 30 Sep 2013 08:35:24 -0700 Subject: [PATCH 12/23] Bumping gaia.json for 5 gaia-central revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/de7396523254 Author: Zac Desc: Merge pull request #12550 from zacc/bug922062 Bug 922062. gaia-ui-tests: Skip cleanUp steps that are not necessary whe... ======== https://hg.mozilla.org/integration/gaia-central/rev/8775e70114fd Author: Zac Campbell Desc: Bug 922062. gaia-ui-tests: Skip cleanUp steps that are not necessary when --restart is passed in. r=bebe/davehunt ======== https://hg.mozilla.org/integration/gaia-central/rev/82214cc7c87e Author: Dave Hunt Desc: Bug 921827 - Follow-up fix to use properties for the media type lists. r=bebe ======== https://hg.mozilla.org/integration/gaia-central/rev/47c82c1e38a5 Author: Francisco Jordano Desc: Merge pull request #12548 from jmcanterafonseca/contacts_types_bug Bug 922037 - Contacts email, phone or address type cannot be selected ======== https://hg.mozilla.org/integration/gaia-central/rev/e3f3c67d40b0 Author: Jose M. Cantera Desc: Bug 922037 - Contacts email, phone or address type cannot be selected --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 808780e3e57d..8f24d8c30d91 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "cd1c67863eb1285b502bfb7d71ac7964447cfc57", + "revision": "de7396523254bf76274ab7f7f5970100b39ee4a5", "repo_path": "/integration/gaia-central" } From 52df1fd95780960409bf1e2797841302216e0c25 Mon Sep 17 00:00:00 2001 From: "Thinker Lee ext:(%2C%20Cervantes%20Yu%20%3Ccyu%40mozilla.com%3E)" Date: Tue, 7 May 2013 14:25:13 +0800 Subject: [PATCH 13/23] Bug 771765 - Support template content process, part 1: the Nuwa API and low-level wrappers. r=khuey Support of zygote-like process forking is done as: * The b2g chrome process forks a child process that will be the template for forking other content processes. * The template process starts running to some point and freeze. * When ready, the chrome process starts requesting the template to fork content proceses. * The content processes forked from the template process recovers its states including the threads, file descriptors for epoll, signaling and IPC. Recovery of the process states are done by wrapping some system and pthread calls to record the states in the template process and recover in the forked content process. --- b2g/confvars.sh | 2 + configure.in | 9 + mozglue/build/Makefile.in | 3 + mozglue/build/Nuwa.cpp | 1746 +++++++++++++++++++++++++++++++++++++ mozglue/build/Nuwa.h | 171 ++++ mozglue/build/moz.build | 5 + 6 files changed, 1936 insertions(+) create mode 100644 mozglue/build/Nuwa.cpp create mode 100644 mozglue/build/Nuwa.h diff --git a/b2g/confvars.sh b/b2g/confvars.sh index 31a11c6f7e6d..de29732aa196 100644 --- a/b2g/confvars.sh +++ b/b2g/confvars.sh @@ -54,4 +54,6 @@ MOZ_PAY=1 MOZ_TOOLKIT_SEARCH= MOZ_PLACES= MOZ_B2G=1 + +#MOZ_NUWA_PROCESS=1 MOZ_FOLD_LIBS=1 diff --git a/configure.in b/configure.in index 18730281e0a3..d48d84815afc 100644 --- a/configure.in +++ b/configure.in @@ -7087,6 +7087,9 @@ if test "$OS_TARGET" = Android; then WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=fork,--wrap=pthread_atfork,--wrap=raise" WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=memccpy,--wrap=memchr,--wrap=memrchr,--wrap=memcmp,--wrap=memcpy,--wrap=memmove,--wrap=memset,--wrap=memmem,--wrap=memswap,--wrap=index,--wrap=strchr,--wrap=strrchr,--wrap=strlen,--wrap=strcmp,--wrap=strcpy,--wrap=strcat,--wrap=strcasecmp,--wrap=strncasecmp,--wrap=strstr,--wrap=strcasestr,--wrap=strtok,--wrap=strtok_r,--wrap=strerror,--wrap=strerror_r,--wrap=strnlen,--wrap=strncat,--wrap=strncmp,--wrap=strncpy,--wrap=strlcat,--wrap=strlcpy,--wrap=strcspn,--wrap=strpbrk,--wrap=strsep,--wrap=strspn,--wrap=strcoll,--wrap=strxfrm" fi + if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then + WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=__pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2" + fi fi dnl ======================================================== @@ -8593,6 +8596,12 @@ AC_SUBST(MOZ_BZ2_LIBS) AC_SUBST(MOZ_PNG_CFLAGS) AC_SUBST(MOZ_PNG_LIBS) +if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then + export MOZ_NUWA_PROCESS + AC_DEFINE(MOZ_NUWA_PROCESS) +fi +AC_SUBST(MOZ_NUWA_PROCESS) + AC_SUBST(NSPR_CFLAGS) AC_SUBST(NSPR_LIBS) AC_SUBST(MOZ_NATIVE_NSPR) diff --git a/mozglue/build/Makefile.in b/mozglue/build/Makefile.in index 93d1ab902857..7fd8729e814c 100644 --- a/mozglue/build/Makefile.in +++ b/mozglue/build/Makefile.in @@ -79,6 +79,9 @@ endif endif ifeq (Android,$(OS_TARGET)) +ifdef MOZ_NUWA_PROCESS +CPPSRCS += Nuwa.cpp +endif SHARED_LIBRARY_LIBS += $(call EXPAND_LIBNAME_PATH,android,$(DEPTH)/other-licenses/android) endif diff --git a/mozglue/build/Nuwa.cpp b/mozglue/build/Nuwa.cpp new file mode 100644 index 000000000000..fdc365e91f15 --- /dev/null +++ b/mozglue/build/Nuwa.cpp @@ -0,0 +1,1746 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mozilla/LinkedList.h" +#include "Nuwa.h" + +using namespace mozilla; + +/** + * Provides the wrappers to a selected set of pthread and system-level functions + * as the basis for implementing Zygote-like preforking mechanism. + */ + +/** + * Real functions for the wrappers. + */ +extern "C" { +int __real_pthread_create(pthread_t *thread, + const pthread_attr_t *attr, + void *(*start_routine) (void *), + void *arg); +int __real_pthread_key_create(pthread_key_t *key, void (*destructor)(void*)); +int __real_pthread_key_delete(pthread_key_t key); +pthread_t __real_pthread_self(); +int __real_pthread_join(pthread_t thread, void **retval); +int __real_epoll_wait(int epfd, + struct epoll_event *events, + int maxevents, + int timeout); +int __real_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mtx); +int __real_pthread_cond_timedwait(pthread_cond_t *cond, + pthread_mutex_t *mtx, + const struct timespec *abstime); +int __real___pthread_cond_timedwait(pthread_cond_t *cond, + pthread_mutex_t *mtx, + const struct timespec *abstime, + clockid_t clock); +int __real_pthread_mutex_lock(pthread_mutex_t *mtx); +int __real_poll(struct pollfd *fds, nfds_t nfds, int timeout); +int __real_epoll_create(int size); +int __real_socketpair(int domain, int type, int protocol, int sv[2]); +int __real_pipe2(int __pipedes[2], int flags); +int __real_pipe(int __pipedes[2]); +int __real_epoll_ctl(int aEpollFd, int aOp, int aFd, struct epoll_event *aEvent); +int __real_close(int aFd); + +} + +#define REAL(s) __real_##s + +/** + * A Nuwa process is started by preparing. After preparing, it waits + * for all threads becoming frozen. Then, it is ready while all + * threads are frozen. + */ +static bool sIsNuwaProcess = false; // This process is a Nuwa process. +static bool sIsFreezing = false; // Waiting for all threads getting frozen. +static bool sNuwaReady = false; // Nuwa process is ready. +static bool sNuwaPendingSpawn = false; // Are there any pending spawn requests? +static bool sNuwaForking = false; + +// Fds of transports of top level protocols. +static NuwaProtoFdInfo sProtoFdInfos[NUWA_TOPLEVEL_MAX]; +static int sProtoFdInfosSize = 0; + +template +struct LibcAllocator: public std::allocator +{ + LibcAllocator() + { + void* libcHandle = dlopen("libc.so", RTLD_LAZY); + mMallocImpl = reinterpret_cast(dlsym(libcHandle, "malloc")); + mFreeImpl = reinterpret_cast(dlsym(libcHandle, "free")); + + if (!(mMallocImpl && mFreeImpl)) { + // libc should be available, or we'll deadlock in using TLSInfoList. + abort(); + } + } + + inline typename std::allocator::pointer + allocate(typename std::allocator::size_type n, + const void * = 0) + { + return reinterpret_cast(mMallocImpl(n)); + } + + inline void + deallocate(typename std::allocator::pointer p, + typename std::allocator::size_type n) + { + mFreeImpl(p); + } + + template + struct rebind + { + typedef LibcAllocator other; + }; +private: + void* (*mMallocImpl)(size_t); + void (*mFreeImpl)(void*); +}; + +/** + * TLSInfoList should use malloc() and free() in libc to avoid the deadlock that + * jemalloc calls into __wrap_pthread_mutex_lock() and then deadlocks while + * the same thread already acquired sThreadCountLock. + */ +typedef std::vector, + LibcAllocator > > +TLSInfoList; + +/** + * The stack size is chosen carefully so the frozen threads doesn't consume too + * much memory in the Nuwa process. The threads shouldn't run deep recursive + * methods or do large allocations on the stack to avoid stack overflow. + */ +#ifndef NUWA_STACK_SIZE +#define NUWA_STACK_SIZE (1024 * 32) +#endif + +struct thread_info : public mozilla::LinkedListElement { + pthread_t origThreadID; + pthread_t recreatedThreadID; + pthread_attr_t threadAttr; + jmp_buf jmpEnv; + jmp_buf retEnv; + + int flags; + + void *(*startupFunc)(void *arg); + void *startupArg; + + // The thread specific function to recreate the new thread. It's executed + // after the thread is recreated. + void (*recrFunc)(void *arg); + void *recrArg; + + TLSInfoList tlsInfo; + + pthread_mutex_t *reacquireMutex; + void *stk; +}; + +typedef struct thread_info thread_info_t; + +static thread_info_t *sCurrentRecreatingThread = NULL; + +/** + * This function runs the custom recreation function registered when calling + * NuwaMarkCurrentThread() after thread stack is restored. + */ +static void +RunCustomRecreation() { + thread_info_t *tinfo = sCurrentRecreatingThread; + if (tinfo->recrFunc != NULL) { + tinfo->recrFunc(tinfo->recrArg); + } +} + +/** + * Every thread should be marked as either TINFO_FLAG_NUWA_SUPPORT or + * TINFO_FLAG_NUWA_SKIP, or it means a potential error. We force + * Gecko code to mark every single thread to make sure there are no accidents + * when recreating threads with Nuwa. + * + * Threads marked as TINFO_FLAG_NUWA_SUPPORT can be checkpointed explicitly, by + * calling NuwaCheckpointCurrentThread(), or implicitly when they call into wrapped + * functions like pthread_mutex_lock(), epoll_wait(), etc. + * TINFO_FLAG_NUWA_EXPLICIT_CHECKPOINT denotes the explicitly checkpointed thread. + */ +#define TINFO_FLAG_NUWA_SUPPORT 0x1 +#define TINFO_FLAG_NUWA_SKIP 0x2 +#define TINFO_FLAG_NUWA_EXPLICIT_CHECKPOINT 0x4 + +typedef struct nuwa_construct { + void (*construct)(void *); + void *arg; +} nuwa_construct_t; + +static std::vector sConstructors; +static std::vector sFinalConstructors; + +typedef std::map TLSKeySet; +static TLSKeySet sTLSKeys; + +/** + * This mutex is used to block the running threads and freeze their contexts. + * PrepareNuwaProcess() is the first one to acquire the lock. Further attempts + * to acquire this mutex (in the freeze point macros) will block and freeze the + * calling thread. + */ +static pthread_mutex_t sThreadFreezeLock = PTHREAD_MUTEX_INITIALIZER; + +static LinkedList sAllThreads; +static int sThreadCount = 0; +static int sThreadFreezeCount = 0; +/** + * This mutex protects the access to thread info: + * sAllThreads, sThreadCount, sThreadFreezeCount, sRecreateVIPCount. + */ +static pthread_mutex_t sThreadCountLock = PTHREAD_MUTEX_INITIALIZER; +/** + * This condition variable lets MakeNuwaProcess() wait until all recreated + * threads are frozen. + */ +static pthread_cond_t sThreadChangeCond = PTHREAD_COND_INITIALIZER; + +/** + * This mutex and condition variable is used to serialize the fork requests + * from the parent process. + */ +static pthread_mutex_t sForkLock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t sForkWaitCond = PTHREAD_COND_INITIALIZER; + +/** + * sForkWaitCondChanged will be reset to false on the IPC thread before + * and will be changed to true on the main thread to indicate that the condition + * that the IPC thread is waiting for has already changed. + */ +static bool sForkWaitCondChanged = false; + +/** + * This mutex protects the access to sTLSKeys, which keeps track of existing + * TLS Keys. + */ +static pthread_mutex_t sTLSKeyLock = PTHREAD_MUTEX_INITIALIZER; +static int sThreadSkipCount = 0; + +static thread_info_t * +GetThreadInfoInner(pthread_t threadID) { + for (thread_info_t *tinfo = sAllThreads.getFirst(); + tinfo; + tinfo = tinfo->getNext()) { + if (pthread_equal(tinfo->origThreadID, threadID)) { + return tinfo; + } + } + + return NULL; +} + +/** + * Get thread info using the specified thread ID. + * + * @return thread_info_t which has threadID == specified threadID + */ +static thread_info_t * +GetThreadInfo(pthread_t threadID) { + if (sIsNuwaProcess) { + REAL(pthread_mutex_lock)(&sThreadCountLock); + } + thread_info_t *tinfo = GetThreadInfoInner(threadID); + if (sIsNuwaProcess) { + pthread_mutex_unlock(&sThreadCountLock); + } + return tinfo; +} + +#if !defined(HAVE_THREAD_TLS_KEYWORD) +/** + * Get thread info of the current thread. + * + * @return thread_info_t for the current thread. + */ +static thread_info_t * +GetCurThreadInfo() { + pthread_t threadID = REAL(pthread_self)(); + pthread_t thread_info_t::*threadIDptr = + (sIsNuwaProcess ? + &thread_info_t::origThreadID : + &thread_info_t::recreatedThreadID); + + REAL(pthread_mutex_lock)(&sThreadCountLock); + thread_info_t *tinfo; + for (tinfo = sAllThreads.getFirst(); + tinfo; + tinfo = tinfo->getNext()) { + if (pthread_equal(tinfo->*threadIDptr, threadID)) { + break; + } + } + pthread_mutex_unlock(&sThreadCountLock); + return tinfo; +} +#define CUR_THREAD_INFO GetCurThreadInfo() +#define SET_THREAD_INFO(x) /* Nothing to do. */ +#else +// Is not NULL only for threads created by pthread_create() in an Nuwa process. +// It is always NULL for the main thread. +static __thread thread_info_t *sCurThreadInfo = NULL; +#define CUR_THREAD_INFO sCurThreadInfo +#define SET_THREAD_INFO(x) do { sCurThreadInfo = (x); } while(0) +#endif // HAVE_THREAD_TLS_KEYWORD + +/* + * Track all epoll fds and handling events. + */ +class EpollManager { +public: + class EpollInfo { + public: + typedef struct epoll_event Events; + typedef std::map EpollEventsMap; + typedef EpollEventsMap::iterator iterator; + typedef EpollEventsMap::const_iterator const_iterator; + + EpollInfo(): mBackSize(0) {} + EpollInfo(int aBackSize): mBackSize(aBackSize) {} + EpollInfo(const EpollInfo &aOther): mEvents(aOther.mEvents) + , mBackSize(aOther.mBackSize) { + } + ~EpollInfo() { + mEvents.clear(); + } + + void AddEvents(int aFd, Events &aEvents) { + std::pair pair = + mEvents.insert(std::make_pair(aFd, aEvents)); + if (!pair.second) { + abort(); + } + } + + void RemoveEvents(int aFd) { + if (!mEvents.erase(aFd)) { + abort(); + } + } + + void ModifyEvents(int aFd, Events &aEvents) { + iterator it = mEvents.find(aFd); + if (it == mEvents.end()) { + abort(); + } + it->second = aEvents; + } + + const Events &FindEvents(int aFd) const { + const_iterator it = mEvents.find(aFd); + if (it == mEvents.end()) { + abort(); + } + return it->second; + } + + int Size() const { return mEvents.size(); } + + // Iterator with values of pairs. + const_iterator begin() const { return mEvents.begin(); } + const_iterator end() const { return mEvents.end(); } + + int BackSize() const { return mBackSize; } + + private: + EpollEventsMap mEvents; + int mBackSize; + + friend class EpollManager; + }; + + typedef std::map EpollInfoMap; + typedef EpollInfoMap::iterator iterator; + typedef EpollInfoMap::const_iterator const_iterator; + +public: + void AddEpollInfo(int aEpollFd, int aBackSize) { + EpollInfo *oldinfo = FindEpollInfo(aEpollFd); + if (oldinfo != NULL) { + abort(); + } + mEpollFdsInfo[aEpollFd] = EpollInfo(aBackSize); + } + + EpollInfo *FindEpollInfo(int aEpollFd) { + iterator it = mEpollFdsInfo.find(aEpollFd); + if (it == mEpollFdsInfo.end()) { + return NULL; + } + return &it->second; + } + + void RemoveEpollInfo(int aEpollFd) { + if (!mEpollFdsInfo.erase(aEpollFd)) { + abort(); + } + } + + int Size() const { return mEpollFdsInfo.size(); } + + // Iterator of pairs. + const_iterator begin() const { return mEpollFdsInfo.begin(); } + const_iterator end() const { return mEpollFdsInfo.end(); } + + static EpollManager *Singleton() { + if (!sInstance) { + sInstance = new EpollManager(); + } + return sInstance; + } + + static void Shutdown() { + if (!sInstance) { + abort(); + } + + delete sInstance; + sInstance = NULL; + } + +private: + static EpollManager *sInstance; + ~EpollManager() { + mEpollFdsInfo.clear(); + } + + EpollInfoMap mEpollFdsInfo; + + EpollManager() {} +}; + +EpollManager* EpollManager::sInstance; + +static thread_info_t * +thread_info_new(void) { + /* link tinfo to sAllThreads */ + thread_info_t *tinfo = new thread_info_t(); + tinfo->flags = 0; + tinfo->recrFunc = NULL; + tinfo->recrArg = NULL; + tinfo->recreatedThreadID = 0; + tinfo->reacquireMutex = NULL; + tinfo->stk = malloc(NUWA_STACK_SIZE); + pthread_attr_init(&tinfo->threadAttr); + + REAL(pthread_mutex_lock)(&sThreadCountLock); + // Insert to the tail. + sAllThreads.insertBack(tinfo); + + sThreadCount++; + pthread_cond_signal(&sThreadChangeCond); + pthread_mutex_unlock(&sThreadCountLock); + + return tinfo; +} + +static void +thread_info_cleanup(void *arg) { + if (sNuwaForking) { + // We shouldn't have any thread exiting when we are forking a new process. + abort(); + } + + thread_info_t *tinfo = (thread_info_t *)arg; + pthread_attr_destroy(&tinfo->threadAttr); + + REAL(pthread_mutex_lock)(&sThreadCountLock); + /* unlink tinfo from sAllThreads */ + tinfo->remove(); + + sThreadCount--; + pthread_cond_signal(&sThreadChangeCond); + pthread_mutex_unlock(&sThreadCountLock); + + free(tinfo->stk); + delete tinfo; +} + +static void * +_thread_create_startup(void *arg) { + thread_info_t *tinfo = (thread_info_t *)arg; + void *r; + + // Save thread info; especially, stackaddr & stacksize. + // Reuse the stack in the new thread. + pthread_getattr_np(REAL(pthread_self)(), &tinfo->threadAttr); + + SET_THREAD_INFO(tinfo); + tinfo->origThreadID = REAL(pthread_self)(); + + pthread_cleanup_push(thread_info_cleanup, tinfo); + + r = tinfo->startupFunc(tinfo->startupArg); + + if (!sIsNuwaProcess) { + return r; + } + + pthread_cleanup_pop(1); + + return r; +} + +// reserve STACK_RESERVED_SZ * 4 bytes for thread_recreate_startup(). +#define STACK_RESERVED_SZ 64 +#define STACK_SENTINEL(v) ((v)[0]) +#define STACK_SENTINEL_VALUE(v) ((uint32_t)(v) ^ 0xdeadbeef) + +static void * +thread_create_startup(void *arg) { + /* + * Dark Art!! Never try to do the same unless you are ABSOLUTELY sure of + * what you are doing! + * + * This function is here for reserving stack space before calling + * _thread_create_startup(). see also thread_create_startup(); + */ + void *r; + volatile uint32_t reserved[STACK_RESERVED_SZ]; + + // Reserve stack space. + STACK_SENTINEL(reserved) = STACK_SENTINEL_VALUE(reserved); + + r = _thread_create_startup(arg); + + // Check if the reservation is enough. + if (STACK_SENTINEL(reserved) != STACK_SENTINEL_VALUE(reserved)) { + abort(); // Did not reserve enough stack space. + } + + thread_info_t *tinfo = CUR_THREAD_INFO; + if (!sIsNuwaProcess) { + longjmp(tinfo->retEnv, 1); + + // Never go here! + abort(); + } + + return r; +} + +extern "C" MFBT_API int +__wrap_pthread_create(pthread_t *thread, + const pthread_attr_t *attr, + void *(*start_routine) (void *), + void *arg) { + if (!sIsNuwaProcess) { + return REAL(pthread_create)(thread, attr, start_routine, arg); + } + + thread_info_t *tinfo = thread_info_new(); + tinfo->startupFunc = start_routine; + tinfo->startupArg = arg; + pthread_attr_setstack(&tinfo->threadAttr, tinfo->stk, NUWA_STACK_SIZE); + + int rv = REAL(pthread_create)(thread, + &tinfo->threadAttr, + thread_create_startup, + tinfo); + if (rv) { + thread_info_cleanup(tinfo); + } else { + tinfo->origThreadID = *thread; + } + + return rv; +} + +// TLS related + +/** + * Iterates over the existing TLS keys and store the TLS data for the current + * thread in tinfo. + */ +static void +SaveTLSInfo(thread_info_t *tinfo) { + REAL(pthread_mutex_lock)(&sTLSKeyLock); + tinfo->tlsInfo.clear(); + for (TLSKeySet::const_iterator it = sTLSKeys.begin(); + it != sTLSKeys.end(); + it++) { + void *value = pthread_getspecific(it->first); + if (value == NULL) { + continue; + } + + pthread_key_t key = it->first; + tinfo->tlsInfo.push_back(TLSInfoList::value_type(key, value)); + } + pthread_mutex_unlock(&sTLSKeyLock); +} + +/** + * Restores the TLS data for the current thread from tinfo. + */ +static void +RestoreTLSInfo(thread_info_t *tinfo) { + int rv; + + for (TLSInfoList::const_iterator it = tinfo->tlsInfo.begin(); + it != tinfo->tlsInfo.end(); + it++) { + pthread_key_t key = it->first; + const void *value = it->second; + if (pthread_setspecific(key, value)) { + abort(); + } + } + + SET_THREAD_INFO(tinfo); + tinfo->recreatedThreadID = REAL(pthread_self)(); +} + +extern "C" MFBT_API int +__wrap_pthread_key_create(pthread_key_t *key, void (*destructor)(void*)) { + int rv = REAL(pthread_key_create)(key, destructor); + if (rv != 0) { + return rv; + } + REAL(pthread_mutex_lock)(&sTLSKeyLock); + sTLSKeys.insert(TLSKeySet::value_type(*key, destructor)); + pthread_mutex_unlock(&sTLSKeyLock); + return 0; +} + +extern "C" MFBT_API int +__wrap_pthread_key_delete(pthread_key_t key) { + if (!sIsNuwaProcess) { + return REAL(pthread_key_delete)(key); + } + int rv = REAL(pthread_key_delete)(key); + if (rv != 0) { + return rv; + } + REAL(pthread_mutex_lock)(&sTLSKeyLock); + sTLSKeys.erase(key); + pthread_mutex_unlock(&sTLSKeyLock); + return 0; +} + +extern "C" MFBT_API pthread_t +__wrap_pthread_self() { + thread_info_t *tinfo = CUR_THREAD_INFO; + if (tinfo) { + // For recreated thread, masquerade as the original thread in the Nuwa + // process. + return tinfo->origThreadID; + } + return REAL(pthread_self)(); +} + +extern "C" MFBT_API int +__wrap_pthread_join(pthread_t thread, void **retval) { + thread_info_t *tinfo = GetThreadInfo(thread); + if (tinfo == NULL) { + return REAL(pthread_join)(thread, retval); + } + // pthread_join() need to use the real thread ID in the spawned process. + return REAL(pthread_join)(tinfo->recreatedThreadID, retval); +} + +/** + * The following are used to synchronize between the main thread and the + * thread being recreated. The main thread will wait until the thread is woken + * up from the freeze points or the blocking intercepted functions and then + * proceed to recreate the next frozen thread. + * + * In thread recreation, the main thread recreates the frozen threads one by + * one. The recreated threads will be "gated" until the main thread "opens the + * gate" to let them run freely as if they were created from scratch. The VIP + * threads gets the chance to run first after their thread stacks are recreated + * (using longjmp()) so they can adjust their contexts to a valid, consistent + * state. The threads frozen waiting for pthread condition variables are VIP + * threads. After woken up they need to run first to make the associated mutex + * in a valid state to maintain the semantics of the intercepted function calls + * (like pthread_cond_wait()). + */ + +// Used to synchronize the main thread and the thread being recreated so that +// only one thread is allowed to be recreated at a time. +static pthread_mutex_t sRecreateWaitLock = PTHREAD_MUTEX_INITIALIZER; +// Used to block recreated threads until the main thread "opens the gate". +static pthread_mutex_t sRecreateGateLock = PTHREAD_MUTEX_INITIALIZER; +// Used to block the main thread from "opening the gate" until all VIP threads +// have been recreated. +static pthread_mutex_t sRecreateVIPGateLock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t sRecreateVIPCond = PTHREAD_COND_INITIALIZER; +static int sRecreateVIPCount = 0; +static int sRecreateGatePassed = 0; + +/** + * Thread recreation macros. + * + * The following macros are used in the forked process to synchronize and + * control the progress of thread recreation. + * + * 1. RECREATE_START() is first called in the beginning of thread + * recreation to set sRecreateWaitLock and sRecreateGateLock in locked + * state. + * 2. For each frozen thread: + * 2.1. RECREATE_BEFORE() to set the thread being recreated. + * 2.2. thread_recreate() to recreate the frozen thread. + * 2.3. Main thread calls RECREATE_WAIT() to wait on sRecreateWaitLock until + * the thread is recreated from the freeze point and calls + * RECREATE_CONTINUE() to release sRecreateWaitLock. + * 2.3. Non-VIP threads are blocked on RECREATE_GATE(). VIP threads calls + * RECREATE_PASS_VIP() to mark that a VIP thread is successfully + * recreated and then is blocked by calling RECREATE_GATE_VIP(). + * 3. RECREATE_WAIT_ALL_VIP() to wait until all VIP threads passed, that is, + * VIP threads already has their contexts (mainly pthread mutex) in a valid + * state. + * 4. RECREATE_OPEN_GATE() to unblock threads blocked by sRecreateGateLock. + * 5. RECREATE_FINISH() to complete thread recreation. + */ +#define RECREATE_START() \ + do { \ + REAL(pthread_mutex_lock)(&sRecreateWaitLock); \ + REAL(pthread_mutex_lock)(&sRecreateGateLock); \ + } while(0) +#define RECREATE_BEFORE(info) do { sCurrentRecreatingThread = info; } while(0) +#define RECREATE_WAIT() REAL(pthread_mutex_lock)(&sRecreateWaitLock) +#define RECREATE_CONTINUE() do { \ + RunCustomRecreation(); \ + pthread_mutex_unlock(&sRecreateWaitLock); \ + } while(0) +#define RECREATE_FINISH() pthread_mutex_unlock(&sRecreateWaitLock) +#define RECREATE_GATE() \ + do { \ + REAL(pthread_mutex_lock)(&sRecreateGateLock); \ + sRecreateGatePassed++; \ + pthread_mutex_unlock(&sRecreateGateLock); \ + } while(0) +#define RECREATE_OPEN_GATE() pthread_mutex_unlock(&sRecreateGateLock) +#define RECREATE_GATE_VIP() \ + do { \ + REAL(pthread_mutex_lock)(&sRecreateGateLock); \ + pthread_mutex_unlock(&sRecreateGateLock); \ + } while(0) +#define RECREATE_PASS_VIP() \ + do { \ + REAL(pthread_mutex_lock)(&sRecreateVIPGateLock); \ + sRecreateGatePassed++; \ + pthread_cond_signal(&sRecreateVIPCond); \ + pthread_mutex_unlock(&sRecreateVIPGateLock); \ + } while(0) +#define RECREATE_WAIT_ALL_VIP() \ + do { \ + REAL(pthread_mutex_lock)(&sRecreateVIPGateLock); \ + while(sRecreateGatePassed < sRecreateVIPCount) { \ + REAL(pthread_cond_wait)(&sRecreateVIPCond, \ + &sRecreateVIPGateLock); \ + } \ + pthread_mutex_unlock(&sRecreateVIPGateLock); \ + } while(0) + +/** + * Thread freeze points. Note that the freeze points are implemented as macros + * so as not to garble the content of the stack after setjmp(). + * + * In the nuwa process, when a thread supporting nuwa calls a wrapper + * function, freeze point 1 setjmp()s to save the state. We only allow the + * thread to be frozen in the wrapper functions. If thread freezing is not + * enabled yet, the wrapper functions act like their wrapped counterparts, + * except for the extra actions in the freeze points. If thread freezing is + * enabled, the thread will be frozen by calling one of the wrapper functions. + * The threads can be frozen in any of the following points: + * + * 1) Freeze point 1: this is the point where we setjmp() in the nuwa process + * and longjmp() in the spawned process. If freezing is enabled, then the + * current thread blocks by acquiring an already locked mutex, + * sThreadFreezeLock. + * 2) The wrapped function: the function that might block waiting for some + * resource or condition. + * 3) Freeze point 2: blocks the current thread by acquiring sThreadFreezeLock. + * If freezing is not enabled then revert the counter change in freeze + * point 1. + */ +#define THREAD_FREEZE_POINT1() \ + bool freezeCountChg = false; \ + bool recreated = false; \ + volatile bool freezePoint2 = false; \ + thread_info_t *tinfo; \ + if (sIsNuwaProcess && \ + (tinfo = CUR_THREAD_INFO) && \ + (tinfo->flags & TINFO_FLAG_NUWA_SUPPORT) && \ + !(tinfo->flags & TINFO_FLAG_NUWA_EXPLICIT_CHECKPOINT)) { \ + if (!setjmp(tinfo->jmpEnv)) { \ + REAL(pthread_mutex_lock)(&sThreadCountLock); \ + SaveTLSInfo(tinfo); \ + sThreadFreezeCount++; \ + freezeCountChg = true; \ + pthread_cond_signal(&sThreadChangeCond); \ + pthread_mutex_unlock(&sThreadCountLock); \ + \ + if (sIsFreezing) { \ + REAL(pthread_mutex_lock)(&sThreadFreezeLock); \ + /* Never return from the pthread_mutex_lock() call. */ \ + abort(); \ + } \ + } else { \ + RECREATE_CONTINUE(); \ + RECREATE_GATE(); \ + freezeCountChg = false; \ + recreated = true; \ + } \ + } + +#define THREAD_FREEZE_POINT1_VIP() \ + bool freezeCountChg = false; \ + bool recreated = false; \ + volatile bool freezePoint1 = false; \ + volatile bool freezePoint2 = false; \ + thread_info_t *tinfo; \ + if (sIsNuwaProcess && \ + (tinfo = CUR_THREAD_INFO) && \ + (tinfo->flags & TINFO_FLAG_NUWA_SUPPORT) && \ + !(tinfo->flags & TINFO_FLAG_NUWA_EXPLICIT_CHECKPOINT)) { \ + if (!setjmp(tinfo->jmpEnv)) { \ + REAL(pthread_mutex_lock)(&sThreadCountLock); \ + SaveTLSInfo(tinfo); \ + sThreadFreezeCount++; \ + sRecreateVIPCount++; \ + freezeCountChg = true; \ + pthread_cond_signal(&sThreadChangeCond); \ + pthread_mutex_unlock(&sThreadCountLock); \ + \ + if (sIsFreezing) { \ + freezePoint1 = true; \ + REAL(pthread_mutex_lock)(&sThreadFreezeLock); \ + /* Never return from the pthread_mutex_lock() call. */ \ + abort(); \ + } \ + } else { \ + freezeCountChg = false; \ + recreated = true; \ + } \ + } + +#define THREAD_FREEZE_POINT2() \ + if (freezeCountChg) { \ + REAL(pthread_mutex_lock)(&sThreadCountLock); \ + if (sNuwaReady && sIsNuwaProcess) { \ + pthread_mutex_unlock(&sThreadCountLock); \ + freezePoint2 = true; \ + REAL(pthread_mutex_lock)(&sThreadFreezeLock); \ + /* Never return from the pthread_mutex_lock() call. */ \ + abort(); \ + } \ + sThreadFreezeCount--; \ + pthread_cond_signal(&sThreadChangeCond); \ + pthread_mutex_unlock(&sThreadCountLock); \ + } + +#define THREAD_FREEZE_POINT2_VIP() \ + if (freezeCountChg) { \ + REAL(pthread_mutex_lock)(&sThreadCountLock); \ + if (sNuwaReady && sIsNuwaProcess) { \ + pthread_mutex_unlock(&sThreadCountLock); \ + freezePoint2 = true; \ + REAL(pthread_mutex_lock)(&sThreadFreezeLock); \ + /* Never return from the pthread_mutex_lock() call. */ \ + abort(); \ + } \ + sThreadFreezeCount--; \ + sRecreateVIPCount--; \ + pthread_cond_signal(&sThreadChangeCond); \ + pthread_mutex_unlock(&sThreadCountLock); \ + } + +/** + * Wrapping the blocking functions: epoll_wait(), poll(), pthread_mutex_lock(), + * pthread_cond_wait() and pthread_cond_timedwait(): + * + * These functions are wrapped by the above freeze point macros. Once a new + * process is forked, the recreated thread will be blocked in one of the wrapper + * functions. When recreating the thread, we longjmp() to + * THREAD_FREEZE_POINT1() to recover the thread stack. Care must be taken to + * maintain the semantics of the wrapped function: + * + * - epoll_wait() and poll(): just retry the function. + * - pthread_mutex_lock(): don't lock if frozen at freeze point 2 (lock is + * already acquired). + * - pthread_cond_wait() and pthread_cond_timedwait(): if the thread is frozen + * waiting the condition variable, the mutex is already released, we need to + * reacquire the mutex before calling the wrapped function again so the mutex + * will be in a valid state. + */ + +extern "C" MFBT_API int +__wrap_epoll_wait(int epfd, + struct epoll_event *events, + int maxevents, + int timeout) { + int rv; + + THREAD_FREEZE_POINT1(); + rv = REAL(epoll_wait)(epfd, events, maxevents, timeout); + THREAD_FREEZE_POINT2(); + + return rv; +} + +extern "C" MFBT_API int +__wrap_pthread_cond_wait(pthread_cond_t *cond, + pthread_mutex_t *mtx) { + int rv = 0; + + THREAD_FREEZE_POINT1_VIP(); + if (freezePoint2) { + RECREATE_CONTINUE(); + RECREATE_PASS_VIP(); + RECREATE_GATE_VIP(); + return rv; + } + if (recreated && mtx) { + if (!freezePoint1 && pthread_mutex_trylock(mtx)) { + // The thread was frozen in pthread_cond_wait() after releasing mtx in the + // Nuwa process. In recreating this thread, We failed to reacquire mtx + // with the pthread_mutex_trylock() call, that is, mtx was acquired by + // another thread. Because of this, we need the main thread's help to + // reacquire mtx so that it will be in a valid state. + tinfo->reacquireMutex = mtx; + } + RECREATE_CONTINUE(); + RECREATE_PASS_VIP(); + } + rv = REAL(pthread_cond_wait)(cond, mtx); + if (recreated && mtx) { + // We still need to be gated as not to acquire another mutex associated with + // another VIP thread and interfere with it. + RECREATE_GATE_VIP(); + } + THREAD_FREEZE_POINT2_VIP(); + + return rv; +} + +extern "C" MFBT_API int +__wrap_pthread_cond_timedwait(pthread_cond_t *cond, + pthread_mutex_t *mtx, + const struct timespec *abstime) { + int rv = 0; + + THREAD_FREEZE_POINT1_VIP(); + if (freezePoint2) { + RECREATE_CONTINUE(); + RECREATE_PASS_VIP(); + RECREATE_GATE_VIP(); + return rv; + } + if (recreated && mtx) { + if (!freezePoint1 && pthread_mutex_trylock(mtx)) { + tinfo->reacquireMutex = mtx; + } + RECREATE_CONTINUE(); + RECREATE_PASS_VIP(); + } + rv = REAL(pthread_cond_timedwait)(cond, mtx, abstime); + if (recreated && mtx) { + RECREATE_GATE_VIP(); + } + THREAD_FREEZE_POINT2_VIP(); + + return rv; +} + +extern "C" int __pthread_cond_timedwait(pthread_cond_t *cond, + pthread_mutex_t *mtx, + const struct timespec *abstime, + clockid_t clock); + +extern "C" MFBT_API int +__wrap___pthread_cond_timedwait(pthread_cond_t *cond, + pthread_mutex_t *mtx, + const struct timespec *abstime, + clockid_t clock) { + int rv = 0; + + THREAD_FREEZE_POINT1_VIP(); + if (freezePoint2) { + RECREATE_CONTINUE(); + RECREATE_PASS_VIP(); + RECREATE_GATE_VIP(); + return rv; + } + if (recreated && mtx) { + if (!freezePoint1 && pthread_mutex_trylock(mtx)) { + tinfo->reacquireMutex = mtx; + } + RECREATE_CONTINUE(); + RECREATE_PASS_VIP(); + } + rv = REAL(__pthread_cond_timedwait)(cond, mtx, abstime, clock); + if (recreated && mtx) { + RECREATE_GATE_VIP(); + } + THREAD_FREEZE_POINT2_VIP(); + + return rv; +} + +extern "C" MFBT_API int +__wrap_pthread_mutex_lock(pthread_mutex_t *mtx) { + int rv = 0; + + THREAD_FREEZE_POINT1(); + if (freezePoint2) { + return rv; + } + rv = REAL(pthread_mutex_lock)(mtx); + THREAD_FREEZE_POINT2(); + + return rv; +} + +extern "C" MFBT_API int +__wrap_poll(struct pollfd *fds, nfds_t nfds, int timeout) { + int rv; + + THREAD_FREEZE_POINT1(); + rv = REAL(poll)(fds, nfds, timeout); + THREAD_FREEZE_POINT2(); + + return rv; +} + +extern "C" MFBT_API int +__wrap_epoll_create(int size) { + int epollfd = REAL(epoll_create)(size); + + if (!sIsNuwaProcess) { + return epollfd; + } + + if (epollfd >= 0) { + EpollManager::Singleton()->AddEpollInfo(epollfd, size); + } + + return epollfd; +} + +/** + * Wrapping the functions to create file descriptor pairs. In the child process + * FD pairs are created for intra-process signaling. The generation of FD pairs + * need to be tracked in the nuwa process so they can be recreated in the + * spawned process. + */ +struct FdPairInfo { + enum { + kPipe, + kSocketpair + } call; + + int FDs[2]; + int flags; + int domain; + int type; + int protocol; +}; + +/** + * Protects the access to sSingalFds. + */ +static pthread_mutex_t sSignalFdLock = PTHREAD_MUTEX_INITIALIZER; +static std::vector sSignalFds; + +extern "C" MFBT_API int +__wrap_socketpair(int domain, int type, int protocol, int sv[2]) +{ + int rv = REAL(socketpair)(domain, type, protocol, sv); + + if (!sIsNuwaProcess || rv < 0) { + return rv; + } + + REAL(pthread_mutex_lock)(&sSignalFdLock); + FdPairInfo signalFd; + signalFd.call = FdPairInfo::kSocketpair; + signalFd.FDs[0] = sv[0]; + signalFd.FDs[1] = sv[1]; + signalFd.domain = domain; + signalFd.type = type; + signalFd.protocol = protocol; + + sSignalFds.push_back(signalFd); + pthread_mutex_unlock(&sSignalFdLock); + + return rv; +} + +extern "C" MFBT_API int +__wrap_pipe2(int __pipedes[2], int flags) +{ + int rv = REAL(pipe2)(__pipedes, flags); + if (!sIsNuwaProcess || rv < 0) { + return rv; + } + + REAL(pthread_mutex_lock)(&sSignalFdLock); + FdPairInfo signalFd; + signalFd.call = FdPairInfo::kPipe; + signalFd.FDs[0] = __pipedes[0]; + signalFd.FDs[1] = __pipedes[1]; + signalFd.flags = flags; + sSignalFds.push_back(signalFd); + pthread_mutex_unlock(&sSignalFdLock); + return rv; +} + +extern "C" MFBT_API int +__wrap_pipe(int __pipedes[2]) +{ + return __wrap_pipe2(__pipedes, 0); +} + +static void +DupeSingleFd(int newFd, int origFd) +{ + struct stat sb; + if (fstat(origFd, &sb)) { + // Maybe the original FD is closed. + return; + } + int fd = fcntl(origFd, F_GETFD); + int fl = fcntl(origFd, F_GETFL); + dup2(newFd, origFd); + fcntl(origFd, F_SETFD, fd); + fcntl(origFd, F_SETFL, fl); + REAL(close)(newFd); +} + +extern "C" MFBT_API void +ReplaceSignalFds() +{ + for (std::vector::iterator it = sSignalFds.begin(); + it < sSignalFds.end(); ++it) { + int fds[2]; + int rc = 0; + switch (it->call) { + case FdPairInfo::kPipe: + rc = REAL(pipe2)(fds, it->flags); + break; + case FdPairInfo::kSocketpair: + rc = REAL(socketpair)(it->domain, it->type, it->protocol, fds); + break; + default: + continue; + } + + if (rc == 0) { + DupeSingleFd(fds[0], it->FDs[0]); + DupeSingleFd(fds[1], it->FDs[1]); + } + } +} + +extern "C" MFBT_API int +__wrap_epoll_ctl(int aEpollFd, int aOp, int aFd, struct epoll_event *aEvent) { + int rv = REAL(epoll_ctl)(aEpollFd, aOp, aFd, aEvent); + + if (!sIsNuwaProcess || rv == -1) { + return rv; + } + + EpollManager::EpollInfo *info = + EpollManager::Singleton()->FindEpollInfo(aEpollFd); + if (info == NULL) { + abort(); + } + + switch(aOp) { + case EPOLL_CTL_ADD: + info->AddEvents(aFd, *aEvent); + break; + + case EPOLL_CTL_MOD: + info->ModifyEvents(aFd, *aEvent); + break; + + case EPOLL_CTL_DEL: + info->RemoveEvents(aFd); + break; + + default: + abort(); + } + + return rv; +} + +// XXX: thinker: Maybe, we should also track dup, dup2, and other functions. +extern "C" MFBT_API int +__wrap_close(int aFd) { + int rv = REAL(close)(aFd); + if (!sIsNuwaProcess || rv == -1) { + return rv; + } + + EpollManager::EpollInfo *info = + EpollManager::Singleton()->FindEpollInfo(aFd); + if (info) { + EpollManager::Singleton()->RemoveEpollInfo(aFd); + } + + return rv; +} + +static void * +thread_recreate_startup(void *arg) { + /* + * Dark Art!! Never do the same unless you are ABSOLUTELY sure what you are + * doing! + * + * The stack space collapsed by this frame had been reserved by + * thread_create_startup(). And thread_create_startup() will + * return immediately after returning from real start routine, so + * all collapsed values does not affect the result. + * + * All outer frames of thread_create_startup() and + * thread_recreate_startup() are equivalent, so + * thread_create_startup() will return successfully. + */ + thread_info_t *tinfo = (thread_info_t *)arg; + + RestoreTLSInfo(tinfo); + + if (setjmp(tinfo->retEnv) != 0) { + return NULL; + } + + // longjump() to recreate the stack on the new thread. + longjmp(tinfo->jmpEnv, 1); + + // Never go here! + abort(); + + return NULL; +} + +/** + * Recreate the context given by tinfo at a new thread. + */ +static void +thread_recreate(thread_info_t *tinfo) { + pthread_t thread; + + // Note that the thread_recreate_startup() runs on the stack specified by + // tinfo. + pthread_create(&thread, &tinfo->threadAttr, thread_recreate_startup, tinfo); +} + +/** + * Recreate all threads in a process forked from an Nuwa process. + */ +static void +RecreateThreads() { + sIsNuwaProcess = false; + sIsFreezing = false; + + // Run registered constructors. + for (std::vector::iterator ctr = sConstructors.begin(); + ctr != sConstructors.end(); + ctr++) { + (*ctr).construct((*ctr).arg); + } + sConstructors.clear(); + + REAL(pthread_mutex_lock)(&sThreadCountLock); + thread_info_t *tinfo = sAllThreads.getFirst(); + pthread_mutex_unlock(&sThreadCountLock); + + RECREATE_START(); + while (tinfo != NULL) { + if (tinfo->flags & TINFO_FLAG_NUWA_SUPPORT) { + RECREATE_BEFORE(tinfo); + thread_recreate(tinfo); + RECREATE_WAIT(); + if (tinfo->reacquireMutex) { + REAL(pthread_mutex_lock)(tinfo->reacquireMutex); + } + } else if(!(tinfo->flags & TINFO_FLAG_NUWA_SKIP)) { + // An unmarked thread is found other than the main thread. + + // All threads should be marked as one of SUPPORT or SKIP, or + // abort the process to make sure all threads in the Nuwa + // process are Nuwa-aware. + abort(); + } + + tinfo = tinfo->getNext(); + } + RECREATE_WAIT_ALL_VIP(); + RECREATE_OPEN_GATE(); + + RECREATE_FINISH(); + + // Run registered final constructors. + for (std::vector::iterator ctr = sFinalConstructors.begin(); + ctr != sFinalConstructors.end(); + ctr++) { + (*ctr).construct((*ctr).arg); + } + sFinalConstructors.clear(); +} + +extern "C" { + +/** + * Recreate all epoll fds and restore status; include all events. + */ +static void +RecreateEpollFds() { + EpollManager *man = EpollManager::Singleton(); + + for (EpollManager::const_iterator info_it = man->begin(); + info_it != man->end(); + info_it++) { + int epollfd = info_it->first; + const EpollManager::EpollInfo *info = &info_it->second; + + int fdflags = fcntl(epollfd, F_GETFD); + if (fdflags == -1) { + abort(); + } + int fl = fcntl(epollfd, F_GETFL); + if (fl == -1) { + abort(); + } + + int newepollfd = REAL(epoll_create)(info->BackSize()); + if (newepollfd == -1) { + abort(); + } + int rv = REAL(close)(epollfd); + if (rv == -1) { + abort(); + } + rv = dup2(newepollfd, epollfd); + if (rv == -1) { + abort(); + } + rv = REAL(close)(newepollfd); + if (rv == -1) { + abort(); + } + + rv = fcntl(epollfd, F_SETFD, fdflags); + if (rv == -1) { + abort(); + } + rv = fcntl(epollfd, F_SETFL, fl); + if (rv == -1) { + abort(); + } + + for (EpollManager::EpollInfo::const_iterator events_it = info->begin(); + events_it != info->end(); + events_it++) { + int fd = events_it->first; + epoll_event events; + events = events_it->second; + rv = REAL(epoll_ctl)(epollfd, EPOLL_CTL_ADD, fd, &events); + if (rv == -1) { + abort(); + } + } + } + + // Shutdown EpollManager. It won't be needed in the spawned process. + EpollManager::Shutdown(); +} + +/** + * Fix IPC to make it ready. + * + * Especially, fix ContentChild. + */ +static void +ReplaceIPC(NuwaProtoFdInfo *aInfoList, int aInfoSize) { + int i; + int rv; + + for (i = 0; i < aInfoSize; i++) { + int fd = fcntl(aInfoList[i].originFd, F_GETFD); + if (fd == -1) { + abort(); + } + + int fl = fcntl(aInfoList[i].originFd, F_GETFL); + if (fl == -1) { + abort(); + } + + rv = dup2(aInfoList[i].newFds[NUWA_NEWFD_CHILD], aInfoList[i].originFd); + if (rv == -1) { + abort(); + } + + rv = fcntl(aInfoList[i].originFd, F_SETFD, fd); + if (rv == -1) { + abort(); + } + + rv = fcntl(aInfoList[i].originFd, F_SETFL, fl); + if (rv == -1) { + abort(); + } + } +} + +/** + * Add a new content process at the chrome process. + */ +static void +AddNewProcess(pid_t pid, NuwaProtoFdInfo *aInfoList, int aInfoSize) { + static bool (*AddNewIPCProcess)(pid_t, NuwaProtoFdInfo *, int) = NULL; + + if (AddNewIPCProcess == NULL) { + AddNewIPCProcess = (bool (*)(pid_t, NuwaProtoFdInfo *, int)) + dlsym(RTLD_DEFAULT, "AddNewIPCProcess"); + } + AddNewIPCProcess(pid, aInfoList, aInfoSize); +} + +static void +PrepareProtoSockets(NuwaProtoFdInfo *aInfoList, int aInfoSize) { + int i; + int rv; + + for (i = 0; i < aInfoSize; i++) { + rv = REAL(socketpair)(PF_UNIX, SOCK_STREAM, 0, aInfoList[i].newFds); + if (rv == -1) { + abort(); + } + } +} + +static void +CloseAllProtoSockets(NuwaProtoFdInfo *aInfoList, int aInfoSize) { + int i; + + for (i = 0; i < aInfoSize; i++) { + REAL(close)(aInfoList[i].newFds[0]); + REAL(close)(aInfoList[i].newFds[1]); + } +} + +/** + * Fork a new process that is ready for running IPC. + * + * @return the PID of the new process. + */ +static int +ForkIPCProcess() { + int pid; + + REAL(pthread_mutex_lock)(&sForkLock); + + PrepareProtoSockets(sProtoFdInfos, sProtoFdInfosSize); + + sNuwaForking = true; + pid = fork(); + sNuwaForking = false; + if (pid == -1) { + abort(); + } + + if (pid > 0) { + // in the parent + AddNewProcess(pid, sProtoFdInfos, sProtoFdInfosSize); + CloseAllProtoSockets(sProtoFdInfos, sProtoFdInfosSize); + } else { + // in the child +#ifdef NUWA_DEBUG_CHILD_PROCESS + fprintf(stderr, "\n\n DEBUG ME @%d\n\n", getpid()); + sleep(15); +#endif + ReplaceSignalFds(); + ReplaceIPC(sProtoFdInfos, sProtoFdInfosSize); + RecreateEpollFds(); + RecreateThreads(); + CloseAllProtoSockets(sProtoFdInfos, sProtoFdInfosSize); + } + + sForkWaitCondChanged = true; + pthread_cond_signal(&sForkWaitCond); + pthread_mutex_unlock(&sForkLock); + + return pid; +} + +/** + * Prepare for spawning a new process. Called on the IPC thread. + */ +MFBT_API void +NuwaSpawnPrepare() { + REAL(pthread_mutex_lock)(&sForkLock); + + sForkWaitCondChanged = false; // Will be modified on the main thread. +} + +/** + * Let IPC thread wait until fork action on the main thread has completed. + */ +MFBT_API void +NuwaSpawnWait() { + while (!sForkWaitCondChanged) { + REAL(pthread_cond_wait)(&sForkWaitCond, &sForkLock); + } + pthread_mutex_unlock(&sForkLock); +} + +/** + * Spawn a new process. If not ready for spawn (still waiting for some threads + * to freeze), postpone the spawn request until ready. + * + * @return the pid of the new process, or 0 if not ready. + */ +MFBT_API pid_t +NuwaSpawn() { + if (gettid() != getpid()) { + // Not the main thread. + abort(); + } + + pid_t pid = 0; + + if (sNuwaReady) { + pid = ForkIPCProcess(); + } else { + sNuwaPendingSpawn = true; + } + + return pid; +} + +/** + * Prepare to freeze the Nuwa-supporting threads. + */ +MFBT_API void +PrepareNuwaProcess() { + sIsNuwaProcess = true; + // Explicitly ignore SIGCHLD so we don't have to call watpid() to reap + // dead child processes. + signal(SIGCHLD, SIG_IGN); + + // Make marked threads block in one freeze point. + REAL(pthread_mutex_lock)(&sThreadFreezeLock); +} + +// Make current process as a Nuwa process. +MFBT_API void +MakeNuwaProcess() { + void (*GetProtoFdInfos)(NuwaProtoFdInfo *, int, int *) = NULL; + void (*OnNuwaProcessReady)() = NULL; + sIsFreezing = true; + + REAL(pthread_mutex_lock)(&sThreadCountLock); + + // wait until all threads are frozen. + while ((sThreadFreezeCount + sThreadSkipCount) != sThreadCount) { + REAL(pthread_cond_wait)(&sThreadChangeCond, &sThreadCountLock); + } + + GetProtoFdInfos = (void (*)(NuwaProtoFdInfo *, int, int *)) + dlsym(RTLD_DEFAULT, "GetProtoFdInfos"); + GetProtoFdInfos(sProtoFdInfos, NUWA_TOPLEVEL_MAX, &sProtoFdInfosSize); + + sNuwaReady = true; + + pthread_mutex_unlock(&sThreadCountLock); + + OnNuwaProcessReady = (void (*)())dlsym(RTLD_DEFAULT, "OnNuwaProcessReady"); + OnNuwaProcessReady(); + + if (sNuwaPendingSpawn) { + sNuwaPendingSpawn = false; + NuwaSpawn(); + } +} + +/** + * Mark the current thread as supporting Nuwa. The thread will be recreated in + * the spawned process. + */ +MFBT_API void +NuwaMarkCurrentThread(void (*recreate)(void *), void *arg) { + if (!sIsNuwaProcess) { + return; + } + + thread_info_t *tinfo = CUR_THREAD_INFO; + if (tinfo == NULL) { + abort(); + } + + tinfo->flags |= TINFO_FLAG_NUWA_SUPPORT; + tinfo->recrFunc = recreate; + tinfo->recrArg = arg; +} + +/** + * Mark the current thread as not supporting Nuwa. Don't recreate this thread in + * the spawned process. + */ +MFBT_API void +NuwaSkipCurrentThread() { + if (!sIsNuwaProcess) return; + + thread_info_t *tinfo = CUR_THREAD_INFO; + if (tinfo == NULL) { + abort(); + } + + if (!(tinfo->flags & TINFO_FLAG_NUWA_SKIP)) { + sThreadSkipCount++; + } + tinfo->flags |= TINFO_FLAG_NUWA_SKIP; +} + +/** + * Force to freeze the current thread. + * + * This method does not return in Nuwa process. It returns for the + * recreated thread. + */ +MFBT_API void +NuwaFreezeCurrentThread() { + thread_info_t *tinfo = CUR_THREAD_INFO; + if (sIsNuwaProcess && + (tinfo = CUR_THREAD_INFO) && + (tinfo->flags & TINFO_FLAG_NUWA_SUPPORT)) { + if (!setjmp(tinfo->jmpEnv)) { + REAL(pthread_mutex_lock)(&sThreadCountLock); + SaveTLSInfo(tinfo); + sThreadFreezeCount++; + pthread_cond_signal(&sThreadChangeCond); + pthread_mutex_unlock(&sThreadCountLock); + + REAL(pthread_mutex_lock)(&sThreadFreezeLock); + } else { + RECREATE_CONTINUE(); + RECREATE_GATE(); + } + } +} + +/** + * The caller of NuwaCheckpointCurrentThread() is at the line it wishes to + * return after the thread is recreated. + * + * The checkpointed thread will restart at the calling line of + * NuwaCheckpointCurrentThread(). This macro returns true in the Nuwa process + * and false on the recreated thread in the forked process. + * + * NuwaCheckpointCurrentThread() is implemented as a macro so we can place the + * setjmp() call in the calling method without changing its stack pointer. This + * is essential for not corrupting the stack when the calling thread continues + * to request the main thread for forking a new process. The caller of + * NuwaCheckpointCurrentThread() should not return before the process forking + * finishes. + * + * @return true for Nuwa process, and false in the forked process. + */ +MFBT_API jmp_buf* +NuwaCheckpointCurrentThread1() { + thread_info_t *tinfo = CUR_THREAD_INFO; + if (sIsNuwaProcess && + (tinfo = CUR_THREAD_INFO) && + (tinfo->flags & TINFO_FLAG_NUWA_SUPPORT)) { + return &tinfo->jmpEnv; + } + abort(); + return nullptr; +} + +MFBT_API bool +NuwaCheckpointCurrentThread2(int setjmpCond) { + thread_info_t *tinfo = CUR_THREAD_INFO; + if (setjmpCond == 0) { + REAL(pthread_mutex_lock)(&sThreadCountLock); + if (!(tinfo->flags & TINFO_FLAG_NUWA_EXPLICIT_CHECKPOINT)) { + tinfo->flags |= TINFO_FLAG_NUWA_EXPLICIT_CHECKPOINT; + SaveTLSInfo(tinfo); + sThreadFreezeCount++; + } + pthread_cond_signal(&sThreadChangeCond); + pthread_mutex_unlock(&sThreadCountLock); + return true; + } + RECREATE_CONTINUE(); + RECREATE_GATE(); + return false; // Recreated thread. +} + +/** + * Register methods to be invoked before recreating threads in the spawned + * process. + */ +MFBT_API void +NuwaAddConstructor(void (*construct)(void *), void *arg) { + nuwa_construct_t ctr; + ctr.construct = construct; + ctr.arg = arg; + sConstructors.push_back(ctr); +} + +/** + * Register methods to be invoked after recreating threads in the spawned + * process. + */ +MFBT_API void +NuwaAddFinalConstructor(void (*construct)(void *), void *arg) { + nuwa_construct_t ctr; + ctr.construct = construct; + ctr.arg = arg; + sFinalConstructors.push_back(ctr); +} + +/** + * @return if the current process is the nuwa process. + */ +MFBT_API bool +IsNuwaProcess() { + return sIsNuwaProcess; +} + +/** + * @return if the nuwa process is ready for spawning new processes. + */ +MFBT_API bool +IsNuwaReady() { + return sNuwaReady; +} + +} // extern "C" diff --git a/mozglue/build/Nuwa.h b/mozglue/build/Nuwa.h new file mode 100644 index 000000000000..8784d66b8042 --- /dev/null +++ b/mozglue/build/Nuwa.h @@ -0,0 +1,171 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=2 autoindent cindent expandtab: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NUWA_H_ +#define __NUWA_H_ + +#include +#include + +#include "mozilla/Types.h" + +/** + * Nuwa is a goddess who created mankind according to her figure in the + * ancient Chinese mythology. + */ + +// Max number of top level protocol that can be handled by Nuwa process. +#ifndef NUWA_TOPLEVEL_MAX +#define NUWA_TOPLEVEL_MAX 8 +#endif + +struct NuwaProtoFdInfo { + int protoId; + int originFd; + int newFds[2]; +}; + +#define NUWA_NEWFD_PARENT 0 +#define NUWA_NEWFD_CHILD 1 + +extern "C" { + +/** + * The following 3 methods are used to synchronously spawn the Nuwa process. + * The typical usage is: + * + * The IPC thread The main thread + * 1. Receives the request. + * 2. NuwaSpawnPrepare(). + * 3. Request main thread to + * spawn. --------------------------------------+ + * 4. NuwaSpawnWait(). V + * 5. NuwaSpawn() to clone all + * the threads in the Nuwa + * process (including main + * & IPC threads). + * 6. NuwaSpawn() finishes and + * +-------------------------------------- signals NuwaSpawnWait(). + * V + * 7. NuwaSpawnWait() returns. + */ + +/** + * Prepare for spawning a new process. The calling thread (typically the IPC + * thread) will block further requests to spawn a new process. + */ +MFBT_API void NuwaSpawnPrepare(); + +/** + * Called on the thread that receives the request. Used to wait until + * NuwaSpawn() finishes and maintain the stack of the calling thread. + */ +MFBT_API void NuwaSpawnWait(); + +/** + * Spawn a new content process, called in the Nuwa process. This has to run on + * the main thread. + * + * @return The process ID of the new process. + */ +MFBT_API pid_t NuwaSpawn(); + +/** + * The following methods are for marking threads created in the Nuwa process so + * they can be frozen and then recreated in the spawned process. All threads + * except the main thread must be marked by one of the following 4 methods; + * otherwise, we have a thread that is created but unknown to us. The Nuwa + * process will never become ready and this needs to be fixed. + */ + +/** + * Mark the current thread as supporting Nuwa. The thread will implicitly freeze + * by calling a blocking method such as pthread_mutex_lock() or epoll_wait(). + * + * @param recreate The custom function that will be called in the spawned + * process, after the thread is recreated. Can be nullptr if no + * custom function to be called after the thread is recreated. + * @param arg The argument passed to the custom function. Can be nullptr. + */ +MFBT_API void NuwaMarkCurrentThread(void (*recreate)(void *), void *arg); + +/** + * Mark the current thread as not supporting Nuwa. The calling thread will not + * be automatically cloned from the Nuwa process to spawned process. If this + * thread needs to be created in the spawned process, use NuwaAddConstructor() + * or NuwaAddFinalConstructor() to do it. + */ +MFBT_API void NuwaSkipCurrentThread(); + +/** + * Force the current thread to freeze explicitly at the current point. + */ +MFBT_API void NuwaFreezeCurrentThread(); + +/** + * Helper functions for the NuwaCheckpointCurrentThread() macro. + */ +MFBT_API jmp_buf* NuwaCheckpointCurrentThread1(); + +MFBT_API bool NuwaCheckpointCurrentThread2(int setjmpCond); + +/** + * Set the point to recover after thread recreation. The calling thread is not + * blocked. This is used for the thread that cannot freeze (i.e. the IPC + * thread). + */ +#define NuwaCheckpointCurrentThread() \ + NuwaCheckpointCurrentThread2(setjmp(*NuwaCheckpointCurrentThread1())) + +/** + * The following methods are called in the initialization stage of the Nuwa + * process. + */ + +/** + * Prepare for making the Nuwa process. + */ +MFBT_API void PrepareNuwaProcess(); + +/** + * Make the current process a Nuwa process. Start to freeze the threads. + */ +MFBT_API void MakeNuwaProcess(); + +/** + * Register a method to be invoked after a new process is spawned. The method + * will be invoked on the main thread. + * + * @param construct The method to be invoked. + * @param arg The argument passed to the method. + */ +MFBT_API void NuwaAddConstructor(void (*construct)(void *), void *arg); + +/** + * Register a method to be invoked after a new process is spawned and threads + * are recreated. The method will be invoked on the main thread. + * + * @param construct The method to be invoked. + * @param arg The argument passed to the method. + */ +MFBT_API void NuwaAddFinalConstructor(void (*construct)(void *), void *arg); + +/** + * The methods to query the Nuwa-related states. + */ + +/** + * @return If the current process is the Nuwa process. + */ +MFBT_API bool IsNuwaProcess(); + +/** + * @return If the Nuwa process is ready for spawning new processes. + */ +MFBT_API bool IsNuwaReady(); +}; + +#endif /* __NUWA_H_ */ diff --git a/mozglue/build/moz.build b/mozglue/build/moz.build index 9ffb6678356e..2f3e0aa2df00 100644 --- a/mozglue/build/moz.build +++ b/mozglue/build/moz.build @@ -30,6 +30,11 @@ if CONFIG['OS_TARGET'] == 'Android': 'BionicGlue.cpp', ] +if CONFIG['MOZ_NUWA_PROCESS']: + EXPORTS.ipc += [ + 'Nuwa.h', + ] + EXPORTS.mozilla += [ 'SSE.h', 'arm.h', From 140b8112822aba72ff43c7c07cc25e8bec467030 Mon Sep 17 00:00:00 2001 From: "Thinker Lee ext:(%2C%20Cervantes%20Yu%20%3Ccyu%40mozilla.com%3E)" Date: Fri, 31 May 2013 21:16:54 +0800 Subject: [PATCH 14/23] Bug 771765 - Support template content process, part 2: IPC and glue changes. r=bent Changes include: * Getting/resetting platform thread ID. * Creating an IPC channel with existing file descriptor sent from the template process. * Child process host with existing process forked from the template. --- .../src/base/platform_thread_posix.cc | 4 ++ ipc/chromium/src/base/process_util_posix.cc | 3 + ipc/chromium/src/base/thread.h | 6 ++ .../src/chrome/common/child_process_host.cc | 18 +++++ .../src/chrome/common/child_process_host.h | 8 +++ ipc/chromium/src/chrome/common/ipc_channel.h | 7 +- .../src/chrome/common/ipc_channel_posix.cc | 17 ++++- .../src/chrome/common/ipc_channel_posix.h | 8 ++- ipc/glue/GeckoChildProcessHost.cpp | 72 ++++++++++++++++--- ipc/glue/GeckoChildProcessHost.h | 34 +++++++-- ipc/glue/Transport_posix.cpp | 2 +- 11 files changed, 159 insertions(+), 20 deletions(-) diff --git a/ipc/chromium/src/base/platform_thread_posix.cc b/ipc/chromium/src/base/platform_thread_posix.cc index e853c4ff4f1a..eb13b980ef83 100644 --- a/ipc/chromium/src/base/platform_thread_posix.cc +++ b/ipc/chromium/src/base/platform_thread_posix.cc @@ -49,7 +49,11 @@ PlatformThreadId PlatformThread::CurrentId() { mach_port_deallocate(mach_task_self(), port); return port; #elif defined(OS_LINUX) +#ifdef MOZ_WIDGET_GONK + return (intptr_t) (pthread_self()); +#else return syscall(__NR_gettid); +#endif #elif defined(OS_OPENBSD) || defined(__GLIBC__) return (intptr_t) (pthread_self()); #elif defined(OS_NETBSD) diff --git a/ipc/chromium/src/base/process_util_posix.cc b/ipc/chromium/src/base/process_util_posix.cc index 77959714aaa0..00d4ef51361e 100644 --- a/ipc/chromium/src/base/process_util_posix.cc +++ b/ipc/chromium/src/base/process_util_posix.cc @@ -248,6 +248,9 @@ bool DidProcessCrash(bool* child_exited, ProcessHandle handle) { int status; const int result = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); if (result == -1) { + // The dead process originally spawned from Nuwa might be taken as not + // crashed because the above waitpid() call returns -1 and ECHILD. The + // caller shouldn't behave incorrectly because of this false negative. LOG(ERROR) << "waitpid failed pid:" << handle << " errno:" << errno; if (child_exited) *child_exited = false; diff --git a/ipc/chromium/src/base/thread.h b/ipc/chromium/src/base/thread.h index ae1b513fab66..e3c732a44faa 100644 --- a/ipc/chromium/src/base/thread.h +++ b/ipc/chromium/src/base/thread.h @@ -106,6 +106,12 @@ class Thread : PlatformThread::Delegate { // The thread ID. PlatformThreadId thread_id() const { return thread_id_; } + // Reset thread ID as current thread. + PlatformThreadId reset_thread_id() { + thread_id_ = PlatformThread::CurrentId(); + return thread_id_; + } + // Returns true if the thread has been started, and not yet stopped. // When a thread is running, the thread_id_ is non-zero. bool IsRunning() const { return thread_id_ != 0; } diff --git a/ipc/chromium/src/chrome/common/child_process_host.cc b/ipc/chromium/src/chrome/common/child_process_host.cc index 87947ca85e88..3d590e5a2f87 100644 --- a/ipc/chromium/src/chrome/common/child_process_host.cc +++ b/ipc/chromium/src/chrome/common/child_process_host.cc @@ -12,6 +12,7 @@ #include "base/waitable_event.h" #include "mozilla/ipc/ProcessChild.h" #include "mozilla/ipc/BrowserProcessSubThread.h" +#include "mozilla/ipc/Transport.h" typedef mozilla::ipc::BrowserProcessSubThread ChromeThread; #include "chrome/common/ipc_logging.h" #include "chrome/common/notification_service.h" @@ -19,6 +20,7 @@ typedef mozilla::ipc::BrowserProcessSubThread ChromeThread; #include "chrome/common/process_watcher.h" #include "chrome/common/result_codes.h" +using mozilla::ipc::FileDescriptor; namespace { typedef std::list ChildProcessList; @@ -84,6 +86,22 @@ bool ChildProcessHost::CreateChannel() { return true; } +bool ChildProcessHost::CreateChannel(FileDescriptor& aFileDescriptor) { + if (channel_.get()) { + channel_->Close(); + } + channel_.reset(mozilla::ipc::OpenDescriptor( + aFileDescriptor, IPC::Channel::MODE_SERVER)); + channel_->set_listener(&listener_); + if (!channel_->Connect()) { + return false; + } + + opening_channel_ = true; + + return true; +} + void ChildProcessHost::SetHandle(base::ProcessHandle process) { #if defined(OS_WIN) process_event_.reset(new base::WaitableEvent(process)); diff --git a/ipc/chromium/src/chrome/common/child_process_host.h b/ipc/chromium/src/chrome/common/child_process_host.h index b35e4955ba62..796cdbb2a974 100644 --- a/ipc/chromium/src/chrome/common/child_process_host.h +++ b/ipc/chromium/src/chrome/common/child_process_host.h @@ -15,6 +15,12 @@ #include "chrome/common/child_process_info.h" #include "chrome/common/ipc_channel.h" +namespace mozilla { +namespace ipc { +class FileDescriptor; +} +} + class NotificationType; // Plugins/workers and other child processes that live on the IO thread should @@ -59,6 +65,8 @@ class ChildProcessHost : // Creates the IPC channel. Returns true iff it succeeded. bool CreateChannel(); + bool CreateChannel(mozilla::ipc::FileDescriptor& aFileDescriptor); + // Once the subclass gets a handle to the process, it needs to tell // ChildProcessHost using this function. void SetHandle(base::ProcessHandle handle); diff --git a/ipc/chromium/src/chrome/common/ipc_channel.h b/ipc/chromium/src/chrome/common/ipc_channel.h index 9b2e6a642410..2411cb8a8bfc 100644 --- a/ipc/chromium/src/chrome/common/ipc_channel.h +++ b/ipc/chromium/src/chrome/common/ipc_channel.h @@ -117,8 +117,11 @@ class Channel : public Message::Sender { // socketpair() in which case this method returns -1 for both parameters. void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const; - // Return the server side of the socketpair. - int GetServerFileDescriptor() const; + // Return the file descriptor for communication with the peer. + int GetFileDescriptor() const; + + // Reset the file descriptor for communication with the peer. + void ResetFileDescriptor(int fd); // Close the client side of the socketpair. void CloseClientFileDescriptor(); diff --git a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc index d0f045c1e806..24ba90c5ead2 100644 --- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc +++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc @@ -356,6 +356,15 @@ bool Channel::ChannelImpl::CreatePipe(const std::wstring& channel_id, return EnqueueHelloMessage(); } +/** + * Reset the file descriptor for communication with the peer. + */ +void Channel::ChannelImpl::ResetFileDescriptor(int fd) { + NS_ASSERTION(fd > 0 && fd == pipe_, "Invalid file descriptor"); + + EnqueueHelloMessage(); +} + bool Channel::ChannelImpl::EnqueueHelloMessage() { scoped_ptr msg(new Message(MSG_ROUTING_NONE, HELLO_MESSAGE_TYPE, @@ -971,8 +980,12 @@ void Channel::GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const { return channel_impl_->GetClientFileDescriptorMapping(src_fd, dest_fd); } -int Channel::GetServerFileDescriptor() const { - return channel_impl_->GetServerFileDescriptor(); +void Channel::ResetFileDescriptor(int fd) { + channel_impl_->ResetFileDescriptor(fd); +} + +int Channel::GetFileDescriptor() const { + return channel_impl_->GetFileDescriptor(); } void Channel::CloseClientFileDescriptor() { diff --git a/ipc/chromium/src/chrome/common/ipc_channel_posix.h b/ipc/chromium/src/chrome/common/ipc_channel_posix.h index d8d413923196..12d56ad4b798 100644 --- a/ipc/chromium/src/chrome/common/ipc_channel_posix.h +++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.h @@ -36,9 +36,11 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher { } bool Send(Message* message); void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const; - int GetServerFileDescriptor() const { - DCHECK(mode_ == MODE_SERVER); - return pipe_; + + void ResetFileDescriptor(int fd); + + int GetFileDescriptor() const { + return pipe_; } void CloseClientFileDescriptor(); diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index cc639771cc10..573e94339873 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -34,6 +34,10 @@ #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1" #endif +#include "nsTArray.h" +#include "nsClassHashtable.h" +#include "nsHashKeys.h" + using mozilla::MonitorAutoLock; using mozilla::ipc::GeckoChildProcessHost; @@ -89,11 +93,6 @@ GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType, #endif { MOZ_COUNT_CTOR(GeckoChildProcessHost); - - MessageLoop* ioLoop = XRE_GetIOMessageLoop(); - ioLoop->PostTask(FROM_HERE, - NewRunnableMethod(this, - &GeckoChildProcessHost::InitializeChannel)); } GeckoChildProcessHost::~GeckoChildProcessHost() @@ -287,7 +286,7 @@ GeckoChildProcessHost::SyncLaunch(std::vector aExtraOpts, int aTime ioLoop->PostTask(FROM_HERE, NewRunnableMethod(this, - &GeckoChildProcessHost::PerformAsyncLaunch, + &GeckoChildProcessHost::RunPerformAsyncLaunch, aExtraOpts, arch)); // NB: this uses a different mechanism than the chromium parent // class. @@ -322,7 +321,7 @@ GeckoChildProcessHost::AsyncLaunch(std::vector aExtraOpts) MessageLoop* ioLoop = XRE_GetIOMessageLoop(); ioLoop->PostTask(FROM_HERE, NewRunnableMethod(this, - &GeckoChildProcessHost::PerformAsyncLaunch, + &GeckoChildProcessHost::RunPerformAsyncLaunch, aExtraOpts, base::GetCurrentProcessArchitecture())); // This may look like the sync launch wait, but we only delay as @@ -343,7 +342,7 @@ GeckoChildProcessHost::LaunchAndWaitForProcessHandle(StringVector aExtraOpts) MessageLoop* ioLoop = XRE_GetIOMessageLoop(); ioLoop->PostTask(FROM_HERE, NewRunnableMethod(this, - &GeckoChildProcessHost::PerformAsyncLaunch, + &GeckoChildProcessHost::RunPerformAsyncLaunch, aExtraOpts, base::GetCurrentProcessArchitecture())); MonitorAutoLock lock(mMonitor); @@ -425,6 +424,14 @@ GeckoChildProcessHost::PerformAsyncLaunch(std::vector aExtraOpts, b return retval; } +bool +GeckoChildProcessHost::RunPerformAsyncLaunch(std::vector aExtraOpts, + base::ProcessArchitecture aArch) +{ + InitializeChannel(); + return PerformAsyncLaunch(aExtraOpts, aArch); +} + void #if defined(XP_WIN) AddAppDirToCommandLine(CommandLine& aCmdLine) @@ -833,3 +840,52 @@ GeckoChildProcessHost::OnWaitableEventSignaled(base::WaitableEvent *event) } ChildProcessHost::OnWaitableEventSignaled(event); } + +#ifdef MOZ_NUWA_PROCESS + +using mozilla::ipc::GeckoExistingProcessHost; +using mozilla::ipc::FileDescriptor; + +GeckoExistingProcessHost:: +GeckoExistingProcessHost(GeckoProcessType aProcessType, + base::ProcessHandle aProcess, + const FileDescriptor& aFileDescriptor, + ChildPrivileges aPrivileges) + : GeckoChildProcessHost(aProcessType, aPrivileges) + , mExistingProcessHandle(aProcess) + , mExistingFileDescriptor(aFileDescriptor) +{ + NS_ASSERTION(aFileDescriptor.IsValid(), + "Expected file descriptor to be valid"); +} + +GeckoExistingProcessHost::~GeckoExistingProcessHost() +{ +} + +bool +GeckoExistingProcessHost::PerformAsyncLaunch(StringVector aExtraOpts, + base::ProcessArchitecture aArch) +{ + SetHandle(mExistingProcessHandle); + + OpenPrivilegedHandle(base::GetProcId(mExistingProcessHandle)); + + MonitorAutoLock lock(mMonitor); + mProcessState = PROCESS_CREATED; + lock.Notify(); + + return true; +} + +void +GeckoExistingProcessHost::InitializeChannel() +{ + CreateChannel(mExistingFileDescriptor); + + MonitorAutoLock lock(mMonitor); + mProcessState = CHANNEL_INITIALIZED; + lock.Notify(); +} + +#endif /* MOZ_NUWA_PROCESS */ diff --git a/ipc/glue/GeckoChildProcessHost.h b/ipc/glue/GeckoChildProcessHost.h index 46c70307aef6..836333d5371f 100644 --- a/ipc/glue/GeckoChildProcessHost.h +++ b/ipc/glue/GeckoChildProcessHost.h @@ -11,6 +11,7 @@ #include "base/waitable_event.h" #include "chrome/common/child_process_host.h" +#include "mozilla/ipc/FileDescriptor.h" #include "mozilla/Monitor.h" #include "nsXULAppAPI.h" // for GeckoProcessType @@ -66,15 +67,15 @@ public: int32_t timeoutMs=0, base::ProcessArchitecture arch=base::GetCurrentProcessArchitecture()); - bool PerformAsyncLaunch(StringVector aExtraOpts=StringVector(), - base::ProcessArchitecture arch=base::GetCurrentProcessArchitecture()); + virtual bool PerformAsyncLaunch(StringVector aExtraOpts=StringVector(), + base::ProcessArchitecture aArch=base::GetCurrentProcessArchitecture()); virtual void OnChannelConnected(int32_t peer_pid); virtual void OnMessageReceived(const IPC::Message& aMsg); virtual void OnChannelError(); virtual void GetQueuedMessages(std::queue& queue); - void InitializeChannel(); + virtual void InitializeChannel(); virtual bool CanShutdown() { return true; } @@ -146,6 +147,8 @@ protected: task_t mChildTask; #endif + void OpenPrivilegedHandle(base::ProcessId aPid); + private: DISALLOW_EVIL_CONSTRUCTORS(GeckoChildProcessHost); @@ -153,7 +156,8 @@ private: bool PerformAsyncLaunchInternal(std::vector& aExtraOpts, base::ProcessArchitecture arch); - void OpenPrivilegedHandle(base::ProcessId aPid); + bool RunPerformAsyncLaunch(StringVector aExtraOpts=StringVector(), + base::ProcessArchitecture aArch=base::GetCurrentProcessArchitecture()); // In between launching the subprocess and handing off its IPC // channel, there's a small window of time in which *we* might still @@ -165,6 +169,28 @@ private: std::queue mQueue; }; +#ifdef MOZ_NUWA_PROCESS +class GeckoExistingProcessHost MOZ_FINAL : public GeckoChildProcessHost +{ +public: + GeckoExistingProcessHost(GeckoProcessType aProcessType, + base::ProcessHandle aProcess, + const FileDescriptor& aFileDescriptor, + ChildPrivileges aPrivileges=base::PRIVILEGES_DEFAULT); + + ~GeckoExistingProcessHost(); + + virtual bool PerformAsyncLaunch(StringVector aExtraOpts=StringVector(), + base::ProcessArchitecture aArch=base::GetCurrentProcessArchitecture()) MOZ_OVERRIDE; + + virtual void InitializeChannel() MOZ_OVERRIDE; + +private: + base::ProcessHandle mExistingProcessHandle; + mozilla::ipc::FileDescriptor mExistingFileDescriptor; +}; +#endif /* MOZ_NUWA_PROCESS */ + } /* namespace ipc */ } /* namespace mozilla */ diff --git a/ipc/glue/Transport_posix.cpp b/ipc/glue/Transport_posix.cpp index a9e53c5e8fc6..7ab47eb9f32a 100644 --- a/ipc/glue/Transport_posix.cpp +++ b/ipc/glue/Transport_posix.cpp @@ -29,7 +29,7 @@ CreateTransport(ProcessHandle /*unused*/, ProcessHandle /*unused*/, wstring id = ChildProcessInfo::GenerateRandomChannelID(aOne); // Use MODE_SERVER to force creation of the socketpair Transport t(id, Transport::MODE_SERVER, nullptr); - int fd1 = t.GetServerFileDescriptor(); + int fd1 = t.GetFileDescriptor(); int fd2, dontcare; t.GetClientFileDescriptorMapping(&fd2, &dontcare); if (fd1 < 0 || fd2 < 0) { From 42fe4d9b7dcc473db5f5e959e7e2705a5732e8e2 Mon Sep 17 00:00:00 2001 From: "Thinker Lee ext:(%2C%20Cervantes%20Yu%20%3Ccyu%40mozilla.com%3E)" Date: Fri, 31 May 2013 21:16:57 +0800 Subject: [PATCH 15/23] Bug 771765 - Support template content process, part 3: IPC glue and IPDL codegen to support protocol cloning. r=bent --- dom/ipc/Blob.h | 2 +- ipc/glue/ProtocolTypes.ipdlh | 18 ++++ ipc/glue/ProtocolUtils.cpp | 42 +++++++++ ipc/glue/ProtocolUtils.h | 128 +++++++++++++++++++++++++++- ipc/glue/moz.build | 1 + ipc/ipdl/ipdl/lower.py | 160 ++++++++++++++++++++++++++++++++--- 6 files changed, 333 insertions(+), 18 deletions(-) create mode 100644 ipc/glue/ProtocolTypes.ipdlh diff --git a/dom/ipc/Blob.h b/dom/ipc/Blob.h index cd77018402ef..e9109cb8c1b9 100644 --- a/dom/ipc/Blob.h +++ b/dom/ipc/Blob.h @@ -169,7 +169,7 @@ public: typedef typename BlobTraits::BaseType BaseType; typedef RemoteBlob RemoteBlobType; typedef mozilla::ipc::IProtocolManager< - mozilla::ipc::MessageListener>::ActorDestroyReason + mozilla::ipc::IProtocol>::ActorDestroyReason ActorDestroyReason; protected: diff --git a/ipc/glue/ProtocolTypes.ipdlh b/ipc/glue/ProtocolTypes.ipdlh new file mode 100644 index 000000000000..a304cd823871 --- /dev/null +++ b/ipc/glue/ProtocolTypes.ipdlh @@ -0,0 +1,18 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +namespace mozilla { +namespace ipc { + +struct ProtocolFdMapping +{ + uint32_t protocolId; + FileDescriptor fd; +}; + +} +} + diff --git a/ipc/glue/ProtocolUtils.cpp b/ipc/glue/ProtocolUtils.cpp index 3c5df7d012a4..2a6865944f14 100644 --- a/ipc/glue/ProtocolUtils.cpp +++ b/ipc/glue/ProtocolUtils.cpp @@ -17,6 +17,48 @@ using namespace IPC; namespace mozilla { namespace ipc { +IToplevelProtocol::~IToplevelProtocol() +{ + mOpenActors.clear(); +} + +void IToplevelProtocol::AddOpenedActor(IToplevelProtocol* aActor) +{ +#ifdef DEBUG + for (const IToplevelProtocol* actor = mOpenActors.getFirst(); + actor; + actor = actor->getNext()) { + NS_ASSERTION(actor != aActor, + "Open the same protocol for more than one time"); + } +#endif + + mOpenActors.insertBack(aActor); +} + +IToplevelProtocol* +IToplevelProtocol::CloneToplevel(const InfallibleTArray& aFds, + base::ProcessHandle aPeerProcess, + ProtocolCloneContext* aCtx) +{ + NS_NOTREACHED("Clone() for this protocol actor is not implemented"); + return nullptr; +} + +void +IToplevelProtocol::CloneOpenedToplevels(IToplevelProtocol* aTemplate, + const InfallibleTArray& aFds, + base::ProcessHandle aPeerProcess, + ProtocolCloneContext* aCtx) +{ + for (IToplevelProtocol* actor = aTemplate->GetFirstOpenedActors(); + actor; + actor = actor->getNext()) { + IToplevelProtocol* newactor = actor->CloneToplevel(aFds, aPeerProcess, aCtx); + AddOpenedActor(newactor); + } +} + class ChannelOpened : public IPC::Message { public: diff --git a/ipc/glue/ProtocolUtils.h b/ipc/glue/ProtocolUtils.h index 0a7638341ceb..e0adb0439cbe 100644 --- a/ipc/glue/ProtocolUtils.h +++ b/ipc/glue/ProtocolUtils.h @@ -19,6 +19,8 @@ #include "mozilla/ipc/FileDescriptor.h" #include "mozilla/ipc/Shmem.h" #include "mozilla/ipc/Transport.h" +#include "mozilla/ipc/MessageLink.h" +#include "mozilla/LinkedList.h" // WARNING: this takes into account the private, special-message-type // enum in ipc_channel.h. They need to be kept in sync. @@ -40,9 +42,18 @@ enum { } namespace mozilla { +namespace dom { +class ContentParent; +} + +namespace net { +class NeckoParent; +} + namespace ipc { -class MessageChannel; +class ProtocolFdMapping; +class ProtocolCloneContext; // Used to pass references to protocol actors across the wire. // Actors created on the parent-side have a positive ID, and actors @@ -70,6 +81,35 @@ struct Trigger int32_t mMsg; }; +class ProtocolCloneContext +{ + typedef mozilla::dom::ContentParent ContentParent; + typedef mozilla::net::NeckoParent NeckoParent; + + ContentParent* mContentParent; + NeckoParent* mNeckoParent; + +public: + ProtocolCloneContext() + : mContentParent(nullptr) + , mNeckoParent(nullptr) + {} + + void SetContentParent(ContentParent* aContentParent) + { + mContentParent = aContentParent; + } + + ContentParent* GetContentParent() { return mContentParent; } + + void SetNeckoParent(NeckoParent* aNeckoParent) + { + mNeckoParent = aNeckoParent; + } + + NeckoParent* GetNeckoParent() { return mNeckoParent; } +}; + template class /*NS_INTERFACE_CLASS*/ IProtocolManager { @@ -100,6 +140,90 @@ public: // XXX odd ducks, acknowledged virtual ProcessHandle OtherProcess() const = 0; virtual MessageChannel* GetIPCChannel() = 0; + + // The implementation of function is generated by code generator. + virtual void CloneManagees(ListenerT* aSource, + ProtocolCloneContext* aCtx) = 0; +}; + +typedef IPCMessageStart ProtocolId; + +/** + * All RPC protocols should implement this interface. + */ +class IProtocol : protected MessageListener +{ +public: + /** + * This function is used to clone this protocol actor. + * + * see IProtocol::CloneProtocol() + */ + virtual IProtocol* + CloneProtocol(MessageChannel* aChannel, + ProtocolCloneContext* aCtx) = 0; +}; + +/** + * All top-level protocols should inherit this class. + * + * IToplevelProtocol tracks all top-level protocol actors created from + * this protocol actor. + */ +class IToplevelProtocol : public LinkedListElement +{ +protected: + IToplevelProtocol(ProtocolId aProtoId) + : mProtocolId(aProtoId) + , mTrans(nullptr) + { + } + + ~IToplevelProtocol(); + + /** + * Add an actor to the list of actors that have been opened by this + * protocol. + */ + void AddOpenedActor(IToplevelProtocol* aActor); + +public: + void SetTransport(Transport* aTrans) + { + mTrans = aTrans; + } + + Transport* GetTransport() const { return mTrans; } + + ProtocolId GetProtocolId() const { return mProtocolId; } + + /** + * Return first of actors of top level protocols opened by this one. + */ + IToplevelProtocol* GetFirstOpenedActors() + { + return mOpenActors.getFirst(); + } + const IToplevelProtocol* GetFirstOpenedActors() const + { + return mOpenActors.getFirst(); + } + + virtual IToplevelProtocol* + CloneToplevel(const InfallibleTArray& aFds, + base::ProcessHandle aPeerProcess, + ProtocolCloneContext* aCtx); + + void CloneOpenedToplevels(IToplevelProtocol* aTemplate, + const InfallibleTArray& aFds, + base::ProcessHandle aPeerProcess, + ProtocolCloneContext* aCtx); + +private: + LinkedList mOpenActors; // All protocol actors opened by this. + + ProtocolId mProtocolId; + Transport* mTrans; }; @@ -120,8 +244,6 @@ MOZ_NEVER_INLINE void FatalError(const char* aProtocolName, const char* aMsg, base::ProcessHandle aHandle, bool aIsParent); -typedef IPCMessageStart ProtocolId; - struct PrivateIPDLInterface {}; bool diff --git a/ipc/glue/moz.build b/ipc/glue/moz.build index 3572412821b9..966fb330f096 100644 --- a/ipc/glue/moz.build +++ b/ipc/glue/moz.build @@ -96,6 +96,7 @@ CPP_SOURCES += [ IPDL_SOURCES = [ 'InputStreamParams.ipdlh', + 'ProtocolTypes.ipdlh', 'URIParams.ipdlh', ] diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index 17afa8368be8..59f758b5b3d5 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -1027,10 +1027,17 @@ class Protocol(ipdl.ast.Protocol): def fqListenerName(self): return 'mozilla::ipc::MessageListener' + def fqBaseClass(self): + return 'mozilla::ipc::IProtocol' + def managerInterfaceType(self, ptr=0): return Type('mozilla::ipc::IProtocolManager', ptr=ptr, - T=Type(self.fqListenerName())) + T=Type(self.fqBaseClass())) + + def openedProtocolInterfaceType(self, ptr=0): + return Type('mozilla::ipc::IToplevelProtocol', + ptr=ptr) def _ipdlmgrtype(self): assert 1 == len(self.decl.type.managers) @@ -1099,6 +1106,12 @@ class Protocol(ipdl.ast.Protocol): fn = ExprSelect(actorThis, '->', fn.name) return ExprCall(fn) + def cloneManagees(self): + return ExprVar('CloneManagees') + + def cloneProtocol(self): + return ExprVar('CloneProtocol') + def processingErrorVar(self): assert self.decl.type.isToplevel() return ExprVar('ProcessingError') @@ -1172,7 +1185,9 @@ class Protocol(ipdl.ast.Protocol): assert not self.decl.type.isToplevel() return ExprVar('mId') - def stateVar(self): + def stateVar(self, actorThis=None): + if actorThis is not None: + return ExprSelect(actorThis, '->', 'mState') return ExprVar('mState') def fqStateType(self): @@ -2452,13 +2467,16 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): def standardTypedefs(self): return [ + Typedef(Type(self.protocol.fqBaseClass()), 'ProtocolBase'), Typedef(Type('IPC::Message'), 'Message'), Typedef(Type(self.protocol.channelName()), 'Channel'), Typedef(Type(self.protocol.fqListenerName()), 'ChannelListener'), Typedef(Type('base::ProcessHandle'), 'ProcessHandle'), Typedef(Type('mozilla::ipc::MessageChannel'), 'MessageChannel'), Typedef(Type('mozilla::ipc::SharedMemory'), 'SharedMemory'), - Typedef(Type('mozilla::ipc::Trigger'), 'Trigger') + Typedef(Type('mozilla::ipc::Trigger'), 'Trigger'), + Typedef(Type('mozilla::ipc::ProtocolCloneContext'), + 'ProtocolCloneContext') ] @@ -2604,6 +2622,10 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): CppDirective('include', '"'+ p.channelHeaderFile() +'"'), Whitespace.NL ]) + optinherits = [] + if ptype.isToplevel(): + optinherits.append(Inherit(p.openedProtocolInterfaceType(), + viz='public')) if ptype.isToplevel() and self.side is 'parent': self.hdrfile.addthings([ _makeForwardDeclForQClass('nsIFile', []), @@ -2612,8 +2634,9 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): self.cls = Class( self.clsname, - inherits=[ Inherit(Type(p.fqListenerName()), viz='protected'), - Inherit(p.managerInterfaceType(), viz='protected') ], + inherits=[ Inherit(Type(p.fqBaseClass()), viz='public'), + Inherit(p.managerInterfaceType(), viz='protected') ] + + optinherits, abstract=True) bridgeActorsCreated = ProcessGraph.bridgeEndpointsOf(ptype, self.side) @@ -2778,6 +2801,10 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): ExprMemberInit(p.stateVar(), [ p.startState() ]) ] + if ptype.isToplevel(): + ctor.memberinits = [ExprMemberInit( + p.openedProtocolInterfaceType(), + [ _protocolId(ptype) ])] + ctor.memberinits else: ctor.memberinits = [ ExprMemberInit(p.idVar(), [ ExprLiteral.ZERO ]), @@ -3370,7 +3397,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): self.cls.addstmt(StmtDecl(Decl(p.channelType(), 'mChannel'))) if ptype.isToplevel(): self.cls.addstmts([ - StmtDecl(Decl(Type('IDMap', T=Type('ChannelListener')), + StmtDecl(Decl(Type('IDMap', T=Type('ProtocolBase')), p.actorMapVar().name)), StmtDecl(Decl(_actorIdType(), p.lastActorIdVar().name)), StmtDecl(Decl(Type('ProcessHandle'), @@ -3406,22 +3433,28 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): sizevar = ExprVar('aSize') typevar = ExprVar('aType') unsafevar = ExprVar('aUnsafe') - listenertype = Type('ChannelListener', ptr=1) + protocolbase = Type('ProtocolBase', ptr=1) + sourcevar = ExprVar('aSource') + ivar = ExprVar('i') + kidsvar = ExprVar('kids') + ithkid = ExprIndex(kidsvar, ivar) + clonecontexttype = Type('ProtocolCloneContext', ptr=1) + clonecontextvar = ExprVar('aCtx') register = MethodDefn(MethodDecl( p.registerMethod().name, - params=[ Decl(listenertype, routedvar.name) ], + params=[ Decl(protocolbase, routedvar.name) ], ret=_actorIdType(), virtual=1)) registerid = MethodDefn(MethodDecl( p.registerIDMethod().name, - params=[ Decl(listenertype, routedvar.name), + params=[ Decl(protocolbase, routedvar.name), Decl(_actorIdType(), idvar.name) ], ret=_actorIdType(), virtual=1)) lookup = MethodDefn(MethodDecl( p.lookupIDMethod().name, params=[ Decl(_actorIdType(), idvar.name) ], - ret=listenertype, virtual=1)) + ret=protocolbase, virtual=1)) unregister = MethodDefn(MethodDecl( p.unregisterMethod().name, params=[ Decl(_actorIdType(), idvar.name) ], @@ -3467,6 +3500,19 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): ret=Type('MessageChannel', ptr=1), virtual=1)) + clonemanagees = MethodDefn(MethodDecl( + p.cloneManagees().name, + params=[ Decl(protocolbase, sourcevar.name), + Decl(clonecontexttype, clonecontextvar.name) ], + virtual=1)) + + cloneprotocol = MethodDefn(MethodDecl( + p.cloneProtocol().name, + params=[ Decl(Type('Channel', ptr=True), 'aChannel'), + Decl(clonecontexttype, clonecontextvar.name) ], + ret=Type(p.fqBaseClass(), ptr=1), + virtual=1)) + if p.decl.type.isToplevel(): tmpvar = ExprVar('tmp') @@ -3683,13 +3729,82 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): p.callOtherProcess(p.managerVar()))) getchannel.addstmt(StmtReturn(p.channelVar())) + cloneprotocol.addstmts([ + _runtimeAbort('Clone() for ' + + p.name + + ' has not yet been implemented'), + StmtReturn(ExprLiteral.NULL) + ]) + + othervar = ExprVar('other') + managertype = Type(_actorName(p.name, self.side), ptr=1) + otherstmt = StmtDecl(Decl(managertype, + othervar.name), + init=ExprCast(sourcevar, + managertype, + static=1)) + clonemanagees.addstmt(otherstmt) + actorvar = ExprVar('actor') + for managee in p.managesStmts: + block = StmtBlock() + manageeipdltype = managee.decl.type + actortype = ipdl.type.ActorType(manageeipdltype) + manageecxxtype = _cxxBareType(actortype, self.side) + manageearray = p.managedVar(manageeipdltype, self.side) + abortstmt = StmtIf(ExprBinary(actorvar, '==', ExprLiteral.NULL)) + abortstmt.addifstmts([ + _runtimeAbort('can not clone an ' + actortype.name() + ' actor'), + StmtReturn()]) + forstmt = StmtFor( + init=Param(Type.UINT32, ivar.name, ExprLiteral.ZERO), + cond=ExprBinary(ivar, '<', _callCxxArrayLength(kidsvar)), + update=ExprPrefixUnop(ivar, '++')) + forstmt.addstmts([ + StmtExpr(ExprAssn( + actorvar, + ExprCast( + ExprCall( + ExprSelect(ithkid, + '->', + p.cloneProtocol().name), + args=[ p.channelForSubactor(), + clonecontextvar ]), + manageecxxtype, + static=1))), + abortstmt, + StmtExpr(ExprAssn(_actorId(actorvar), _actorId(ithkid))), + StmtExpr(ExprAssn(_actorManager(actorvar), ExprVar.THIS)), + StmtExpr(ExprAssn( + _actorChannel(actorvar), + p.channelForSubactor())), + StmtExpr(ExprAssn(_actorState(actorvar), _actorState(ithkid))), + StmtExpr(_callCxxArrayInsertSorted(manageearray, actorvar)), + StmtExpr(ExprCall(p.registerIDMethod(), + args=[actorvar, _actorId(actorvar)])), + StmtExpr(ExprCall( + ExprSelect(actorvar, + '->', + p.cloneManagees().name), + args=[ ithkid, clonecontextvar ])) + ]) + block.addstmts([ + StmtDecl(Decl(_cxxArrayType(manageecxxtype, ref=0), + kidsvar.name)), + StmtExpr(ExprAssn(kidsvar, + ExprSelect(othervar, + '->', + manageearray.name))), + StmtDecl(Decl(manageecxxtype, actorvar.name)), + forstmt]) + clonemanagees.addstmt(block) + # all protocols share the "same" RemoveManagee() implementation pvar = ExprVar('aProtocolId') listenervar = ExprVar('aListener') removemanagee = MethodDefn(MethodDecl( p.removeManageeMethod().name, params=[ Decl(_protocolIdType(), pvar.name), - Decl(listenertype, listenervar.name) ], + Decl(protocolbase, listenervar.name) ], virtual=1)) if not len(p.managesStmts): @@ -3735,6 +3850,8 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): destroyshmem, otherprocess, getchannel, + clonemanagees, + cloneprotocol, Whitespace.NL ] def makeShmemIface(self): @@ -3968,15 +4085,30 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): args=[ tdvar, modevar ])))) iffailopen.addifstmt(StmtReturn(_Result.ValuError)) - iffailalloc = StmtIf(ExprNot(ExprCall( - _allocMethod(actor.ptype, actor.side), - args=[ tvar, pidvar ]))) + pvar = ExprVar('p') + iffailalloc = StmtIf(ExprNot(ExprAssn( + pvar, + ExprCall( + _allocMethod(actor.ptype, actor.side), + args=[ tvar, pidvar ])))) iffailalloc.addifstmt(StmtReturn(_Result.ProcessingError)) + settrans = StmtExpr(ExprCall( + ExprSelect(pvar, '->', 'IToplevelProtocol::SetTransport'), + args=[tvar])) + + addopened = StmtExpr(ExprCall( + ExprVar('IToplevelProtocol::AddOpenedActor'), + args=[pvar])) + case.addstmts([ StmtDecl(Decl(Type('Transport', ptr=1), tvar.name)), + StmtDecl(Decl(Type(_actorName(actor.ptype.name(), self.side), + ptr=1), pvar.name)), iffailopen, iffailalloc, + settrans, + addopened, StmtBreak() ]) label = _messageStartName(actor.ptype) From 32bdb371afc4fa2525e0780574b5f7e8901ba493 Mon Sep 17 00:00:00 2001 From: "Thinker Lee ext:(%2C%20Cervantes%20Yu%20%3Ccyu%40mozilla.com%3E)" Date: Mon, 3 Jun 2013 18:14:37 +0800 Subject: [PATCH 16/23] Bug 771765 - Support template content process, part 4: Clone IPC protocol objects that will be up when the template process is ready. r=khuey, r=bent --- dom/indexedDB/ipc/IndexedDBParent.cpp | 18 ++++++++++ dom/indexedDB/ipc/IndexedDBParent.h | 4 +++ dom/ipc/ContentParent.h | 48 ++++++++++++++++++-------- dom/ipc/CrashReporterParent.cpp | 24 +++++++++++++ dom/ipc/CrashReporterParent.h | 3 ++ dom/src/storage/DOMStorageIPC.cpp | 13 +++++++ dom/src/storage/DOMStorageIPC.h | 4 +++ gfx/layers/ipc/CompositorParent.cpp | 44 +++++++++++++++++++++++ gfx/layers/ipc/CompositorParent.h | 6 ++++ gfx/layers/ipc/ImageBridgeParent.cpp | 18 ++++++++++ gfx/layers/ipc/ImageBridgeParent.h | 6 ++++ gfx/layers/ipc/PImageBridge.ipdl | 1 + hal/sandbox/SandboxHal.cpp | 13 +++++++ ipc/glue/GeckoChildProcessHost.h | 4 +++ ipc/glue/Transport.h | 4 +++ ipc/glue/Transport_posix.cpp | 11 ++++-- ipc/glue/Transport_win.cpp | 7 ++++ js/ipc/JavaScriptParent.cpp | 12 +++++++ js/ipc/JavaScriptParent.h | 3 ++ netwerk/cookie/CookieServiceParent.cpp | 12 +++++++ netwerk/cookie/CookieServiceParent.h | 4 +++ netwerk/ipc/NeckoParent.cpp | 20 +++++++++++ netwerk/ipc/NeckoParent.h | 16 ++++++++- 23 files changed, 278 insertions(+), 17 deletions(-) diff --git a/dom/indexedDB/ipc/IndexedDBParent.cpp b/dom/indexedDB/ipc/IndexedDBParent.cpp index 763b8e55c3d3..810b6a893203 100644 --- a/dom/indexedDB/ipc/IndexedDBParent.cpp +++ b/dom/indexedDB/ipc/IndexedDBParent.cpp @@ -112,6 +112,24 @@ IndexedDBParent::CheckWritePermission(const nsAString& aDatabaseName) return CheckPermissionInternal(aDatabaseName, permission); } +mozilla::ipc::IProtocol* +IndexedDBParent::CloneProtocol(Channel* aChannel, + mozilla::ipc::ProtocolCloneContext* aCtx) +{ + MOZ_ASSERT(mManagerContent != nullptr); + MOZ_ASSERT(mManagerTab == nullptr); + MOZ_ASSERT(!mDisconnected); + MOZ_ASSERT(IndexedDatabaseManager::Get()); + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + + ContentParent* contentParent = aCtx->GetContentParent(); + nsAutoPtr actor(contentParent->AllocPIndexedDBParent()); + if (!actor || !contentParent->RecvPIndexedDBConstructor(actor)) { + return nullptr; + } + return actor.forget(); +} + bool IndexedDBParent::CheckPermissionInternal(const nsAString& aDatabaseName, const nsDependentCString& aPermission) diff --git a/dom/indexedDB/ipc/IndexedDBParent.h b/dom/indexedDB/ipc/IndexedDBParent.h index 51d79b381c12..f0963637c72c 100644 --- a/dom/indexedDB/ipc/IndexedDBParent.h +++ b/dom/indexedDB/ipc/IndexedDBParent.h @@ -194,6 +194,10 @@ public: bool CheckWritePermission(const nsAString& aDatabaseName); + mozilla::ipc::IProtocol* + CloneProtocol(Channel* aChannel, + mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE; + protected: bool CheckPermissionInternal(const nsAString& aDatabaseName, diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index ef6f2df98b96..588180388de4 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -39,6 +39,7 @@ class TestShellParent; namespace jsipc { class JavaScriptParent; +class PJavaScriptParent; } namespace layers { @@ -171,6 +172,39 @@ public: */ void FriendlyName(nsAString& aName); + virtual PIndexedDBParent* AllocPIndexedDBParent() MOZ_OVERRIDE; + virtual bool + RecvPIndexedDBConstructor(PIndexedDBParent* aActor) MOZ_OVERRIDE; + + virtual PCrashReporterParent* + AllocPCrashReporterParent(const NativeThreadId& tid, + const uint32_t& processType) MOZ_OVERRIDE; + virtual bool + RecvPCrashReporterConstructor(PCrashReporterParent* actor, + const NativeThreadId& tid, + const uint32_t& processType) MOZ_OVERRIDE; + + virtual PNeckoParent* AllocPNeckoParent() MOZ_OVERRIDE; + virtual bool RecvPNeckoConstructor(PNeckoParent* aActor) MOZ_OVERRIDE { + return PContentParent::RecvPNeckoConstructor(aActor); + } + + virtual PHalParent* AllocPHalParent() MOZ_OVERRIDE; + virtual bool RecvPHalConstructor(PHalParent* aActor) MOZ_OVERRIDE { + return PContentParent::RecvPHalConstructor(aActor); + } + + virtual PStorageParent* AllocPStorageParent() MOZ_OVERRIDE; + virtual bool RecvPStorageConstructor(PStorageParent* aActor) MOZ_OVERRIDE { + return PContentParent::RecvPStorageConstructor(aActor); + } + virtual PJavaScriptParent* + AllocPJavaScriptParent() MOZ_OVERRIDE; + virtual bool + RecvPJavaScriptConstructor(PJavaScriptParent* aActor) MOZ_OVERRIDE { + return PContentParent::RecvPJavaScriptConstructor(aActor); + } + protected: void OnChannelConnected(int32_t pid) MOZ_OVERRIDE; virtual void ActorDestroy(ActorDestroyReason why); @@ -262,7 +296,6 @@ private: bool* aIsForBrowser) MOZ_OVERRIDE; virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline) MOZ_OVERRIDE; - virtual mozilla::jsipc::PJavaScriptParent* AllocPJavaScriptParent(); virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*); virtual PBrowserParent* AllocPBrowserParent(const IPCTabContext& aContext, @@ -275,33 +308,21 @@ private: virtual PBlobParent* AllocPBlobParent(const BlobConstructorParams& aParams); virtual bool DeallocPBlobParent(PBlobParent*); - virtual PCrashReporterParent* AllocPCrashReporterParent(const NativeThreadId& tid, - const uint32_t& processType); virtual bool DeallocPCrashReporterParent(PCrashReporterParent* crashreporter); - virtual bool RecvPCrashReporterConstructor(PCrashReporterParent* actor, - const NativeThreadId& tid, - const uint32_t& processType); virtual bool RecvGetRandomValues(const uint32_t& length, InfallibleTArray* randomValues); - virtual PHalParent* AllocPHalParent() MOZ_OVERRIDE; virtual bool DeallocPHalParent(PHalParent*) MOZ_OVERRIDE; - virtual PIndexedDBParent* AllocPIndexedDBParent(); - virtual bool DeallocPIndexedDBParent(PIndexedDBParent* aActor); - virtual bool - RecvPIndexedDBConstructor(PIndexedDBParent* aActor); - virtual PMemoryReportRequestParent* AllocPMemoryReportRequestParent(); virtual bool DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor); virtual PTestShellParent* AllocPTestShellParent(); virtual bool DeallocPTestShellParent(PTestShellParent* shell); - virtual PNeckoParent* AllocPNeckoParent(); virtual bool DeallocPNeckoParent(PNeckoParent* necko); virtual PExternalHelperAppParent* AllocPExternalHelperAppParent( @@ -320,7 +341,6 @@ private: virtual PTelephonyParent* AllocPTelephonyParent(); virtual bool DeallocPTelephonyParent(PTelephonyParent*); - virtual PStorageParent* AllocPStorageParent(); virtual bool DeallocPStorageParent(PStorageParent* aActor); virtual PBluetoothParent* AllocPBluetoothParent(); diff --git a/dom/ipc/CrashReporterParent.cpp b/dom/ipc/CrashReporterParent.cpp index 356c2d1c25cb..cd9bc4773287 100644 --- a/dom/ipc/CrashReporterParent.cpp +++ b/dom/ipc/CrashReporterParent.cpp @@ -4,6 +4,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "CrashReporterParent.h" +#include "mozilla/dom/ContentParent.h" #include "nsXULAppAPI.h" #include @@ -29,6 +30,29 @@ CrashReporterParent::RecvAppendAppNotes(const nsCString& data) return true; } +mozilla::ipc::IProtocol* +CrashReporterParent::CloneProtocol(Channel* aChannel, + mozilla::ipc::ProtocolCloneContext* aCtx) +{ + ContentParent* contentParent = aCtx->GetContentParent(); + CrashReporter::ThreadId childThreadId = contentParent->Pid(); + GeckoProcessType childProcessType = + contentParent->Process()->GetProcessType(); + + nsAutoPtr actor( + contentParent->AllocPCrashReporterParent(childThreadId, + childProcessType) + ); + if (!actor || + !contentParent->RecvPCrashReporterConstructor(actor, + childThreadId, + childThreadId)) { + return nullptr; + } + + return actor.forget(); +} + CrashReporterParent::CrashReporterParent() : #ifdef MOZ_CRASHREPORTER diff --git a/dom/ipc/CrashReporterParent.h b/dom/ipc/CrashReporterParent.h index 1af79583a686..62069430778d 100644 --- a/dom/ipc/CrashReporterParent.h +++ b/dom/ipc/CrashReporterParent.h @@ -80,6 +80,9 @@ public: } virtual bool RecvAppendAppNotes(const nsCString& data); + virtual mozilla::ipc::IProtocol* + CloneProtocol(Channel* aChannel, + mozilla::ipc::ProtocolCloneContext *aCtx) MOZ_OVERRIDE; #ifdef MOZ_CRASHREPORTER AnnotationTable mNotes; diff --git a/dom/src/storage/DOMStorageIPC.cpp b/dom/src/storage/DOMStorageIPC.cpp index 3826e85b5811..569a45d4773f 100644 --- a/dom/src/storage/DOMStorageIPC.cpp +++ b/dom/src/storage/DOMStorageIPC.cpp @@ -8,6 +8,7 @@ #include "DOMStorageManager.h" #include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/ContentParent.h" #include "mozilla/unused.h" #include "nsIDiskSpaceWatcher.h" #include "nsThreadUtils.h" @@ -364,6 +365,18 @@ DOMStorageDBParent::~DOMStorageDBParent() } } +mozilla::ipc::IProtocol* +DOMStorageDBParent::CloneProtocol(Channel* aChannel, + mozilla::ipc::ProtocolCloneContext* aCtx) +{ + ContentParent* contentParent = aCtx->GetContentParent(); + nsAutoPtr actor(contentParent->AllocPStorageParent()); + if (!actor || !contentParent->RecvPStorageConstructor(actor)) { + return nullptr; + } + return actor.forget(); +} + DOMStorageDBParent::CacheParentBridge* DOMStorageDBParent::NewCache(const nsACString& aScope) { diff --git a/dom/src/storage/DOMStorageIPC.h b/dom/src/storage/DOMStorageIPC.h index ce27635bd846..bb1ff3a840a2 100644 --- a/dom/src/storage/DOMStorageIPC.h +++ b/dom/src/storage/DOMStorageIPC.h @@ -112,6 +112,10 @@ public: DOMStorageDBParent(); virtual ~DOMStorageDBParent(); + virtual mozilla::ipc::IProtocol* + CloneProtocol(Channel* aChannel, + mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE; + NS_IMETHOD_(nsrefcnt) AddRef(void); NS_IMETHOD_(nsrefcnt) Release(void); diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index 6a83144979da..aa4ad8aeef1e 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -49,6 +49,7 @@ #include "mozilla/layers/CompositorD3D9.h" #endif #include "GeckoProfiler.h" +#include "mozilla/ipc/ProtocolTypes.h" using namespace base; using namespace mozilla; @@ -869,6 +870,12 @@ public: {} virtual ~CrossProcessCompositorParent(); + // IToplevelProtocol::CloneToplevel() + virtual IToplevelProtocol* + CloneToplevel(const InfallibleTArray& aFds, + base::ProcessHandle aPeerProcess, + mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE; + virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; // FIXME/bug 774388: work out what shutdown protocol we need. @@ -931,6 +938,24 @@ CompositorParent::Create(Transport* aTransport, ProcessId aOtherProcess) return true; } +IToplevelProtocol* +CompositorParent::CloneToplevel(const InfallibleTArray& aFds, + base::ProcessHandle aPeerProcess, + mozilla::ipc::ProtocolCloneContext* aCtx) +{ + for (unsigned int i = 0; i < aFds.Length(); i++) { + if (aFds[i].protocolId() == (unsigned)GetProtocolId()) { + Transport* transport = OpenDescriptor(aFds[i].fd(), + Transport::MODE_SERVER); + PCompositorParent* compositor = Create(transport, base::GetProcId(aPeerProcess)); + compositor->CloneManagees(this, aCtx); + compositor->IToplevelProtocol::SetTransport(transport); + return compositor; + } + } + return nullptr; +} + static void UpdateIndirectTree(uint64_t aId, Layer* aRoot, const TargetConfig& aTargetConfig) { @@ -1030,5 +1055,24 @@ CrossProcessCompositorParent::~CrossProcessCompositorParent() new DeleteTask(mTransport)); } +IToplevelProtocol* +CrossProcessCompositorParent::CloneToplevel(const InfallibleTArray& aFds, + base::ProcessHandle aPeerProcess, + mozilla::ipc::ProtocolCloneContext* aCtx) +{ + for (unsigned int i = 0; i < aFds.Length(); i++) { + if (aFds[i].protocolId() == (unsigned)GetProtocolId()) { + Transport* transport = OpenDescriptor(aFds[i].fd(), + Transport::MODE_SERVER); + PCompositorParent* compositor = + CompositorParent::Create(transport, base::GetProcId(aPeerProcess)); + compositor->CloneManagees(this, aCtx); + compositor->IToplevelProtocol::SetTransport(transport); + return compositor; + } + } + return nullptr; +} + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/ipc/CompositorParent.h b/gfx/layers/ipc/CompositorParent.h index c403b7753cf0..70c605919a60 100644 --- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -73,6 +73,12 @@ public: virtual ~CompositorParent(); + // IToplevelProtocol::CloneToplevel() + virtual IToplevelProtocol* + CloneToplevel(const InfallibleTArray& aFds, + base::ProcessHandle aPeerProcess, + mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE; + virtual bool RecvWillStop() MOZ_OVERRIDE; virtual bool RecvStop() MOZ_OVERRIDE; virtual bool RecvPause() MOZ_OVERRIDE; diff --git a/gfx/layers/ipc/ImageBridgeParent.cpp b/gfx/layers/ipc/ImageBridgeParent.cpp index dd58cd24672a..601476818c83 100644 --- a/gfx/layers/ipc/ImageBridgeParent.cpp +++ b/gfx/layers/ipc/ImageBridgeParent.cpp @@ -185,5 +185,23 @@ ImageBridgeParent::DeferredDestroy() // |this| was just destroyed, hands off } +IToplevelProtocol* +ImageBridgeParent::CloneToplevel(const InfallibleTArray& aFds, + base::ProcessHandle aPeerProcess, + mozilla::ipc::ProtocolCloneContext* aCtx) +{ + for (unsigned int i = 0; i < aFds.Length(); i++) { + if (aFds[i].protocolId() == (int)GetProtocolId()) { + Transport* transport = OpenDescriptor(aFds[i].fd(), + Transport::MODE_SERVER); + PImageBridgeParent* bridge = Create(transport, base::GetProcId(aPeerProcess)); + bridge->CloneManagees(this, aCtx); + bridge->IToplevelProtocol::SetTransport(transport); + return bridge; + } + } + return nullptr; +} + } // layers } // mozilla diff --git a/gfx/layers/ipc/ImageBridgeParent.h b/gfx/layers/ipc/ImageBridgeParent.h index 5f2b14d80213..dfe499d961a2 100644 --- a/gfx/layers/ipc/ImageBridgeParent.h +++ b/gfx/layers/ipc/ImageBridgeParent.h @@ -88,6 +88,12 @@ public: PImageBridgeParent::DeallocShmem(aShmem); } + // Overriden from IToplevelProtocol + IToplevelProtocol* + CloneToplevel(const InfallibleTArray& aFds, + base::ProcessHandle aPeerProcess, + mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE; + private: void DeferredDestroy(); diff --git a/gfx/layers/ipc/PImageBridge.ipdl b/gfx/layers/ipc/PImageBridge.ipdl index 0c59b96c8124..6b5a50a38d96 100644 --- a/gfx/layers/ipc/PImageBridge.ipdl +++ b/gfx/layers/ipc/PImageBridge.ipdl @@ -7,6 +7,7 @@ include LayersSurfaces; include LayersMessages; include protocol PGrallocBuffer; include protocol PCompositable; +include ProtocolTypes; include "mozilla/layers/CompositorTypes.h"; include "mozilla/GfxMessageUtils.h"; diff --git a/hal/sandbox/SandboxHal.cpp b/hal/sandbox/SandboxHal.cpp index 9c8d26b6e56d..96c309e1d5e4 100644 --- a/hal/sandbox/SandboxHal.cpp +++ b/hal/sandbox/SandboxHal.cpp @@ -7,6 +7,7 @@ #include "Hal.h" #include "mozilla/AppProcessChecker.h" #include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/ContentParent.h" #include "mozilla/hal_sandbox/PHalChild.h" #include "mozilla/hal_sandbox/PHalParent.h" #include "mozilla/dom/TabParent.h" @@ -815,6 +816,18 @@ public: hal::FactoryReset(); return true; } + + virtual mozilla::ipc::IProtocol* + CloneProtocol(Channel* aChannel, + mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE + { + ContentParent* contentParent = aCtx->GetContentParent(); + nsAutoPtr actor(contentParent->AllocPHalParent()); + if (!actor || !contentParent->RecvPHalConstructor(actor)) { + return nullptr; + } + return actor.forget(); + } }; class HalChild : public PHalChild { diff --git a/ipc/glue/GeckoChildProcessHost.h b/ipc/glue/GeckoChildProcessHost.h index 836333d5371f..3737dc4df75d 100644 --- a/ipc/glue/GeckoChildProcessHost.h +++ b/ipc/glue/GeckoChildProcessHost.h @@ -93,6 +93,10 @@ public: return mChildProcessHandle; } + GeckoProcessType GetProcessType() { + return mProcessType; + } + #ifdef XP_MACOSX task_t GetChildTask() { return mChildTask; diff --git a/ipc/glue/Transport.h b/ipc/glue/Transport.h index 34656bbcdda5..4126ed7a5674 100644 --- a/ipc/glue/Transport.h +++ b/ipc/glue/Transport.h @@ -20,6 +20,7 @@ namespace mozilla { namespace ipc { +class FileDescriptor; typedef IPC::Channel Transport; @@ -29,6 +30,9 @@ bool CreateTransport(base::ProcessHandle aProcOne, base::ProcessHandle aProcTwo, Transport* OpenDescriptor(const TransportDescriptor& aTd, Transport::Mode aMode); +Transport* OpenDescriptor(const FileDescriptor& aFd, + Transport::Mode aMode); + void CloseDescriptor(const TransportDescriptor& aTd); } // namespace ipc diff --git a/ipc/glue/Transport_posix.cpp b/ipc/glue/Transport_posix.cpp index 7ab47eb9f32a..6f41c2057a4d 100644 --- a/ipc/glue/Transport_posix.cpp +++ b/ipc/glue/Transport_posix.cpp @@ -12,6 +12,7 @@ #include "chrome/common/child_process_info.h" #include "mozilla/ipc/Transport.h" +#include "mozilla/ipc/FileDescriptor.h" using namespace base; using namespace std; @@ -44,8 +45,8 @@ CreateTransport(ProcessHandle /*unused*/, ProcessHandle /*unused*/, return false; } - aOne->mFd = FileDescriptor(fd1, true/*close after sending*/); - aTwo->mFd = FileDescriptor(fd2, true/*close after sending*/); + aOne->mFd = base::FileDescriptor(fd1, true/*close after sending*/); + aTwo->mFd = base::FileDescriptor(fd2, true/*close after sending*/); return true; } @@ -55,6 +56,12 @@ OpenDescriptor(const TransportDescriptor& aTd, Transport::Mode aMode) return new Transport(aTd.mFd.fd, aMode, nullptr); } +Transport* +OpenDescriptor(const FileDescriptor& aFd, Transport::Mode aMode) +{ + return new Transport(aFd.PlatformHandle(), aMode, nullptr); +} + void CloseDescriptor(const TransportDescriptor& aTd) { diff --git a/ipc/glue/Transport_win.cpp b/ipc/glue/Transport_win.cpp index 03352cb00760..f20f55774c41 100644 --- a/ipc/glue/Transport_win.cpp +++ b/ipc/glue/Transport_win.cpp @@ -58,6 +58,13 @@ OpenDescriptor(const TransportDescriptor& aTd, Transport::Mode aMode) return new Transport(aTd.mPipeName, aTd.mServerPipe, aMode, nullptr); } +Transport* +OpenDescriptor(const FileDescriptor& aFd, Transport::Mode aMode) +{ + NS_NOTREACHED("Not implemented!"); + return nullptr; +} + void CloseDescriptor(const TransportDescriptor& aTd) { diff --git a/js/ipc/JavaScriptParent.cpp b/js/ipc/JavaScriptParent.cpp index aa680b2f866f..23bf7cd06a99 100644 --- a/js/ipc/JavaScriptParent.cpp +++ b/js/ipc/JavaScriptParent.cpp @@ -19,6 +19,7 @@ using namespace js; using namespace JS; using namespace mozilla; using namespace mozilla::jsipc; +using namespace mozilla::dom; JavaScriptParent::JavaScriptParent() : refcount_(1), @@ -683,3 +684,14 @@ JavaScriptParent::domInstanceOf(JSObject *obj, int prototypeID, int depth, bool return true; } + +mozilla::ipc::IProtocol* +JavaScriptParent::CloneProtocol(Channel* aChannel, ProtocolCloneContext* aCtx) +{ + ContentParent *contentParent = aCtx->GetContentParent(); + nsAutoPtr actor(contentParent->AllocPJavaScriptParent()); + if (!actor || !contentParent->RecvPJavaScriptConstructor(actor)) { + return nullptr; + } + return actor.forget(); +} diff --git a/js/ipc/JavaScriptParent.h b/js/ipc/JavaScriptParent.h index d6d975622c2b..6bc60e5db480 100644 --- a/js/ipc/JavaScriptParent.h +++ b/js/ipc/JavaScriptParent.h @@ -77,6 +77,9 @@ class JavaScriptParent static bool DOMInstanceOf(JSObject *obj, int prototypeID, int depth, bool *bp); bool domInstanceOf(JSObject *obj, int prototypeID, int depth, bool *bp); + mozilla::ipc::IProtocol* + CloneProtocol(Channel* aChannel, ProtocolCloneContext* aCtx) MOZ_OVERRIDE; + protected: JSObject *unwrap(JSContext *cx, ObjectId objId); diff --git a/netwerk/cookie/CookieServiceParent.cpp b/netwerk/cookie/CookieServiceParent.cpp index 9a4b8ec7f39f..f12a0718a5d2 100644 --- a/netwerk/cookie/CookieServiceParent.cpp +++ b/netwerk/cookie/CookieServiceParent.cpp @@ -127,6 +127,18 @@ CookieServiceParent::RecvSetCookieString(const URIParams& aHost, return true; } +mozilla::ipc::IProtocol* +CookieServiceParent::CloneProtocol(Channel* aChannel, + mozilla::ipc::ProtocolCloneContext* aCtx) +{ + NeckoParent* manager = aCtx->GetNeckoParent(); + nsAutoPtr actor(manager->AllocPCookieServiceParent()); + if (!actor || !manager->RecvPCookieServiceConstructor(actor)) { + return nullptr; + } + return actor.forget(); +} + } } diff --git a/netwerk/cookie/CookieServiceParent.h b/netwerk/cookie/CookieServiceParent.h index 93ca1e6421fb..8a082a3464a2 100644 --- a/netwerk/cookie/CookieServiceParent.h +++ b/netwerk/cookie/CookieServiceParent.h @@ -43,6 +43,10 @@ protected: const IPC::SerializedLoadContext& loadContext) MOZ_OVERRIDE; + virtual mozilla::ipc::IProtocol* + CloneProtocol(Channel* aChannel, + mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE; + nsRefPtr mCookieService; }; diff --git a/netwerk/ipc/NeckoParent.cpp b/netwerk/ipc/NeckoParent.cpp index 53a12d853aca..3ce27d26722e 100644 --- a/netwerk/ipc/NeckoParent.cpp +++ b/netwerk/ipc/NeckoParent.cpp @@ -505,4 +505,24 @@ NeckoParent::RecvCancelHTMLDNSPrefetch(const nsString& hostname, return true; } +void +NeckoParent::CloneManagees(ProtocolBase* aSource, + mozilla::ipc::ProtocolCloneContext* aCtx) +{ + aCtx->SetNeckoParent(this); // For cloning protocols managed by this. + PNeckoParent::CloneManagees(aSource, aCtx); +} + +mozilla::ipc::IProtocol* +NeckoParent::CloneProtocol(Channel* aChannel, + mozilla::ipc::ProtocolCloneContext* aCtx) +{ + ContentParent* contentParent = aCtx->GetContentParent(); + nsAutoPtr actor(contentParent->AllocPNeckoParent()); + if (!actor || !contentParent->RecvPNeckoConstructor(actor)) { + return nullptr; + } + return actor.forget(); +} + }} // mozilla::net diff --git a/netwerk/ipc/NeckoParent.h b/netwerk/ipc/NeckoParent.h index 32299891ff7d..993a1730c17f 100644 --- a/netwerk/ipc/NeckoParent.h +++ b/netwerk/ipc/NeckoParent.h @@ -57,6 +57,16 @@ public: const SerializedLoadContext& aSerialized, nsCOMPtr &aResult); + virtual void + CloneManagees(ProtocolBase* aSource, + mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE; + virtual PCookieServiceParent* AllocPCookieServiceParent() MOZ_OVERRIDE; + virtual bool + RecvPCookieServiceConstructor(PCookieServiceParent* aActor) MOZ_OVERRIDE + { + return PNeckoParent::RecvPCookieServiceConstructor(aActor); + } + protected: virtual PHttpChannelParent* AllocPHttpChannelParent(PBrowserParent*, const SerializedLoadContext&, @@ -68,7 +78,6 @@ protected: const SerializedLoadContext& aSerialized, const HttpChannelCreationArgs& aOpenArgs); virtual bool DeallocPHttpChannelParent(PHttpChannelParent*); - virtual PCookieServiceParent* AllocPCookieServiceParent(); virtual bool DeallocPCookieServiceParent(PCookieServiceParent*); virtual PWyciwygChannelParent* AllocPWyciwygChannelParent(); virtual bool DeallocPWyciwygChannelParent(PWyciwygChannelParent*); @@ -112,6 +121,11 @@ protected: virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname, const uint16_t& flags, const nsresult& reason); + + virtual mozilla::ipc::IProtocol* + CloneProtocol(Channel* aChannel, + mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE; + private: nsCString mCoreAppsBasePath; nsCString mWebAppsBasePath; From f1e07724554eee6137803fa943a0b5faa3ed99df Mon Sep 17 00:00:00 2001 From: "Thinker Lee ext:(%2C%20Cervantes%20Yu%20%3Ccyu%40mozilla.com%3E)" Date: Mon, 3 Jun 2013 18:14:40 +0800 Subject: [PATCH 17/23] Bug 771765 - Support template content process, part 5: PContent protocol changes. r=khuey, r=bent Change the PContent protocol to support: * Creating the template process. * Forking new content processes from the template on the child side and cloning the actor on the parent side. * Fallback to non-templated content process creation if no template-preallocated process is available. --- dom/ipc/ContentChild.cpp | 216 ++++++++++++++++++++++ dom/ipc/ContentChild.h | 2 + dom/ipc/ContentParent.cpp | 357 ++++++++++++++++++++++++++++++++++--- dom/ipc/ContentParent.h | 27 ++- dom/ipc/ContentProcess.cpp | 2 +- dom/ipc/PContent.ipdl | 8 + 6 files changed, 590 insertions(+), 22 deletions(-) diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 7216f949eb14..72a985a8d299 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -90,6 +90,11 @@ #include "nsIAccessibilityService.h" #endif +#ifdef MOZ_NUWA_PROCESS +#include +#include "ipc/Nuwa.h" +#endif + #include "mozilla/dom/indexedDB/PIndexedDBChild.h" #include "mozilla/dom/mobilemessage/SmsChild.h" #include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h" @@ -131,6 +136,19 @@ using namespace mozilla::jsipc; using namespace mozilla::system; #endif +#ifdef MOZ_NUWA_PROCESS +static bool sNuwaForking = false; + +// The size of the reserved stack (in unsigned ints). It's used to reserve space +// to push sigsetjmp() in NuwaCheckpointCurrentThread() to higher in the stack +// so that after it returns and do other work we don't garble the stack we want +// to preserve in NuwaCheckpointCurrentThread(). +#define RESERVED_INT_STACK 128 + +// A sentinel value for checking whether RESERVED_INT_STACK is large enough. +#define STACK_SENTINEL_VALUE 0xdeadbeef +#endif + namespace mozilla { namespace dom { @@ -310,6 +328,10 @@ ContentChild::Init(MessageLoop* aIOLoop, XRE_InstallX11ErrorHandler(); #endif +#ifdef MOZ_NUWA_PROCESS + SetTransport(aChannel); +#endif + NS_ASSERTION(!sSingleton, "only one ContentChild per child"); Open(aChannel, aParentHandle, aIOLoop); @@ -324,6 +346,12 @@ ContentChild::Init(MessageLoop* aIOLoop, GetCPOWManager(); +#ifdef MOZ_NUWA_PROCESS + if (IsNuwaProcess()) { + SetProcessName(NS_LITERAL_STRING("(Nuwa)")); + return true; + } +#endif if (mIsForApp && !mIsForBrowser) { SetProcessName(NS_LITERAL_STRING("(Preallocated app)")); } else { @@ -1198,6 +1226,12 @@ ContentChild::RecvScreenSizeChanged(const gfxIntSize& size) bool ContentChild::RecvFlushMemory(const nsString& reason) { +#ifdef MOZ_NUWA_PROCESS + if (IsNuwaProcess()) { + // Don't flush memory in the nuwa process: the GC thread could be frozen. + return true; + } +#endif nsCOMPtr os = mozilla::services::GetObserverService(); if (os) @@ -1332,6 +1366,13 @@ ContentChild::RecvNotifyProcessPriorityChanged( bool ContentChild::RecvMinimizeMemoryUsage() { +#ifdef MOZ_NUWA_PROCESS + if (IsNuwaProcess()) { + // Don't minimize memory in the nuwa process: it will perform GC, but the + // GC thread could be frozen. + return true; + } +#endif nsCOMPtr mgr = do_GetService("@mozilla.org/memory-reporter-manager;1"); NS_ENSURE_TRUE(mgr, true); @@ -1406,5 +1447,180 @@ ContentChild::RecvUnregisterSheet(const URIParams& aURI, const uint32_t& aType) return true; } +#ifdef MOZ_NUWA_PROCESS +class CallNuwaSpawn : public nsRunnable +{ +public: + NS_IMETHOD Run() + { + NuwaSpawn(); + if (IsNuwaProcess()) { + return NS_OK; + } + + // In the new process. + ContentChild* child = ContentChild::GetSingleton(); + child->SetProcessName(NS_LITERAL_STRING("(Preallocated app)")); + mozilla::ipc::Transport* transport = child->GetTransport(); + int fd = transport->GetFileDescriptor(); + transport->ResetFileDescriptor(fd); + + IToplevelProtocol* toplevel = child->GetFirstOpenedActors(); + while (toplevel != nullptr) { + transport = toplevel->GetTransport(); + fd = transport->GetFileDescriptor(); + transport->ResetFileDescriptor(fd); + + toplevel = toplevel->getNext(); + } + return NS_OK; + } +}; + +static void +DoNuwaFork() +{ + NS_ASSERTION(NuwaSpawnPrepare != nullptr, + "NuwaSpawnPrepare() is not available!"); + NuwaSpawnPrepare(); // NuwaSpawn will be blocked. + + { + nsCOMPtr callSpawn(new CallNuwaSpawn()); + NS_DispatchToMainThread(callSpawn); + } + + // IOThread should be blocked here for waiting NuwaSpawn(). + NS_ASSERTION(NuwaSpawnWait != nullptr, + "NuwaSpawnWait() is not available!"); + NuwaSpawnWait(); // Now! NuwaSpawn can go. + // Here, we can make sure the spawning was finished. +} + +/** + * This function should keep IO thread in a stable state and freeze it + * until the spawning is finished. + */ +static void +RunNuwaFork() +{ + if (NuwaCheckpointCurrentThread()) { + DoNuwaFork(); + } +} +#endif + +bool +ContentChild::RecvNuwaFork() +{ +#ifdef MOZ_NUWA_PROCESS + if (sNuwaForking) { // No reentry. + return true; + } + sNuwaForking = true; + + MessageLoop* ioloop = XRE_GetIOMessageLoop(); + ioloop->PostTask(FROM_HERE, NewRunnableFunction(RunNuwaFork)); + return true; +#else + return false; // Makes the underlying IPC channel abort. +#endif +} + } // namespace dom } // namespace mozilla + +extern "C" { + +#if defined(MOZ_NUWA_PROCESS) +NS_EXPORT void +GetProtoFdInfos(NuwaProtoFdInfo* aInfoList, + size_t aInfoListSize, + size_t* aInfoSize) +{ + size_t i = 0; + + mozilla::dom::ContentChild* content = + mozilla::dom::ContentChild::GetSingleton(); + aInfoList[i].protoId = content->GetProtocolId(); + aInfoList[i].originFd = + content->GetTransport()->GetFileDescriptor(); + i++; + + for (IToplevelProtocol* actor = content->GetFirstOpenedActors(); + actor != nullptr; + actor = actor->getNext()) { + if (i >= aInfoListSize) { + NS_RUNTIMEABORT("Too many top level protocols!"); + } + + aInfoList[i].protoId = actor->GetProtocolId(); + aInfoList[i].originFd = + actor->GetTransport()->GetFileDescriptor(); + i++; + } + + if (i > NUWA_TOPLEVEL_MAX) { + NS_RUNTIMEABORT("Too many top level protocols!"); + } + *aInfoSize = i; +} + +class RunAddNewIPCProcess : public nsRunnable +{ +public: + RunAddNewIPCProcess(pid_t aPid, + nsTArray& aMaps) + : mPid(aPid) + { + mMaps.SwapElements(aMaps); + } + + NS_IMETHOD Run() + { + mozilla::dom::ContentChild::GetSingleton()-> + SendAddNewProcess(mPid, mMaps); + + MOZ_ASSERT(sNuwaForking); + sNuwaForking = false; + + return NS_OK; + } + +private: + pid_t mPid; + nsTArray mMaps; +}; + +/** + * AddNewIPCProcess() is called by Nuwa process to tell the parent + * process that a new process is created. + * + * In the newly created process, ResetContentChildTransport() is called to + * reset fd for the IPC Channel and the session. + */ +NS_EXPORT void +AddNewIPCProcess(pid_t aPid, NuwaProtoFdInfo* aInfoList, size_t aInfoListSize) +{ + nsTArray maps; + + for (size_t i = 0; i < aInfoListSize; i++) { + int _fd = aInfoList[i].newFds[NUWA_NEWFD_PARENT]; + mozilla::ipc::FileDescriptor fd(_fd); + mozilla::ipc::ProtocolFdMapping map(aInfoList[i].protoId, fd); + maps.AppendElement(map); + } + + nsRefPtr runner = new RunAddNewIPCProcess(aPid, maps); + NS_DispatchToMainThread(runner); +} + +NS_EXPORT void +OnNuwaProcessReady() +{ + mozilla::dom::ContentChild* content = + mozilla::dom::ContentChild::GetSingleton(); + content->SendNuwaReady(); +} +#endif // MOZ_NUWA_PROCESS + +} diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 27a6cbb947ea..fd841e9c9c3f 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -216,6 +216,8 @@ public: const bool& aIsMediaPresent, const bool& aIsSharing); + virtual bool RecvNuwaFork() MOZ_OVERRIDE; + virtual bool RecvNotifyProcessPriorityChanged(const hal::ProcessPriority& aPriority); virtual bool RecvMinimizeMemoryUsage(); virtual bool RecvCancelMinimizeMemoryUsage(); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 88e10ae7e27f..55368376406a 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -120,6 +120,10 @@ using namespace mozilla::system; #include "BluetoothService.h" #endif +#ifdef MOZ_NUWA_PROCESS +#include "ipc/Nuwa.h" +#endif + #include "JavaScriptParent.h" #ifdef MOZ_B2G_FM @@ -135,6 +139,8 @@ using namespace mozilla::system; static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); static const char* sClipboardTextFlavors[] = { kUnicodeMime }; +#define NUWA_FORK_WAIT_DURATION_MS 2000 // 2 seconds. + using base::ChildPrivileges; using base::KillProcess; using namespace mozilla::dom::bluetooth; @@ -318,15 +324,146 @@ static bool sCanLaunchSubprocesses; // The first content child has ID 1, so the chrome process can have ID 0. static uint64_t gContentChildID = 1; + +// sNuwaProcess points to the Nuwa process which is used for forking new +// processes later. +static StaticRefPtr sNuwaProcess; +// Nuwa process is ready for creating new process. +static bool sNuwaReady = false; +// The array containing the preallocated processes. 4 as the inline storage size +// should be enough so we don't need to grow the nsAutoTArray. +static StaticAutoPtr, 4> > sSpareProcesses; +static StaticAutoPtr > sNuwaForkWaitTasks; + // We want the prelaunched process to know that it's for apps, but not // actually for any app in particular. Use a magic manifest URL. // Can't be a static constant. #define MAGIC_PREALLOCATED_APP_MANIFEST_URL NS_LITERAL_STRING("{{template}}") +void +ContentParent::RunNuwaProcess() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (sNuwaProcess) { + NS_RUNTIMEABORT("sNuwaProcess is created twice."); + } + + sNuwaProcess = + new ContentParent(/* aApp = */ nullptr, + /* aIsForBrowser = */ false, + /* aIsForPreallocated = */ true, + // Final privileges are set when we + // transform into our app. + base::PRIVILEGES_INHERIT, + PROCESS_PRIORITY_BACKGROUND, + /* aIsNuwaProcess = */ true); + sNuwaProcess->Init(); +} + +#ifdef MOZ_NUWA_PROCESS +// initialization off the critical path of app startup. +static CancelableTask* sPreallocateAppProcessTask; +// This number is fairly arbitrary ... the intention is to put off +// launching another app process until the last one has finished +// loading its content, to reduce CPU/memory/IO contention. +static int sPreallocateDelayMs = 1000; + +static void +DelayedNuwaFork() +{ + MOZ_ASSERT(NS_IsMainThread()); + + sPreallocateAppProcessTask = nullptr; + + if (!sNuwaReady) { + if (!sNuwaProcess) { + ContentParent::RunNuwaProcess(); + } + // else sNuwaProcess is starting. It will SendNuwaFork() when ready. + } else if (sSpareProcesses->IsEmpty()) { + sNuwaProcess->SendNuwaFork(); + } +} + +static void +ScheduleDelayedNuwaFork() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (sPreallocateAppProcessTask) { + // Make sure there is only one request running. + return; + } + + sPreallocateAppProcessTask = NewRunnableFunction(DelayedNuwaFork); + MessageLoop::current()-> + PostDelayedTask(FROM_HERE, + sPreallocateAppProcessTask, + sPreallocateDelayMs); +} + +/** + * Get a spare ContentParent from sSpareProcesses list. + */ +static already_AddRefed +GetSpareProcess() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (sSpareProcesses->IsEmpty()) { + return nullptr; + } + + nsRefPtr process = sSpareProcesses->LastElement(); + sSpareProcesses->RemoveElementAt(sSpareProcesses->Length() - 1); + + if (sSpareProcesses->IsEmpty() && sNuwaReady) { + NS_ASSERTION(sNuwaProcess != nullptr, + "Nuwa process is not present!"); + ScheduleDelayedNuwaFork(); + } + + return process.forget(); +} + +/** + * Publish a ContentParent to spare process list. + */ +static void +PublishSpareProcess(ContentParent* aContent) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!sNuwaForkWaitTasks->IsEmpty()) { + sNuwaForkWaitTasks->ElementAt(0)->Cancel(); + sNuwaForkWaitTasks->RemoveElementAt(0); + } + + sSpareProcesses->AppendElement(aContent); +} + +static void +MaybeForgetSpare(ContentParent* aContent) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (sSpareProcesses->RemoveElement(aContent)) { + return; + } + + if (aContent == sNuwaProcess) { + sNuwaProcess = nullptr; + sNuwaReady = false; + ScheduleDelayedNuwaFork(); + } +} + +#endif + // PreallocateAppProcess is called by the PreallocatedProcessManager. // ContentParent then takes this process back within // MaybeTakePreallocatedAppProcess. - /*static*/ already_AddRefed ContentParent::PreallocateAppProcess() { @@ -347,7 +484,11 @@ ContentParent::MaybeTakePreallocatedAppProcess(const nsAString& aAppManifestURL, ChildPrivileges aPrivs, ProcessPriority aInitialPriority) { +#ifdef MOZ_NUWA_PROCESS + nsRefPtr process = GetSpareProcess(); +#else nsRefPtr process = PreallocatedProcessManager::Take(); +#endif if (!process) { return nullptr; } @@ -375,8 +516,17 @@ ContentParent::StartUp() sCanLaunchSubprocesses = true; + sSpareProcesses = new nsAutoTArray, 4>(); + ClearOnShutdown(&sSpareProcesses); + + sNuwaForkWaitTasks = new nsTArray(); + ClearOnShutdown(&sNuwaForkWaitTasks); +#ifdef MOZ_NUWA_PROCESS + ScheduleDelayedNuwaFork(); +#else // Try to preallocate a process that we can transform into an app later. PreallocatedProcessManager::AllocateAfterDelay(); +#endif } /*static*/ void @@ -627,7 +777,7 @@ ContentParent::GetAll(nsTArray& aArray) } for (ContentParent* cp = sContentParents->getFirst(); cp; - cp = cp->getNext()) { + cp = cp->LinkedListElement::getNext()) { if (cp->mIsAlive) { aArray.AppendElement(cp); } @@ -644,7 +794,7 @@ ContentParent::GetAllEvenIfDead(nsTArray& aArray) } for (ContentParent* cp = sContentParents->getFirst(); cp; - cp = cp->getNext()) { + cp = cp->LinkedListElement::getNext()) { aArray.AppendElement(cp); } } @@ -931,6 +1081,28 @@ ContentParent::MarkAsDead() mIsAlive = false; } +void +ContentParent::OnNuwaForkTimeout() +{ + if (!sNuwaForkWaitTasks->IsEmpty()) { + sNuwaForkWaitTasks->RemoveElementAt(0); + } + + // We haven't RecvAddNewProcess() after SendNuwaFork(). Maybe the main + // thread of the Nuwa process is in deadlock. + MOZ_ASSERT(false, "Can't fork from the nuwa process."); +} + +void +ContentParent::OnChannelError() +{ + nsRefPtr content(this); + PContentParent::OnChannelError(); +#ifdef MOZ_NUWA_PROCESS + MaybeForgetSpare(this); +#endif +} + void ContentParent::OnChannelConnected(int32_t pid) { @@ -1197,24 +1369,32 @@ ContentParent::GetTestShellSingleton() return static_cast(ManagedPTestShellParent()[0]); } +void +ContentParent::InitializeMembers() +{ + mSubprocess = nullptr; + mChildID = gContentChildID++; + mGeolocationWatchID = -1; + mForceKillTask = nullptr; + mNumDestroyingTabs = 0; + mIsAlive = true; + mSendPermissionUpdates = false; + mCalledClose = false; + mCalledCloseWithError = false; + mCalledKillHard = false; +} + ContentParent::ContentParent(mozIApplication* aApp, bool aIsForBrowser, bool aIsForPreallocated, ChildPrivileges aOSPrivileges, - ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */) - : mSubprocess(nullptr) - , mOSPrivileges(aOSPrivileges) - , mChildID(gContentChildID++) - , mGeolocationWatchID(-1) - , mForceKillTask(nullptr) - , mNumDestroyingTabs(0) - , mIsAlive(true) - , mSendPermissionUpdates(false) + ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */, + bool aIsNuwaProcess /* = false */) + : mOSPrivileges(aOSPrivileges) , mIsForBrowser(aIsForBrowser) - , mCalledClose(false) - , mCalledCloseWithError(false) - , mCalledKillHard(false) { + InitializeMembers(); // Perform common initialization. + // No more than one of !!aApp, aIsForBrowser, and aIsForPreallocated should // be true. MOZ_ASSERT(!!aApp + aIsForBrowser + aIsForPreallocated <= 1); @@ -1223,7 +1403,9 @@ ContentParent::ContentParent(mozIApplication* aApp, if (!sContentParents) { sContentParents = new LinkedList(); } - sContentParents->insertBack(this); + if (!aIsNuwaProcess) { + sContentParents->insertBack(this); + } if (aApp) { aApp->GetManifestURL(mAppManifestURL); @@ -1240,7 +1422,13 @@ ContentParent::ContentParent(mozIApplication* aApp, mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, aOSPrivileges); - mSubprocess->LaunchAndWaitForProcessHandle(); + IToplevelProtocol::SetTransport(mSubprocess->GetChannel()); + + std::vector extraArgs; + if (aIsNuwaProcess) { + extraArgs.push_back("-nuwa"); + } + mSubprocess->LaunchAndWaitForProcessHandle(extraArgs); Open(mSubprocess->GetChannel(), mSubprocess->GetChildProcessHandle()); @@ -1316,6 +1504,78 @@ ContentParent::ContentParent(mozIApplication* aApp, } +#ifdef MOZ_NUWA_PROCESS +static const FileDescriptor* +FindFdProtocolFdMapping(const nsTArray& aFds, + ProtocolId aProtoId) +{ + for (unsigned int i = 0; i < aFds.Length(); i++) { + if (aFds[i].protocolId() == aProtoId) { + return &aFds[i].fd(); + } + } + return nullptr; +} + +/** + * This constructor is used for new content process cloned from a template. + * + * For Nuwa. + */ +ContentParent::ContentParent(ContentParent* aTemplate, + const nsAString& aAppManifestURL, + base::ProcessHandle aPid, + const nsTArray& aFds, + ChildPrivileges aOSPrivileges) + : mOSPrivileges(aOSPrivileges) + , mAppManifestURL(aAppManifestURL) + , mIsForBrowser(false) +{ + InitializeMembers(); // Perform common initialization. + + sContentParents->insertBack(this); + + // From this point on, NS_WARNING, NS_ASSERTION, etc. should print out the + // PID along with the warning. + nsDebugImpl::SetMultiprocessMode("Parent"); + + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + const FileDescriptor* fd = FindFdProtocolFdMapping(aFds, GetProtocolId()); + + NS_ASSERTION(fd != nullptr, "IPC Channel for PContent is necessary!"); + mSubprocess = new GeckoExistingProcessHost(GeckoProcessType_Content, + aPid, + *fd, + aOSPrivileges); + + mSubprocess->LaunchAndWaitForProcessHandle(); + + // Clone actors routed by aTemplate for this instance. + IToplevelProtocol::SetTransport(mSubprocess->GetChannel()); + ProtocolCloneContext cloneContext; + cloneContext.SetContentParent(this); + CloneManagees(aTemplate, &cloneContext); + CloneOpenedToplevels(aTemplate, aFds, aPid, &cloneContext); + + Open(mSubprocess->GetChannel(), + mSubprocess->GetChildProcessHandle()); + + // Set the subprocess's priority (bg if we're a preallocated process, fg + // otherwise). We do this first because we're likely /lowering/ its CPU and + // memory priority, which it has inherited from this process. + ProcessPriority priority; + if (IsPreallocated()) { + priority = PROCESS_PRIORITY_BACKGROUND; + } else { + priority = PROCESS_PRIORITY_FOREGROUND; + } + + ProcessPriorityManager::SetProcessPriority(this, priority); + mMessageManager = nsFrameMessageManager::NewProcessMessageManager(this); +} +#endif // MOZ_NUWA_PROCESS + ContentParent::~ContentParent() { if (mForceKillTask) { @@ -1568,12 +1828,19 @@ ContentParent::RecvGetShowPasswordSetting(bool* showPassword) bool ContentParent::RecvFirstIdle() { - // When the ContentChild goes idle, it sends us a FirstIdle message which we - // use as an indicator that it's a good time to prelaunch another process. - // If we prelaunch any sooner than this, then we'll be competing with the +#ifdef MOZ_NUWA_PROCESS + if (sSpareProcesses->IsEmpty() && sNuwaReady) { + ScheduleDelayedNuwaFork(); + } + return true; +#else + // When the ContentChild goes idle, it sends us a FirstIdle message + // which we use as a good time to prelaunch another process. If we + // prelaunch any sooner than this, then we'll be competing with the // child process and slowing it down. PreallocatedProcessManager::AllocateAfterDelay(); return true; +#endif } bool @@ -1669,6 +1936,56 @@ ContentParent::RecvRecordingDeviceEvents(const nsString& aRecordingStatus) return true; } +bool +ContentParent::SendNuwaFork() +{ + if (this != sNuwaProcess) { + return false; + } + + CancelableTask* nuwaForkTimeoutTask = NewRunnableMethod( + this, &ContentParent::OnNuwaForkTimeout); + sNuwaForkWaitTasks->AppendElement(nuwaForkTimeoutTask); + + MessageLoop::current()-> + PostDelayedTask(FROM_HERE, + nuwaForkTimeoutTask, + NUWA_FORK_WAIT_DURATION_MS); + + return PContentParent::SendNuwaFork(); +} + +bool +ContentParent::RecvNuwaReady() +{ + NS_ASSERTION(!sNuwaReady, "Multiple Nuwa processes created!"); + ProcessPriorityManager::SetProcessPriority(sNuwaProcess, + hal::PROCESS_PRIORITY_FOREGROUND); + sNuwaReady = true; + SendNuwaFork(); + return true; +} + +bool +ContentParent::RecvAddNewProcess(const uint32_t& aPid, + const InfallibleTArray& aFds) +{ +#ifdef MOZ_NUWA_PROCESS + nsRefPtr content; + content = new ContentParent(this, + MAGIC_PREALLOCATED_APP_MANIFEST_URL, + aPid, + aFds, + base::PRIVILEGES_DEFAULT); + content->Init(); + PublishSpareProcess(content); + return true; +#else + NS_ERROR("ContentParent::RecvAddNewProcess() not implemented!"); + return false; +#endif +} + NS_IMPL_ISUPPORTS3(ContentParent, nsIObserver, nsIThreadObserver, diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 588180388de4..a7c7be02de37 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -92,6 +92,8 @@ public: */ static already_AddRefed PreallocateAppProcess(); + static void RunNuwaProcess(); + /** * Get or create a content process for the given TabContext. aFrameElement * should be the frame/iframe element with which this process will @@ -172,6 +174,8 @@ public: */ void FriendlyName(nsAString& aName); + virtual void OnChannelError() MOZ_OVERRIDE; + virtual PIndexedDBParent* AllocPIndexedDBParent() MOZ_OVERRIDE; virtual bool RecvPIndexedDBConstructor(PIndexedDBParent* aActor) MOZ_OVERRIDE; @@ -198,6 +202,7 @@ public: virtual bool RecvPStorageConstructor(PStorageParent* aActor) MOZ_OVERRIDE { return PContentParent::RecvPStorageConstructor(aActor); } + virtual PJavaScriptParent* AllocPJavaScriptParent() MOZ_OVERRIDE; virtual bool @@ -205,9 +210,12 @@ public: return PContentParent::RecvPJavaScriptConstructor(aActor); } + virtual bool SendNuwaFork(); + protected: void OnChannelConnected(int32_t pid) MOZ_OVERRIDE; virtual void ActorDestroy(ActorDestroyReason why); + void OnNuwaForkTimeout(); bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE; @@ -241,7 +249,19 @@ private: bool aIsForBrowser, bool aIsForPreallocated, ChildPrivileges aOSPrivileges = base::PRIVILEGES_DEFAULT, - hal::ProcessPriority aInitialPriority = hal::PROCESS_PRIORITY_FOREGROUND); + hal::ProcessPriority aInitialPriority = hal::PROCESS_PRIORITY_FOREGROUND, + bool aIsNuwaProcess = false); + +#ifdef MOZ_NUWA_PROCESS + ContentParent(ContentParent* aTemplate, + const nsAString& aAppManifestURL, + base::ProcessHandle aPid, + const nsTArray& aFds, + ChildPrivileges aOSPrivileges = base::PRIVILEGES_DEFAULT); +#endif + + // The common initialization for the constructors. + void InitializeMembers(); virtual ~ContentParent(); @@ -451,6 +471,11 @@ private: virtual bool RecvSystemMessageHandled() MOZ_OVERRIDE; + virtual bool RecvNuwaReady() MOZ_OVERRIDE; + + virtual bool RecvAddNewProcess(const uint32_t& aPid, + const InfallibleTArray& aFds) MOZ_OVERRIDE; + virtual bool RecvCreateFakeVolume(const nsString& fsName, const nsString& mountPoint) MOZ_OVERRIDE; virtual bool RecvSetFakeVolumeState(const nsString& fsName, const int32_t& fsState) MOZ_OVERRIDE; diff --git a/dom/ipc/ContentProcess.cpp b/dom/ipc/ContentProcess.cpp index 455b9303ee17..bf8e190e63d1 100644 --- a/dom/ipc/ContentProcess.cpp +++ b/dom/ipc/ContentProcess.cpp @@ -37,5 +37,5 @@ ContentProcess::CleanUp() mXREEmbed.Stop(); } -} // namespace tabs +} // namespace dom } // namespace mozilla diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index cb9e440671ac..bed1599f09aa 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -28,6 +28,7 @@ include JavaScriptTypes; include InputStreamParams; include PTabContext; include URIParams; +include ProtocolTypes; include "mozilla/chrome/RegistryMessageUtils.h"; include "mozilla/dom/PermissionMessageUtils.h"; @@ -310,6 +311,9 @@ child: int32_t mountGeneration, bool isMediaPresent, bool isSharing); + // Ask the Nuwa process to create a new child process. + NuwaFork(); + NotifyProcessPriorityChanged(ProcessPriority priority); MinimizeMemoryUsage(); CancelMinimizeMemoryUsage(); @@ -463,6 +467,10 @@ parent: // Notify the parent that the child has finished handling a system message. async SystemMessageHandled(); + NuwaReady(); + + sync AddNewProcess(uint32_t pid, ProtocolFdMapping[] aFds); + // called by the child (test code only) to propagate volume changes to the parent async CreateFakeVolume(nsString fsName, nsString mountPoint); async SetFakeVolumeState(nsString fsName, int32_t fsState); From f5213d3e6666c532cbcd20837188d3b0fbcc950d Mon Sep 17 00:00:00 2001 From: "Thinker Lee ext:(%2C%20Cervantes%20Yu%20%3Ccyu%40mozilla.com%3E)" Date: Mon, 3 Jun 2013 18:14:42 +0800 Subject: [PATCH 18/23] Bug 771765 - Support template content process, part 6: support re-creation of the threads created in the template process. r=khuey, r=jorendorff The threads that are frozen/recreated include: * ImageBridgeChildThread. * Image decoding thread pool. * IPC thread (checkpointed, but not frozen). * GC Helper thread. * XPC runtime watchdog thread. * Socket transport service thread/thread pool. * Memory pressure watcher. * Timer thread. * DOM promise worker. --- dom/workers/RuntimeService.cpp | 12 ++++++ gfx/layers/ipc/ImageBridgeChild.cpp | 14 +++++++ image/src/RasterImage.cpp | 41 +++++++++++++++++++ .../src/chrome/common/child_thread.cc | 23 ++++++++++- ipc/chromium/src/chrome/common/child_thread.h | 4 ++ js/src/configure.in | 4 ++ js/src/jsgc.cpp | 15 +++++++ js/xpconnect/src/XPCJSRuntime.cpp | 13 ++++++ .../base/src/nsSocketTransportService2.cpp | 12 ++++++ netwerk/base/src/nsStreamTransportService.cpp | 37 +++++++++++++++++ widget/gonk/GonkMemoryPressureMonitoring.cpp | 12 ++++++ xpcom/base/nsCycleCollector.cpp | 4 ++ xpcom/threads/TimerThread.cpp | 12 ++++++ 13 files changed, 202 insertions(+), 1 deletion(-) diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index a2a1465a2e1b..d89e083f3f42 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -45,6 +45,10 @@ #include "Worker.h" #include "WorkerPrivate.h" +#ifdef MOZ_NUWA_PROCESS +#include "ipc/Nuwa.h" +#endif + using namespace mozilla; using namespace mozilla::dom; @@ -893,6 +897,14 @@ public: NS_IMETHOD Run() { +#ifdef MOZ_NUWA_PROCESS + if (IsNuwaProcess()) { + NS_ASSERTION(NuwaMarkCurrentThread != nullptr, + "NuwaMarkCurrentThread is undefined!"); + NuwaMarkCurrentThread(nullptr, nullptr); + NuwaFreezeCurrentThread(); + } +#endif WorkerPrivate* workerPrivate = mWorkerPrivate; mWorkerPrivate = nullptr; diff --git a/gfx/layers/ipc/ImageBridgeChild.cpp b/gfx/layers/ipc/ImageBridgeChild.cpp index 137187a461b8..ad18f5c3685a 100644 --- a/gfx/layers/ipc/ImageBridgeChild.cpp +++ b/gfx/layers/ipc/ImageBridgeChild.cpp @@ -361,6 +361,10 @@ ConnectImageBridgeInChildProcess(Transport* aTransport, ipc::ChildSide); } +#ifdef MOZ_NUWA_PROCESS +#include "ipc/Nuwa.h" +#endif + static void ReleaseImageClientNow(ImageClient* aClient) { MOZ_ASSERT(InImageBridgeChildThread()); @@ -537,6 +541,16 @@ ImageBridgeChild::StartUpInChildProcess(Transport* aTransport, return false; } +#ifdef MOZ_NUWA_PROCESS + if (IsNuwaProcess()) { + sImageBridgeChildThread + ->message_loop()->PostTask(FROM_HERE, + NewRunnableFunction(NuwaMarkCurrentThread, + (void (*)(void *))nullptr, + (void *)nullptr)); + } +#endif + sImageBridgeChildSingleton = new ImageBridgeChild(); sImageBridgeChildSingleton->GetMessageLoop()->PostTask( FROM_HERE, diff --git a/image/src/RasterImage.cpp b/image/src/RasterImage.cpp index 915112ab9cf9..b3eb18dc6f03 100644 --- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -43,6 +43,10 @@ #include "gfx2DGlue.h" #include +#ifdef MOZ_NUWA_PROCESS +#include "ipc/Nuwa.h" +#endif + using namespace mozilla; using namespace mozilla::image; using namespace mozilla::layers; @@ -3055,6 +3059,37 @@ RasterImage::DecodePool::GetEventTarget() return target.forget(); } +#ifdef MOZ_NUWA_PROCESS + +class RIDThreadPoolListener : public nsIThreadPoolListener +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSITHREADPOOLLISTENER + + RIDThreadPoolListener() {} + ~RIDThreadPoolListener() {} +}; + +NS_IMPL_ISUPPORTS1(RIDThreadPoolListener, nsIThreadPoolListener) + +NS_IMETHODIMP +RIDThreadPoolListener::OnThreadCreated() +{ + if (IsNuwaProcess()) { + NuwaMarkCurrentThread((void (*)(void *))nullptr, nullptr); + } + return NS_OK; +} + +NS_IMETHODIMP +RIDThreadPoolListener::OnThreadShuttingDown() +{ + return NS_OK; +} + +#endif // MOZ_NUWA_PROCESS + RasterImage::DecodePool::DecodePool() : mThreadPoolMutex("Thread Pool") { @@ -3072,6 +3107,12 @@ RasterImage::DecodePool::DecodePool() mThreadPool->SetThreadLimit(limit); mThreadPool->SetIdleThreadLimit(limit); +#ifdef MOZ_NUWA_PROCESS + if (IsNuwaProcess()) { + mThreadPool->SetListener(new RIDThreadPoolListener()); + } +#endif + nsCOMPtr obsSvc = mozilla::services::GetObserverService(); if (obsSvc) { obsSvc->AddObserver(this, "xpcom-shutdown-threads", false); diff --git a/ipc/chromium/src/chrome/common/child_thread.cc b/ipc/chromium/src/chrome/common/child_thread.cc index 950407d4229c..73aec54ca95f 100644 --- a/ipc/chromium/src/chrome/common/child_thread.cc +++ b/ipc/chromium/src/chrome/common/child_thread.cc @@ -26,14 +26,35 @@ ChildThread::ChildThread(Thread::Options options) ChildThread::~ChildThread() { } +#ifdef MOZ_NUWA_PROCESS +#include "ipc/Nuwa.h" +#endif + bool ChildThread::Run() { - return StartWithOptions(options_); + bool r = StartWithOptions(options_); +#ifdef MOZ_NUWA_PROCESS + NS_ASSERTION(NuwaMarkCurrentThread, "NuwaMarkCurrentThread is not defined!"); + if (IsNuwaProcess()) { + message_loop()->PostTask(FROM_HERE, + NewRunnableFunction(&ChildThread::MarkThread)); + } +#endif + return r; } void ChildThread::OnChannelError() { owner_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); } +#ifdef MOZ_NUWA_PROCESS +void ChildThread::MarkThread() { + NuwaMarkCurrentThread(nullptr, nullptr); + if (!NuwaCheckpointCurrentThread()) { + NS_RUNTIMEABORT("Should not be here!"); + } +} +#endif + bool ChildThread::Send(IPC::Message* msg) { if (!channel_.get()) { delete msg; diff --git a/ipc/chromium/src/chrome/common/child_thread.h b/ipc/chromium/src/chrome/common/child_thread.h index 36e5f9b11c39..ac7ed0670130 100644 --- a/ipc/chromium/src/chrome/common/child_thread.h +++ b/ipc/chromium/src/chrome/common/child_thread.h @@ -61,6 +61,10 @@ class ChildThread : public IPC::Channel::Listener, virtual void OnMessageReceived(const IPC::Message& msg); virtual void OnChannelError(); +#ifdef MOZ_NUWA_PROCESS + static void MarkThread(); +#endif + // The message loop used to run tasks on the thread that started this thread. MessageLoop* owner_loop_; diff --git a/js/src/configure.in b/js/src/configure.in index 0ba866267bfb..b3d4c1660f70 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -4049,6 +4049,10 @@ AC_SUBST(NSPR_CFLAGS) AC_SUBST(NSPR_LIBS) AC_SUBST(MOZ_NATIVE_NSPR) +if test -n "$MOZ_NUWA_PROCESS"; then + AC_DEFINE(MOZ_NUWA_PROCESS) +fi + OS_CFLAGS="$CFLAGS" OS_CXXFLAGS="$CXXFLAGS" OS_CPPFLAGS="$CPPFLAGS" diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 9a17cf3d2f98..19fa8c471216 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2279,11 +2279,26 @@ GCHelperThread::finish() } #ifdef JS_THREADSAFE +#ifdef MOZ_NUWA_PROCESS +extern "C" { +MFBT_API bool IsNuwaProcess(); +MFBT_API void NuwaMarkCurrentThread(void (*recreate)(void *), void *arg); +} +#endif + /* static */ void GCHelperThread::threadMain(void *arg) { PR_SetCurrentThreadName("JS GC Helper"); + +#ifdef MOZ_NUWA_PROCESS + if (IsNuwaProcess && IsNuwaProcess()) { + JS_ASSERT(NuwaMarkCurrentThread != nullptr); + NuwaMarkCurrentThread(nullptr, nullptr); + } +#endif + static_cast(arg)->threadLoop(); } diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 292e5aa78526..11a535af4722 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1022,6 +1022,10 @@ class Watchdog bool mShuttingDown; }; +#ifdef MOZ_NUWA_PROCESS +#include "ipc/Nuwa.h" +#endif + class WatchdogManager : public nsIObserver { public: @@ -1155,6 +1159,15 @@ WatchdogMain(void *arg) { PR_SetCurrentThreadName("JS Watchdog"); +#ifdef MOZ_NUWA_PROCESS + if (IsNuwaProcess()) { + NS_ASSERTION(NuwaMarkCurrentThread != nullptr, + "NuwaMarkCurrentThread is undefined!"); + NuwaMarkCurrentThread(nullptr, nullptr); + NuwaFreezeCurrentThread(); + } +#endif + Watchdog* self = static_cast(arg); WatchdogManager* manager = self->Manager(); diff --git a/netwerk/base/src/nsSocketTransportService2.cpp b/netwerk/base/src/nsSocketTransportService2.cpp index afff83ff1f26..d4ba3b2f6be2 100644 --- a/netwerk/base/src/nsSocketTransportService2.cpp +++ b/netwerk/base/src/nsSocketTransportService2.cpp @@ -634,11 +634,23 @@ nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread, return NS_OK; } +#ifdef MOZ_NUWA_PROCESS +#include "ipc/Nuwa.h" +#endif + NS_IMETHODIMP nsSocketTransportService::Run() { PR_SetCurrentThreadName("Socket Thread"); +#ifdef MOZ_NUWA_PROCESS + if (IsNuwaProcess()) { + NS_ASSERTION(NuwaMarkCurrentThread != nullptr, + "NuwaMarkCurrentThread is undefined!"); + NuwaMarkCurrentThread(nullptr, nullptr); + } +#endif + SOCKET_LOG(("STS thread init\n")); psm::InitializeSSLServerCertVerificationThreads(); diff --git a/netwerk/base/src/nsStreamTransportService.cpp b/netwerk/base/src/nsStreamTransportService.cpp index 361cb4f9c658..9d9df2722f50 100644 --- a/netwerk/base/src/nsStreamTransportService.cpp +++ b/netwerk/base/src/nsStreamTransportService.cpp @@ -425,6 +425,38 @@ nsOutputStreamTransport::IsNonBlocking(bool *result) return NS_OK; } +#ifdef MOZ_NUWA_PROCESS +#include "ipc/Nuwa.h" + +class STSThreadPoolListener : public nsIThreadPoolListener +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSITHREADPOOLLISTENER + + STSThreadPoolListener() {} + ~STSThreadPoolListener() {} +}; + +NS_IMPL_ISUPPORTS1(STSThreadPoolListener, nsIThreadPoolListener) + +NS_IMETHODIMP +STSThreadPoolListener::OnThreadCreated() +{ + if (IsNuwaProcess()) { + NuwaMarkCurrentThread(nullptr, nullptr); + } + return NS_OK; +} + +NS_IMETHODIMP +STSThreadPoolListener::OnThreadShuttingDown() +{ + return NS_OK; +} + +#endif // MOZ_NUWA_PROCESS + //----------------------------------------------------------------------------- // nsStreamTransportService //----------------------------------------------------------------------------- @@ -445,6 +477,11 @@ nsStreamTransportService::Init() mPool->SetThreadLimit(25); mPool->SetIdleThreadLimit(1); mPool->SetIdleThreadTimeout(PR_SecondsToInterval(30)); +#ifdef MOZ_NUWA_PROCESS + if (IsNuwaProcess()) { + mPool->SetListener(new STSThreadPoolListener()); + } +#endif nsCOMPtr obsSvc = mozilla::services::GetObserverService(); diff --git a/widget/gonk/GonkMemoryPressureMonitoring.cpp b/widget/gonk/GonkMemoryPressureMonitoring.cpp index af066802d8f4..374a09fbf8e0 100644 --- a/widget/gonk/GonkMemoryPressureMonitoring.cpp +++ b/widget/gonk/GonkMemoryPressureMonitoring.cpp @@ -21,6 +21,10 @@ #define LOG(args...) \ __android_log_print(ANDROID_LOG_INFO, "GonkMemoryPressure" , ## args) +#ifdef MOZ_NUWA_PROCESS +#include "ipc/Nuwa.h" +#endif + using namespace mozilla; namespace { @@ -113,6 +117,14 @@ public: { MOZ_ASSERT(!NS_IsMainThread()); +#ifdef MOZ_NUWA_PROCESS + if (IsNuwaProcess()) { + NS_ASSERTION(NuwaMarkCurrentThread != nullptr, + "NuwaMarkCurrentThread is undefined!"); + NuwaMarkCurrentThread(nullptr, nullptr); + } +#endif + int lowMemFd = open("/sys/kernel/mm/lowmemkiller/notify_trigger_active", O_RDONLY | O_CLOEXEC); NS_ENSURE_STATE(lowMemFd != -1); diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index 3f2092fa9f77..c8078412ad61 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -893,6 +893,10 @@ enum ccType { ShutdownCC /* Shutdown CC, used for finding leaks. */ }; +#ifdef MOZ_NUWA_PROCESS +#include "ipc/Nuwa.h" +#endif + //////////////////////////////////////////////////////////////////////// // Top level structure for the cycle collector. //////////////////////////////////////////////////////////////////////// diff --git a/xpcom/threads/TimerThread.cpp b/xpcom/threads/TimerThread.cpp index 68803aeeb948..69ed7c63098b 100644 --- a/xpcom/threads/TimerThread.cpp +++ b/xpcom/threads/TimerThread.cpp @@ -159,11 +159,23 @@ nsresult TimerThread::Shutdown() return NS_OK; } +#ifdef MOZ_NUWA_PROCESS +#include "ipc/Nuwa.h" +#endif + /* void Run(); */ NS_IMETHODIMP TimerThread::Run() { PR_SetCurrentThreadName("Timer"); +#ifdef MOZ_NUWA_PROCESS + if (IsNuwaProcess()) { + NS_ASSERTION(NuwaMarkCurrentThread != nullptr, + "NuwaMarkCurrentThread is undefined!"); + NuwaMarkCurrentThread(nullptr, nullptr); + } +#endif + MonitorAutoLock lock(mMonitor); // We need to know how many microseconds give a positive PRIntervalTime. This From ba5a1bd24c86e905c0393ef5b0eee3f268daede9 Mon Sep 17 00:00:00 2001 From: "Thinker Lee ext:(%2C%20Cervantes%20Yu%20%3Ccyu%40mozilla.com%3E)" Date: Mon, 3 Jun 2013 18:14:44 +0800 Subject: [PATCH 19/23] Bug 771765 - Support template content process, part 7: Don't schedule timer in the message pump if template process is ready. r=khuey --- ipc/glue/MessagePump.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/ipc/glue/MessagePump.cpp b/ipc/glue/MessagePump.cpp index 6b4af0c36799..2d4d988516e2 100644 --- a/ipc/glue/MessagePump.cpp +++ b/ipc/glue/MessagePump.cpp @@ -18,6 +18,10 @@ #include "AndroidBridge.h" #endif +#ifdef MOZ_NUWA_PROCESS +#include "ipc/Nuwa.h" +#endif + using mozilla::ipc::DoWorkRunnable; using mozilla::ipc::MessagePump; using mozilla::ipc::MessagePumpForChildProcess; @@ -96,7 +100,11 @@ MessagePump::Run(MessagePump::Delegate* aDelegate) did_work |= aDelegate->DoDelayedWork(&delayed_work_time_); - if (did_work && delayed_work_time_.is_null()) +if (did_work && delayed_work_time_.is_null() +#ifdef MOZ_NUWA_PROCESS + && (!IsNuwaReady() || !IsNuwaProcess()) +#endif + ) mDelayedWorkTimer->Cancel(); if (!keep_running_) @@ -116,7 +124,10 @@ MessagePump::Run(MessagePump::Delegate* aDelegate) NS_ProcessNextEvent(mThread, true); } - mDelayedWorkTimer->Cancel(); +#ifdef MOZ_NUWA_PROCESS + if (!IsNuwaReady() || !IsNuwaProcess()) +#endif + mDelayedWorkTimer->Cancel(); keep_running_ = true; } @@ -148,6 +159,11 @@ MessagePump::ScheduleWorkForNestedLoop() void MessagePump::ScheduleDelayedWork(const base::TimeTicks& aDelayedTime) { +#ifdef MOZ_NUWA_PROCESS + if (IsNuwaReady() && IsNuwaProcess()) + return; +#endif + if (!mDelayedWorkTimer) { mDelayedWorkTimer = do_CreateInstance(NS_TIMER_CONTRACTID); if (!mDelayedWorkTimer) { From cadac0602aaff675f8156dc7a25dc00acadac865 Mon Sep 17 00:00:00 2001 From: "Thinker Lee ext:(%2C%20Cervantes%20Yu%20%3Ccyu%40mozilla.com%3E)" Date: Mon, 3 Jun 2013 18:14:46 +0800 Subject: [PATCH 20/23] Bug 771765 - Support template content process, part 8: process initialization flow changes. r=khuey Changes initialization code for the template process: * Let the process run for NUWA_PREPARATION_TIME ms and then start freezing the threads. * Delay android binder thread pool creation after the content process is forked from the template and other thread recreation has finished. * Poke the app shell after the content process is forked from the template. --- ipc/app/MozillaRuntimeMain.cpp | 47 ++++++++++++++++++++++++++------ toolkit/xre/nsEmbedFunctions.cpp | 35 ++++++++++++++++++++++++ widget/gonk/nsAppShell.cpp | 9 ++++++ 3 files changed, 83 insertions(+), 8 deletions(-) diff --git a/ipc/app/MozillaRuntimeMain.cpp b/ipc/app/MozillaRuntimeMain.cpp index ef4d6f7f2206..e1f3388bbf02 100644 --- a/ipc/app/MozillaRuntimeMain.cpp +++ b/ipc/app/MozillaRuntimeMain.cpp @@ -41,15 +41,14 @@ #endif -int -main(int argc, char* argv[]) -{ -#ifdef MOZ_WIDGET_GONK - // This creates a ThreadPool for binder ipc. A ThreadPool is necessary to - // receive binder calls, though not necessary to send binder calls. - // ProcessState::Self() also needs to be called once on the main thread to - // register the main thread with the binder driver. +#ifdef MOZ_NUWA_PROCESS +#include +#include "ipc/Nuwa.h" +#endif +#ifdef MOZ_WIDGET_GONK +static void +InitializeBinder(void *aDummy) { // Change thread priority to 0 only during calling ProcessState::self(). // The priority is registered to binder driver and used for default Binder // Thread's priority. @@ -60,6 +59,38 @@ main(int argc, char* argv[]) LOGE_IF(err, "setpriority failed. Current process needs root permission."); android::ProcessState::self()->startThreadPool(); setpriority(PRIO_PROCESS, 0, curPrio); +} +#endif + +int +main(int argc, char* argv[]) +{ +#ifdef MOZ_NUWA_PROCESS + bool isNuwa = false; + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-nuwa") == 0) { + PrepareNuwaProcess(); + isNuwa = true; + break; + } + } +#endif + +#ifdef MOZ_WIDGET_GONK + // This creates a ThreadPool for binder ipc. A ThreadPool is necessary to + // receive binder calls, though not necessary to send binder calls. + // ProcessState::Self() also needs to be called once on the main thread to + // register the main thread with the binder driver. + +#ifdef MOZ_NUWA_PROCESS + if (!isNuwa) { + InitializeBinder(nullptr); + } else { + NuwaAddFinalConstructor(&InitializeBinder, nullptr); + } +#else + InitializeBinder(nullptr); +#endif #endif #if defined(XP_WIN) && defined(DEBUG_bent) diff --git a/toolkit/xre/nsEmbedFunctions.cpp b/toolkit/xre/nsEmbedFunctions.cpp index a0d5e0191280..f0e6c60d6fe0 100644 --- a/toolkit/xre/nsEmbedFunctions.cpp +++ b/toolkit/xre/nsEmbedFunctions.cpp @@ -77,6 +77,11 @@ using mozilla::_ipdltest::IPDLUnitTestProcessChild; #endif // ifdef MOZ_IPDL_TESTS +#ifdef MOZ_NUWA_PROCESS +#include "nsITimer.h" +#define NUWA_PREPARATION_TIME 1000 +#endif + using namespace mozilla; using mozilla::ipc::BrowserProcessSubThread; @@ -102,6 +107,13 @@ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); static const PRUnichar kShellLibraryName[] = L"shell32.dll"; #endif +#ifdef MOZ_NUWA_PROCESS +extern "C" { +void PrepareNuwaProcess() __attribute__((weak)); +void MakeNuwaProcess() __attribute__((weak)); +}; +#endif + nsresult XRE_LockProfileDirectory(nsIFile* aDirectory, nsISupports* *aLockObject) @@ -265,6 +277,16 @@ SetTaskbarGroupId(const nsString& aId) } #endif +#ifdef MOZ_NUWA_PROCESS +void +OnFinishNuwaPreparation(nsITimer *aTimer, void *aClosure) +{ + NS_ASSERTION(MakeNuwaProcess != nullptr, + "MakeNuwaProcess() is not available!"); + MakeNuwaProcess(); +} +#endif + nsresult XRE_InitChildProcess(int aArgc, char* aArgv[], @@ -512,6 +534,19 @@ XRE_InitChildProcess(int aArgc, return NS_ERROR_FAILURE; } +#ifdef MOZ_NUWA_PROCESS + nsCOMPtr timer; + if (aProcess == GeckoProcessType_Content && + CommandLine::ForCurrentProcess()->HasSwitch(L"nuwa")) { + // Wait the Nuwa process for NUWA_PREPARATION_TIME ms. + timer = do_CreateInstance(NS_TIMER_CONTRACTID); + rv = timer->InitWithFuncCallback(OnFinishNuwaPreparation, + nullptr, + NUWA_PREPARATION_TIME, + nsITimer::TYPE_ONE_SHOT); + } +#endif + // Run the UI event loop on the main thread. uiMessageLoop.MessageLoop::Run(); diff --git a/widget/gonk/nsAppShell.cpp b/widget/gonk/nsAppShell.cpp index 1aea67d8c91c..4c63658c8153 100644 --- a/widget/gonk/nsAppShell.cpp +++ b/widget/gonk/nsAppShell.cpp @@ -57,6 +57,10 @@ #include "libui/InputDispatcher.h" #include "cutils/properties.h" +#ifdef MOZ_NUWA_PROCESS +#include "ipc/Nuwa.h" +#endif + #include "GeckoProfiler.h" // Defines kKeyMapping and GetKeyNameIndex() @@ -733,6 +737,11 @@ nsAppShell::Init() obsServ->AddObserver(this, "browser-ui-startup-complete", false); } +#ifdef MOZ_NUWA_PROCESS + // Make sure main thread was woken up after Nuwa fork. + NuwaAddConstructor((void (*)(void *))&NotifyEvent, nullptr); +#endif + // Delay initializing input devices until the screen has been // initialized (and we know the resolution). return rv; From 0a6a25b7c4d404134d31953001c2cde5ae888cf8 Mon Sep 17 00:00:00 2001 From: Cervantes Yu Date: Thu, 26 Sep 2013 12:19:09 +0800 Subject: [PATCH 21/23] Bug 771765 - Support template content process, part 9: allocating a toplevel protocol should return itself. r=dzbarsky Reverse the effect in bug 879475 part 14 since we need to keep track of open protocols in the Nuwa process. --- dom/ipc/ContentChild.cpp | 4 ++-- dom/ipc/ContentChild.h | 4 ++-- dom/ipc/ContentParent.cpp | 4 ++-- dom/ipc/ContentParent.h | 4 ++-- gfx/layers/ipc/CompositorChild.cpp | 9 ++++----- gfx/layers/ipc/CompositorChild.h | 2 +- gfx/layers/ipc/CompositorParent.cpp | 8 +++++--- gfx/layers/ipc/CompositorParent.h | 2 +- gfx/layers/ipc/ImageBridgeChild.cpp | 8 ++++---- gfx/layers/ipc/ImageBridgeChild.h | 2 +- gfx/layers/ipc/ImageBridgeParent.cpp | 6 +++--- gfx/layers/ipc/ImageBridgeParent.h | 2 +- ipc/ipdl/ipdl/lower.py | 2 +- ipc/ipdl/test/cxx/TestBridgeMain.cpp | 14 ++++++-------- ipc/ipdl/test/cxx/TestBridgeMain.h | 4 ++-- ipc/ipdl/test/cxx/TestOpens.cpp | 12 ++++++------ ipc/ipdl/test/cxx/TestOpens.h | 4 ++-- 17 files changed, 45 insertions(+), 46 deletions(-) diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 72a985a8d299..f09cd505f7e5 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -531,14 +531,14 @@ ContentChild::RecvDumpGCAndCCLogsToFile(const nsString& aIdentifier, return true; } -bool +PCompositorChild* ContentChild::AllocPCompositorChild(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) { return CompositorChild::Create(aTransport, aOtherProcess); } -bool +PImageBridgeChild* ContentChild::AllocPImageBridgeChild(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) { diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index fd841e9c9c3f..4f15636a89ce 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -77,10 +77,10 @@ public: void SetProcessName(const nsAString& aName); const void GetProcessName(nsAString& aName); - bool + PCompositorChild* AllocPCompositorChild(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) MOZ_OVERRIDE; - bool + PImageBridgeChild* AllocPImageBridgeChild(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) MOZ_OVERRIDE; diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 55368376406a..5c61f1e2297b 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -2095,14 +2095,14 @@ ContentParent::Observe(nsISupports* aSubject, return NS_OK; } -bool +PCompositorParent* ContentParent::AllocPCompositorParent(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) { return CompositorParent::Create(aTransport, aOtherProcess); } -bool +PImageBridgeParent* ContentParent::AllocPImageBridgeParent(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) { diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index a7c7be02de37..ad87d689d593 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -304,10 +304,10 @@ private: */ void ShutDownProcess(bool aCloseWithError); - bool + PCompositorParent* AllocPCompositorParent(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) MOZ_OVERRIDE; - bool + PImageBridgeParent* AllocPImageBridgeParent(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) MOZ_OVERRIDE; diff --git a/gfx/layers/ipc/CompositorChild.cpp b/gfx/layers/ipc/CompositorChild.cpp index f74b0f30e67e..490f42ae882f 100644 --- a/gfx/layers/ipc/CompositorChild.cpp +++ b/gfx/layers/ipc/CompositorChild.cpp @@ -51,7 +51,7 @@ CompositorChild::Destroy() SendStop(); } -/*static*/ bool +/*static*/ PCompositorChild* CompositorChild::Create(Transport* aTransport, ProcessId aOtherProcess) { // There's only one compositor per child process. @@ -62,15 +62,14 @@ CompositorChild::Create(Transport* aTransport, ProcessId aOtherProcess) if (!base::OpenProcessHandle(aOtherProcess, &handle)) { // We can't go on without a compositor. NS_RUNTIMEABORT("Couldn't OpenProcessHandle() to parent process."); - return false; + return nullptr; } if (!child->Open(aTransport, handle, XRE_GetIOMessageLoop(), ipc::ChildSide)) { NS_RUNTIMEABORT("Couldn't Open() Compositor channel."); - return false; + return nullptr; } // We release this ref in ActorDestroy(). - sCompositor = child.forget().get(); - return true; + return sCompositor = child.forget().get(); } /*static*/ PCompositorChild* diff --git a/gfx/layers/ipc/CompositorChild.h b/gfx/layers/ipc/CompositorChild.h index 238f42ce6939..1eca1b4cb016 100644 --- a/gfx/layers/ipc/CompositorChild.h +++ b/gfx/layers/ipc/CompositorChild.h @@ -38,7 +38,7 @@ public: * or Bridge() request from our parent process. The Transport is to * the compositor's context. */ - static bool + static PCompositorChild* Create(Transport* aTransport, ProcessId aOtherProcess); static PCompositorChild* Get(); diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index aa4ad8aeef1e..cba7dfde097c 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -920,7 +920,7 @@ OpenCompositor(CrossProcessCompositorParent* aCompositor, MOZ_ASSERT(ok); } -/*static*/ bool +/*static*/ PCompositorParent* CompositorParent::Create(Transport* aTransport, ProcessId aOtherProcess) { nsRefPtr cpcp = @@ -928,14 +928,16 @@ CompositorParent::Create(Transport* aTransport, ProcessId aOtherProcess) ProcessHandle handle; if (!base::OpenProcessHandle(aOtherProcess, &handle)) { // XXX need to kill |aOtherProcess|, it's boned - return false; + return nullptr; } cpcp->mSelfRef = cpcp; CompositorLoop()->PostTask( FROM_HERE, NewRunnableFunction(OpenCompositor, cpcp.get(), aTransport, handle, XRE_GetIOMessageLoop())); - return true; + // The return value is just compared to null for success checking, + // we're not sharing a ref. + return cpcp.get(); } IToplevelProtocol* diff --git a/gfx/layers/ipc/CompositorParent.h b/gfx/layers/ipc/CompositorParent.h index 70c605919a60..be1fbfe5d87d 100644 --- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -182,7 +182,7 @@ public: * A new child process has been configured to push transactions * directly to us. Transport is to its thread context. */ - static bool + static PCompositorParent* Create(Transport* aTransport, ProcessId aOtherProcess); /** diff --git a/gfx/layers/ipc/ImageBridgeChild.cpp b/gfx/layers/ipc/ImageBridgeChild.cpp index ad18f5c3685a..ef0e3aac572c 100644 --- a/gfx/layers/ipc/ImageBridgeChild.cpp +++ b/gfx/layers/ipc/ImageBridgeChild.cpp @@ -525,7 +525,7 @@ ImageBridgeChild::EndTransaction() } -bool +PImageBridgeChild* ImageBridgeChild::StartUpInChildProcess(Transport* aTransport, ProcessId aOtherProcess) { @@ -533,12 +533,12 @@ ImageBridgeChild::StartUpInChildProcess(Transport* aTransport, ProcessHandle processHandle; if (!base::OpenProcessHandle(aOtherProcess, &processHandle)) { - return false; + return nullptr; } sImageBridgeChildThread = new Thread("ImageBridgeChild"); if (!sImageBridgeChildThread->Start()) { - return false; + return nullptr; } #ifdef MOZ_NUWA_PROCESS @@ -557,7 +557,7 @@ ImageBridgeChild::StartUpInChildProcess(Transport* aTransport, NewRunnableFunction(ConnectImageBridgeInChildProcess, aTransport, processHandle)); - return true; + return sImageBridgeChildSingleton; } void ImageBridgeChild::ShutDown() diff --git a/gfx/layers/ipc/ImageBridgeChild.h b/gfx/layers/ipc/ImageBridgeChild.h index 1e76f0d8c372..b040fd7950ec 100644 --- a/gfx/layers/ipc/ImageBridgeChild.h +++ b/gfx/layers/ipc/ImageBridgeChild.h @@ -114,7 +114,7 @@ public: */ static void StartUp(); - static bool + static PImageBridgeChild* StartUpInChildProcess(Transport* aTransport, ProcessId aOtherProcess); /** diff --git a/gfx/layers/ipc/ImageBridgeParent.cpp b/gfx/layers/ipc/ImageBridgeParent.cpp index 601476818c83..9582fc558a8d 100644 --- a/gfx/layers/ipc/ImageBridgeParent.cpp +++ b/gfx/layers/ipc/ImageBridgeParent.cpp @@ -102,12 +102,12 @@ ConnectImageBridgeInParentProcess(ImageBridgeParent* aBridge, aBridge->Open(aTransport, aOtherProcess, XRE_GetIOMessageLoop(), ipc::ParentSide); } -/*static*/ bool +/*static*/ PImageBridgeParent* ImageBridgeParent::Create(Transport* aTransport, ProcessId aOtherProcess) { ProcessHandle processHandle; if (!base::OpenProcessHandle(aOtherProcess, &processHandle)) { - return false; + return nullptr; } MessageLoop* loop = CompositorParent::CompositorLoop(); @@ -116,7 +116,7 @@ ImageBridgeParent::Create(Transport* aTransport, ProcessId aOtherProcess) loop->PostTask(FROM_HERE, NewRunnableFunction(ConnectImageBridgeInParentProcess, bridge.get(), aTransport, processHandle)); - return true; + return bridge.get(); } bool ImageBridgeParent::RecvStop() diff --git a/gfx/layers/ipc/ImageBridgeParent.h b/gfx/layers/ipc/ImageBridgeParent.h index dfe499d961a2..30ab44cd20b6 100644 --- a/gfx/layers/ipc/ImageBridgeParent.h +++ b/gfx/layers/ipc/ImageBridgeParent.h @@ -44,7 +44,7 @@ public: virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - static bool + static PImageBridgeParent* Create(Transport* aTransport, ProcessId aOtherProcess); virtual PGrallocBufferParent* diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index 59f758b5b3d5..aef6ac5972d9 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -2738,7 +2738,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): _allocMethod(actor.ptype, actor.side).name, params=[ Decl(Type('Transport', ptr=1), 'transport'), Decl(Type('ProcessId'), 'otherProcess') ], - ret=Type.BOOL, + ret=actortype, virtual=1, pure=1))) # optional ActorDestroy() method; default is no-op diff --git a/ipc/ipdl/test/cxx/TestBridgeMain.cpp b/ipc/ipdl/test/cxx/TestBridgeMain.cpp index a87aa050bf4c..05e72d4d6c0c 100644 --- a/ipc/ipdl/test/cxx/TestBridgeMain.cpp +++ b/ipc/ipdl/test/cxx/TestBridgeMain.cpp @@ -25,7 +25,7 @@ TestBridgeMainParent::Main() fail("sending Start"); } -bool +PTestBridgeMainSubParent* TestBridgeMainParent::AllocPTestBridgeMainSubParent(Transport* transport, ProcessId otherProcess) { @@ -38,8 +38,7 @@ TestBridgeMainParent::AllocPTestBridgeMainSubParent(Transport* transport, if (!a->Open(transport, h, XRE_GetIOMessageLoop(), ipc::ParentSide)) { return nullptr; } - a.forget(); - return true; + return a.forget(); } void @@ -177,25 +176,24 @@ TestBridgeSubChild::RecvPing() return true; } -bool +PTestBridgeMainSubChild* TestBridgeSubChild::AllocPTestBridgeMainSubChild(Transport* transport, ProcessId otherProcess) { ProcessHandle h; if (!base::OpenProcessHandle(otherProcess, &h)) { - return false; + return nullptr; } nsAutoPtr a(new TestBridgeMainSubChild(transport)); if (!a->Open(transport, h, XRE_GetIOMessageLoop(), ipc::ChildSide)) { - return false; + return nullptr; } if (!a->SendHello()) fail("sending Hello"); - a.forget(); - return true; + return a.forget(); } void diff --git a/ipc/ipdl/test/cxx/TestBridgeMain.h b/ipc/ipdl/test/cxx/TestBridgeMain.h index 16fc0d710ebb..170f2127359f 100644 --- a/ipc/ipdl/test/cxx/TestBridgeMain.h +++ b/ipc/ipdl/test/cxx/TestBridgeMain.h @@ -31,7 +31,7 @@ public: void Main(); protected: - virtual bool + virtual PTestBridgeMainSubParent* AllocPTestBridgeMainSubParent(Transport* transport, ProcessId otherProcess) MOZ_OVERRIDE; @@ -105,7 +105,7 @@ public: protected: virtual bool RecvPing() MOZ_OVERRIDE; - virtual bool + virtual PTestBridgeMainSubChild* AllocPTestBridgeMainSubChild(Transport* transport, ProcessId otherProcess) MOZ_OVERRIDE; diff --git a/ipc/ipdl/test/cxx/TestOpens.cpp b/ipc/ipdl/test/cxx/TestOpens.cpp index 4cffaf9d9ecb..10c8332ebac3 100644 --- a/ipc/ipdl/test/cxx/TestOpens.cpp +++ b/ipc/ipdl/test/cxx/TestOpens.cpp @@ -64,7 +64,7 @@ OpenParent(TestOpensOpenedParent* aParent, fail("opening Parent"); } -bool +PTestOpensOpenedParent* TestOpensParent::AllocPTestOpensOpenedParent(Transport* transport, ProcessId otherProcess) { @@ -72,7 +72,7 @@ TestOpensParent::AllocPTestOpensOpenedParent(Transport* transport, ProcessHandle h; if (!base::OpenProcessHandle(otherProcess, &h)) { - return false; + return nullptr; } gParentThread = new Thread("ParentThread"); @@ -84,7 +84,7 @@ TestOpensParent::AllocPTestOpensOpenedParent(Transport* transport, FROM_HERE, NewRunnableFunction(OpenParent, a, transport, h)); - return true; + return a; } void @@ -177,7 +177,7 @@ OpenChild(TestOpensOpenedChild* aChild, fail("sending Hello"); } -bool +PTestOpensOpenedChild* TestOpensChild::AllocPTestOpensOpenedChild(Transport* transport, ProcessId otherProcess) { @@ -185,7 +185,7 @@ TestOpensChild::AllocPTestOpensOpenedChild(Transport* transport, ProcessHandle h; if (!base::OpenProcessHandle(otherProcess, &h)) { - return false; + return nullptr; } gChildThread = new Thread("ChildThread"); @@ -197,7 +197,7 @@ TestOpensChild::AllocPTestOpensOpenedChild(Transport* transport, FROM_HERE, NewRunnableFunction(OpenChild, a, transport, h)); - return true; + return a; } void diff --git a/ipc/ipdl/test/cxx/TestOpens.h b/ipc/ipdl/test/cxx/TestOpens.h index 3f92d5f2d6bb..f3a1f7d4ba89 100644 --- a/ipc/ipdl/test/cxx/TestOpens.h +++ b/ipc/ipdl/test/cxx/TestOpens.h @@ -27,7 +27,7 @@ public: void Main(); protected: - virtual bool + virtual PTestOpensOpenedParent* AllocPTestOpensOpenedParent(Transport* transport, ProcessId otherProcess) MOZ_OVERRIDE; virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE; @@ -70,7 +70,7 @@ public: protected: virtual bool RecvStart() MOZ_OVERRIDE; - virtual bool + virtual PTestOpensOpenedChild* AllocPTestOpensOpenedChild(Transport* transport, ProcessId otherProcess) MOZ_OVERRIDE; virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE; From 8157ab6f0cc6eca71509b9656c2c4ea3119b13aa Mon Sep 17 00:00:00 2001 From: Gaia Pushbot Date: Mon, 30 Sep 2013 08:45:40 -0700 Subject: [PATCH 22/23] Bumping gaia.json for 1 gaia-central revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/913fdd90b6ec Author: Dave Hunt Desc: Bug 922077 - Bump gaiatest version to 0.16. r=bebe --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 8f24d8c30d91..2b9e31dc1cb7 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "de7396523254bf76274ab7f7f5970100b39ee4a5", + "revision": "913fdd90b6eca6f08b1953a3f548b888918a777b", "repo_path": "/integration/gaia-central" } From 47908bd980e5f4f7e03e6875b0cb3d8ca47e803d Mon Sep 17 00:00:00 2001 From: Gaia Pushbot Date: Mon, 30 Sep 2013 09:00:25 -0700 Subject: [PATCH 23/23] Bumping gaia.json for 2 gaia-central revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/2d443e3eebba Author: Ran Ben Aharon Desc: Merge pull request #12423 from EverythingMe/bugfix-915467 Bug 915467 - [e.me][bug] Provider init may occur before Homescreen init [r=crdlc] ======== https://hg.mozilla.org/integration/gaia-central/rev/39ccd7794b31 Author: Ran Ben Aharon Desc: Bug 915467 - [e.me][bug] Provider init may occur before Homescreen init [r=crdlc] --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 2b9e31dc1cb7..dce87aaf2052 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "913fdd90b6eca6f08b1953a3f548b888918a777b", + "revision": "2d443e3eebba5eba4a0c164c9692e6d20a859954", "repo_path": "/integration/gaia-central" }