From 023203e555da7e1746e7fb884cde62b2be1f963f Mon Sep 17 00:00:00 2001 From: roytam1 Date: Mon, 18 Apr 2022 11:30:49 +0800 Subject: [PATCH] addendum of rev c554d461ab --- dom/events/EventNameList.h | 6 +- dom/inputmethod/MozKeyboard.js | 110 +- dom/inputmethod/forms.js | 156 +- dom/inputmethod/mochitest/mochitest.ini | 2 + .../mochitest/test_bug1137557.html | 1674 +++++++++++++++++ dom/inputmethod/mochitest/test_unload.html | 167 ++ dom/plugins/base/nsPluginNativeWindowWin.cpp | 4 +- dom/webidl/InputMethod.webidl | 143 +- layout/forms/nsListControlFrame.cpp | 4 +- layout/generic/nsPluginFrame.cpp | 18 +- netwerk/base/nsSocketTransportService2.cpp | 13 +- netwerk/base/nsSocketTransportService2.h | 1 + widget/EventMessageList.h | 48 +- widget/WidgetEventImpl.cpp | 4 +- xpcom/tests/gtest/TestThreads.cpp | 115 +- xpcom/threads/LazyIdleThread.cpp | 7 + xpcom/threads/nsEventQueue.cpp | 89 +- xpcom/threads/nsEventQueue.h | 53 +- xpcom/threads/nsIThread.idl | 24 +- xpcom/threads/nsThread.cpp | 146 +- xpcom/threads/nsThread.h | 32 +- xpcom/threads/nsThreadPool.cpp | 73 +- xpcom/threads/nsThreadPool.h | 3 + 23 files changed, 2614 insertions(+), 278 deletions(-) create mode 100644 dom/inputmethod/mochitest/test_bug1137557.html create mode 100644 dom/inputmethod/mochitest/test_unload.html diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index d810489814..f843d36d4a 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -493,7 +493,7 @@ WINDOW_EVENT(hashchange, EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly, eBasicEventClass) WINDOW_EVENT(languagechange, - NS_LANGUAGECHANGE, + eLanguageChange, EventNameType_HTMLBodyOrFramesetOnly, eBasicEventClass) // XXXbz Should the onmessage attribute on really not work? If so, do we @@ -504,11 +504,11 @@ WINDOW_EVENT(message, EventNameType_None, eBasicEventClass) WINDOW_EVENT(offline, - NS_OFFLINE, + eOffline, EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly, eBasicEventClass) WINDOW_EVENT(online, - NS_ONLINE, + eOnline, EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly, eBasicEventClass) WINDOW_EVENT(pagehide, diff --git a/dom/inputmethod/MozKeyboard.js b/dom/inputmethod/MozKeyboard.js index 34283105f2..be0f83f184 100644 --- a/dom/inputmethod/MozKeyboard.js +++ b/dom/inputmethod/MozKeyboard.js @@ -375,7 +375,7 @@ MozInputMethod.prototype = { }, addInput: function(inputId, inputManifest) { - return this._sendPromise(function(resolverId) { + return this.createPromiseWithId(function(resolverId) { let appId = this._window.document.nodePrincipal.appId; cpmm.sendAsyncMessage('InputRegistry:Add', { @@ -567,7 +567,7 @@ MozInputContext.prototype = { switch (msg.name) { case "Keyboard:SendKey:Result:OK": - resolver.resolve(); + resolver.resolve(true); break; case "Keyboard:SendKey:Result:Error": resolver.reject(json.error); @@ -590,7 +590,7 @@ MozInputContext.prototype = { break; case "Keyboard:SetComposition:Result:OK": // Fall through. case "Keyboard:EndComposition:Result:OK": - resolver.resolve(); + resolver.resolve(true); break; default: dump("Could not find a handler for " + msg.name); @@ -732,42 +732,79 @@ MozInputContext.prototype = { return this.replaceSurroundingText(null, offset, length); }, - sendKey: function ic_sendKey(keyCode, charCode, modifiers, repeat) { - let self = this; + sendKey: function ic_sendKey(dictOrKeyCode, charCode, modifiers, repeat) { + if (typeof dictOrKeyCode === 'number') { + // XXX: modifiers are ignored in this API method. - // XXX: modifiers are ignored in this API method. + return this._sendPromise((resolverId) => { + cpmmSendAsyncMessageWithKbID(this, 'Keyboard:SendKey', { + contextId: this._contextId, + requestId: resolverId, + method: 'sendKey', + keyCode: dictOrKeyCode, + charCode: charCode, + repeat: repeat + }); + }); + } else if (typeof dictOrKeyCode === 'object') { + return this._sendPromise((resolverId) => { + cpmmSendAsyncMessageWithKbID(this, 'Keyboard:SendKey', { + contextId: this._contextId, + requestId: resolverId, + method: 'sendKey', + keyboardEventDict: this._getkeyboardEventDict(dictOrKeyCode) + }); + }); + } else { + // XXX: Should not reach here; implies WebIDL binding error. + throw new TypeError('Unknown argument passed.'); + } + }, - return this._sendPromise(function(resolverId) { - cpmmSendAsyncMessageWithKbID(self, 'Keyboard:SendKey', { - contextId: self._contextId, + keydown: function ic_keydown(dict) { + return this._sendPromise((resolverId) => { + cpmmSendAsyncMessageWithKbID(this, 'Keyboard:SendKey', { + contextId: this._contextId, + requestId: resolverId, + method: 'keydown', + keyboardEventDict: this._getkeyboardEventDict(dict) + }); + }); + }, + + keyup: function ic_keyup(dict) { + return this._sendPromise((resolverId) => { + cpmmSendAsyncMessageWithKbID(this, 'Keyboard:SendKey', { + contextId: this._contextId, requestId: resolverId, - keyCode: keyCode, - charCode: charCode, - repeat: repeat + method: 'keyup', + keyboardEventDict: this._getkeyboardEventDict(dict) }); }); }, - setComposition: function ic_setComposition(text, cursor, clauses) { + setComposition: function ic_setComposition(text, cursor, clauses, dict) { let self = this; - return this._sendPromise(function(resolverId) { + return this._sendPromise((resolverId) => { cpmmSendAsyncMessageWithKbID(self, 'Keyboard:SetComposition', { contextId: self._contextId, requestId: resolverId, text: text, cursor: (typeof cursor !== 'undefined') ? cursor : text.length, - clauses: clauses || null + clauses: clauses || null, + keyboardEventDict: this._getkeyboardEventDict(dict) }); }); }, - endComposition: function ic_endComposition(text) { + endComposition: function ic_endComposition(text, dict) { let self = this; - return this._sendPromise(function(resolverId) { + return this._sendPromise((resolverId) => { cpmmSendAsyncMessageWithKbID(self, 'Keyboard:EndComposition', { contextId: self._contextId, requestId: resolverId, - text: text || '' + text: text || '', + keyboardEventDict: this._getkeyboardEventDict(dict) }); }); }, @@ -782,6 +819,43 @@ MozInputContext.prototype = { } callback(aResolverId); }); + }, + + // Take a MozInputMethodKeyboardEventDict dict, creates a keyboardEventDict + // object that can be sent to forms.js + _getkeyboardEventDict: function(dict) { + if (typeof dict !== 'object' || !dict.key) { + return; + } + + var keyboardEventDict = { + key: dict.key, + code: dict.code, + repeat: dict.repeat, + flags: 0 + }; + + if (dict.printable) { + keyboardEventDict.flags |= + Ci.nsITextInputProcessor.KEY_FORCE_PRINTABLE_KEY; + } + + if (/^[a-zA-Z0-9]$/.test(dict.key)) { + // keyCode must follow the key value in this range; + // disregard the keyCode from content. + keyboardEventDict.keyCode = dict.key.toUpperCase().charCodeAt(0); + } else if (typeof dict.keyCode === 'number') { + // Allow keyCode to be specified for other key values. + keyboardEventDict.keyCode = dict.keyCode; + + // Allow keyCode to be explicitly set to zero. + if (dict.keyCode === 0) { + keyboardEventDict.flags |= + Ci.nsITextInputProcessor.KEY_KEEP_KEYCODE_ZERO; + } + } + + return keyboardEventDict; } }; diff --git a/dom/inputmethod/forms.js b/dom/inputmethod/forms.js index bd6c1c39d6..c7155a6ff4 100644 --- a/dom/inputmethod/forms.js +++ b/dom/inputmethod/forms.js @@ -11,6 +11,7 @@ dump("###################################### forms.js loaded\n"); let Ci = Components.interfaces; let Cc = Components.classes; let Cu = Components.utils; +let Cr = Components.results; Cu.import("resource://gre/modules/Services.jsm"); Cu.import('resource://gre/modules/XPCOMUtils.jsm'); @@ -650,7 +651,7 @@ let FormAssistant = { break; case "keydown": - if (!this.focusedElement) { + if (!this.focusedElement || this._editing) { break; } @@ -658,7 +659,7 @@ let FormAssistant = { break; case "keyup": - if (!this.focusedElement) { + if (!this.focusedElement || this._editing) { break; } @@ -733,55 +734,100 @@ let FormAssistant = { break; } - // The naive way to figure out if the key to dispatch is printable. - let printable = !!json.charCode; + // If we receive a keyboardEventDict from json, that means the user + // is calling the method with the new arguments. + // Otherwise, we would have to construct our own keyboardEventDict + // based on legacy values we have received. + let keyboardEventDict = json.keyboardEventDict; + let flags = 0; + + if (keyboardEventDict) { + if ('flags' in keyboardEventDict) { + flags = keyboardEventDict.flags; + } + } else { + // The naive way to figure out if the key to dispatch is printable. + let printable = !!json.charCode; - let keyboardEventDict = { // For printable keys, the value should be the actual character. // For non-printable keys, it should be a value in the D3E spec. // Here we make some educated guess for it. - key: printable ? - String.fromCharCode(json.charCode) : - guessKeyNameFromKeyCode(win.KeyboardEvent, json.keyCode), - // We don't have any information to tell the virtual key the - // user have interacted with. - code: "", - // We violate the spec here and ask TextInputProcessor not to infer - // this value from value of key nor code so we could keep the original + let key = printable ? + String.fromCharCode(json.charCode) : + guessKeyNameFromKeyCode(win.KeyboardEvent, json.keyCode); + + // keyCode from content is only respected when the key is not an + // an alphanumeric character. We also ask TextInputProcessor not to + // infer this value for non-printable keys to keep the original // behavior. - keyCode: json.keyCode, - // We do not have the information to infer location of the virtual key - // either (and we would need TextInputProcessor not to compute it). - location: 0, - // This indicates the key is triggered for repeats. - repeat: json.repeat - }; + let keyCode = (printable && /^[a-zA-Z0-9]$/.test(key)) ? + key.toUpperCase().charCodeAt(0) : + json.keyCode; + + keyboardEventDict = { + key: key, + keyCode: keyCode, + // We don't have any information to tell the virtual key the + // user have interacted with. + code: "", + // We do not have the information to infer location of the virtual key + // either (and we would need TextInputProcessor not to compute it). + location: 0, + // This indicates the key is triggered for repeats. + repeat: json.repeat + }; + + flags = tip.KEY_KEEP_KEY_LOCATION_STANDARD; + if (!printable) { + flags |= tip.KEY_NON_PRINTABLE_KEY; + } + if (!keyboardEventDict.keyCode) { + flags |= tip.KEY_KEEP_KEYCODE_ZERO; + } + } let keyboardEvent = new win.KeyboardEvent("", keyboardEventDict); - let flags = tip.KEY_KEEP_KEY_LOCATION_STANDARD; - if (!printable) { - flags |= tip.KEY_NON_PRINTABLE_KEY; - } - if (!json.keyCode) { - flags |= tip.KEY_KEEP_KEYCODE_ZERO; - } - let keydownDefaultPrevented; + let keydownDefaultPrevented = false; try { - let consumedFlags = tip.keydown(keyboardEvent, flags); - keydownDefaultPrevented = - !!(tip.KEYDOWN_IS_CONSUMED & consumedFlags); - if (!json.repeat) { - tip.keyup(keyboardEvent, flags); + switch (json.method) { + case 'sendKey': { + let consumedFlags = tip.keydown(keyboardEvent, flags); + keydownDefaultPrevented = + !!(tip.KEYDOWN_IS_CONSUMED & consumedFlags); + if (!keyboardEventDict.repeat) { + tip.keyup(keyboardEvent, flags); + } + break; + } + case 'keydown': { + let consumedFlags = tip.keydown(keyboardEvent, flags); + keydownDefaultPrevented = + !!(tip.KEYDOWN_IS_CONSUMED & consumedFlags); + break; + } + case 'keyup': { + tip.keyup(keyboardEvent, flags); + + break; + } } - } catch (e) { - dump("forms.js:" + e.toString() + "\n"); + } catch (err) { + dump("forms.js:" + err.toString() + "\n"); if (json.requestId) { - sendAsyncMessage("Forms:SendKey:Result:Error", { - requestId: json.requestId, - error: "Unable to type into destoryed input." - }); + if (err instanceof Ci.nsIException && + err.result == Cr.NS_ERROR_ILLEGAL_VALUE) { + sendAsyncMessage("Forms:SendKey:Result:Error", { + requestId: json.requestId, + error: "The values specified are illegal." + }); + } else { + sendAsyncMessage("Forms:SendKey:Result:Error", { + requestId: json.requestId, + error: "Unable to type into destroyed input." + }); + } } break; @@ -913,7 +959,7 @@ let FormAssistant = { case "Forms:SetComposition": { CompositionManager.setComposition(target, json.text, json.cursor, - json.clauses); + json.clauses, json.keyboardEventDict); sendAsyncMessage("Forms:SetComposition:Result:OK", { requestId: json.requestId, selectioninfo: this.getSelectionInfo() @@ -922,7 +968,7 @@ let FormAssistant = { } case "Forms:EndComposition": { - CompositionManager.endComposition(json.text); + CompositionManager.endComposition(json.text, json.keyboardEventDict); sendAsyncMessage("Forms:EndComposition:Result:OK", { requestId: json.requestId, selectioninfo: this.getSelectionInfo() @@ -1442,6 +1488,7 @@ function replaceSurroundingText(element, text, offset, length) { let CompositionManager = { _isStarted: false, _tip: null, + _KeyboardEventForWin: null, _clauseAttrMap: { 'raw-input': Ci.nsITextInputProcessor.ATTR_RAW_CLAUSE, @@ -1453,7 +1500,7 @@ let CompositionManager = { Ci.nsITextInputProcessor.ATTR_SELECTED_CLAUSE }, - setComposition: function cm_setComposition(element, text, cursor, clauses) { + setComposition: function cm_setComposition(element, text, cursor, clauses, dict) { // Check parameters. if (!element) { return; @@ -1506,13 +1553,22 @@ let CompositionManager = { if (cursor >= 0) { tip.setCaretInPendingComposition(cursor); } - this._isStarted = tip.flushPendingComposition(); + + if (!dict) { + this._isStarted = tip.flushPendingComposition(); + } else { + let keyboardEvent = new win.KeyboardEvent("", dict); + let flags = dict.flags; + this._isStarted = tip.flushPendingComposition(keyboardEvent, flags); + } + if (this._isStarted) { this._tip = tip; + this._KeyboardEventForWin = win.KeyboardEvent; } }, - endComposition: function cm_endComposition(text) { + endComposition: function cm_endComposition(text, dict) { if (!this._isStarted) { return; } @@ -1521,9 +1577,18 @@ let CompositionManager = { return; } - tip.commitCompositionWith(text ? text : ""); + text = text || ""; + if (!dict) { + tip.commitCompositionWith(text); + } else { + let keyboardEvent = new this._KeyboardEventForWin("", dict); + let flags = dict.flags; + tip.commitCompositionWith(text, keyboardEvent, flags); + } + this._isStarted = false; this._tip = null; + this._KeyboardEventForWin = null; }, // Composition ends due to external actions. @@ -1534,5 +1599,6 @@ let CompositionManager = { this._isStarted = false; this._tip = null; + this._KeyboardEventForWin = null; } }; diff --git a/dom/inputmethod/mochitest/mochitest.ini b/dom/inputmethod/mochitest/mochitest.ini index 761f1f2610..ab39bfdeb7 100644 --- a/dom/inputmethod/mochitest/mochitest.ini +++ b/dom/inputmethod/mochitest/mochitest.ini @@ -25,3 +25,5 @@ support-files = [test_sync_edit.html] [test_two_inputs.html] [test_two_selects.html] +[test_unload.html] +[test_bug1137557.html] diff --git a/dom/inputmethod/mochitest/test_bug1137557.html b/dom/inputmethod/mochitest/test_bug1137557.html new file mode 100644 index 0000000000..518e0f7204 --- /dev/null +++ b/dom/inputmethod/mochitest/test_bug1137557.html @@ -0,0 +1,1674 @@ + + + + + Test for new API arguments accepting D3E properties + + + + + +Mozilla Bug 1137557 +

+
+
+
+ + + diff --git a/dom/inputmethod/mochitest/test_unload.html b/dom/inputmethod/mochitest/test_unload.html new file mode 100644 index 0000000000..1963a9992a --- /dev/null +++ b/dom/inputmethod/mochitest/test_unload.html @@ -0,0 +1,167 @@ + + + + + Test focus when page unloads + + + + + +Mozilla Bug 1122463 +Mozilla Bug 820057 +

+
+
+
+ + + diff --git a/dom/plugins/base/nsPluginNativeWindowWin.cpp b/dom/plugins/base/nsPluginNativeWindowWin.cpp index 95c439a679..bd7970da95 100644 --- a/dom/plugins/base/nsPluginNativeWindowWin.cpp +++ b/dom/plugins/base/nsPluginNativeWindowWin.cpp @@ -196,7 +196,7 @@ static LRESULT CALLBACK PluginWndProcInternal(HWND hWnd, UINT msg, WPARAM wParam if (!win) return TRUE; - // The DispatchEvent(NS_PLUGIN_ACTIVATE) below can trigger a reentrant focus + // The DispatchEvent(ePluginActivate) below can trigger a reentrant focus // event which might destroy us. Hold a strong ref on the plugin instance // to prevent that, bug 374229. nsRefPtr inst; @@ -271,7 +271,7 @@ static LRESULT CALLBACK PluginWndProcInternal(HWND hWnd, UINT msg, WPARAM wParam nsCOMPtr widget; win->GetPluginWidget(getter_AddRefs(widget)); if (widget) { - WidgetGUIEvent event(true, NS_PLUGIN_ACTIVATE, widget); + WidgetGUIEvent event(true, ePluginActivate, widget); nsEventStatus status; widget->DispatchEvent(&event, status); } diff --git a/dom/webidl/InputMethod.webidl b/dom/webidl/InputMethod.webidl index eceebf37e9..cb356bda9f 100644 --- a/dom/webidl/InputMethod.webidl +++ b/dom/webidl/InputMethod.webidl @@ -193,17 +193,54 @@ interface MozInputContext: EventTarget { attribute EventHandler onsurroundingtextchange; /* - * send a character with its key events. - * @param modifiers this paramater is no longer honored. - * @param repeat indicates whether a key would be sent repeatedly. - * @return true if succeeds. Otherwise false if the input context becomes void. - * Alternative: sendKey(KeyboardEvent event), but we will likely - * waste memory for creating the KeyboardEvent object. - * Note that, if you want to send a key n times repeatedly, make sure set - * parameter repeat to true and invoke sendKey n-1 times, and then set - * repeat to false in the last invoke. - */ - Promise sendKey(long keyCode, long charCode, long modifiers, optional boolean repeat); + * Send a string/character with its key events. There are two ways of invocating + * the method for backward compability purpose. + * + * (1) The recommended way, allow specifying DOM level 3 properties like |code|. + * @param dictOrKeyCode See MozInputMethodKeyboardEventDict. + * @param charCode disregarded + * @param modifiers disregarded + * @param repeat disregarded + * + * (2) Deprecated, reserved for backward compability. + * @param dictOrKeyCode keyCode of the key to send, should be one of the DOM_VK_ value in KeyboardEvent. + * @param charCode charCode of the character, should be 0 for non-printable keys. + * @param modifiers this paramater is no longer honored. + * @param repeat indicates whether a key would be sent repeatedly. + * + * @return A promise. Resolve to true if succeeds. + * Rejects to a string indicating the error. + * + * Note that, if you want to send a key n times repeatedly, make sure set + * parameter repeat to true and invoke sendKey n times, and invoke keyup + * after the end of the input. + */ + Promise sendKey((MozInputMethodRequiredKeyboardEventDict or long) dictOrKeyCode, + optional long charCode, + optional long modifiers, + optional boolean repeat); + + /* + * Send a string/character with keydown, and keypress events. + * keyup should be called afterwards to ensure properly sequence. + * + * @param dict See MozInputMethodKeyboardEventDict. + * + * @return A promise. Resolve to true if succeeds. + * Rejects to a string indicating the error. + */ + Promise keydown(MozInputMethodRequiredKeyboardEventDict dict); + + /* + * Send a keyup event. keydown should be called first to ensure properly sequence. + * + * @param dict See MozInputMethodKeyboardEventDict. + * + * @return A promise. Resolve to true if succeeds. + * Rejects to a string indicating the error. + * + */ + Promise keyup(MozInputMethodRequiredKeyboardEventDict dict); /* * Set current composing text. This method will start composition or update @@ -219,7 +256,11 @@ interface MozInputContext: EventTarget { * cursor will be positioned after the composition text. * @param clauses The array of composition clause information. If not set, * only one clause is supported. - * + * @param dict The properties of the keyboard event that cause the composition + * to set. keydown or keyup event will be fired if it's necessary. + * For compatibility, we recommend that you should always set this argument + * if it's caused by a key operation. + * * The composing text, which is shown with underlined style to distinguish * from the existing text, is used to compose non-ASCII characters from * keystrokes, e.g. Pinyin or Hiragana. The composing text is the @@ -232,9 +273,10 @@ interface MozInputContext: EventTarget { * To finish composition and commit text to current input field, an IME * should call |endComposition|. */ - // XXXbz what is this promise resolved with? - Promise setComposition(DOMString text, optional long cursor, - optional sequence clauses); + Promise setComposition(DOMString text, + optional long cursor, + optional sequence clauses, + optional MozInputMethodKeyboardEventDict dict); /* * End composition, clear the composing text and commit given text to @@ -242,6 +284,10 @@ interface MozInputContext: EventTarget { * position. * @param text The text to commited before cursor position. If empty string * is given, no text will be committed. + * @param dict The properties of the keyboard event that cause the composition + * to end. keydown or keyup event will be fired if it's necessary. + * For compatibility, we recommend that you should always set this argument + * if it's caused by a key operation. * * Note that composition always ends automatically with nothing to commit if * the composition does not explicitly end by calling |endComposition|, but @@ -249,8 +295,8 @@ interface MozInputContext: EventTarget { * |replaceSurroundingText|, |deleteSurroundingText|, user moving the * cursor, changing the focus, etc. */ - // XXXbz what is this promise resolved with? - Promise endComposition(optional DOMString text); + Promise endComposition(optional DOMString text, + optional MozInputMethodKeyboardEventDict dict); }; enum CompositionClauseSelectionType { @@ -264,3 +310,66 @@ dictionary CompositionClauseParameters { DOMString selectionType = "raw-input"; long length; }; + +/* + * A MozInputMethodKeyboardEventDictBase contains the following properties, + * indicating the properties of the keyboard event caused. + * + * This is the base dictionary type for us to create two child types that could + * be used as argument type in two types of methods, as WebIDL parser required. + * + */ +dictionary MozInputMethodKeyboardEventDictBase { + /* + * String/character to output, or a registered name of non-printable key. + * (To be defined in the inheriting dictionary types.) + */ + // DOMString key; + /* + * String/char indicating the virtual hardware key pressed. Optional. + * Must be a value defined in + * http://www.w3.org/TR/DOM-Level-3-Events-code/#keyboard-chording-virtual + * If your keyboard app emulates physical keyboard layout, this value should + * not be empty string. Otherwise, it should be empty string. + */ + DOMString code = ""; + /* + * keyCode of the keyboard event. Optional. + * To be disregarded if |key| is an alphanumeric character. + * If the key causes inputting a character and if your keyboard app emulates + * a physical keyboard layout, this value should be same as the value used + * by Firefox for desktop. If the key causes inputting an ASCII character + * but if your keyboard app doesn't emulate any physical keyboard layouts, + * the value should be proper value for the key value. + */ + long? keyCode; + /* + * Indicates whether a key would be sent repeatedly. Optional. + */ + boolean repeat = false; + /* + * Optional. True if |key| property is explicitly referring to a printable key. + * When this is set, key will be printable even if the |key| value matches any + * of the registered name of non-printable keys. + */ + boolean printable = false; +}; + +/* + * For methods like setComposition() and endComposition(), the optional + * dictionary type argument is really optional when all of it's property + * are optional. + * This dictionary type is used to denote that argument. + */ +dictionary MozInputMethodKeyboardEventDict : MozInputMethodKeyboardEventDictBase { + DOMString? key; +}; + +/* + * For methods like keydown() and keyup(), the dictionary type argument is + * considered required only if at least one of it's property is required. + * This dictionary type is used to denote that argument. + */ +dictionary MozInputMethodRequiredKeyboardEventDict : MozInputMethodKeyboardEventDictBase { + required DOMString key; +}; diff --git a/layout/forms/nsListControlFrame.cpp b/layout/forms/nsListControlFrame.cpp index aaff2ab53b..1eb2170064 100644 --- a/layout/forms/nsListControlFrame.cpp +++ b/layout/forms/nsListControlFrame.cpp @@ -898,8 +898,8 @@ nsListControlFrame::HandleEvent(nsPresContext* aPresContext, "NS_MOUSE_LEFT_CLICK", "NS_MOUSE_MIDDLE_CLICK", "NS_MOUSE_RIGHT_CLICK"}; - int inx = aEvent->mMessage-NS_MOUSE_MESSAGE_START; - if (inx >= 0 && inx <= (NS_MOUSE_RIGHT_CLICK-NS_MOUSE_MESSAGE_START)) { + int inx = aEvent->mMessage - eMouseEventFirst; + if (inx >= 0 && inx <= (NS_MOUSE_RIGHT_CLICK - eMouseEventFirst)) { printf("Mouse in ListFrame %s [%d]\n", desc[inx], aEvent->mMessage); } else { printf("Mouse in ListFrame [%d]\n", aEvent->mMessage); diff --git a/layout/generic/nsPluginFrame.cpp b/layout/generic/nsPluginFrame.cpp index 7e41583526..fea902112b 100644 --- a/layout/generic/nsPluginFrame.cpp +++ b/layout/generic/nsPluginFrame.cpp @@ -606,9 +606,19 @@ nsPluginFrame::CallSetWindow(bool aCheckIsHidden) if (aCheckIsHidden && IsHidden()) return NS_ERROR_FAILURE; + // Calling either nsPluginInstanceOwner::FixUpPluginWindow() (here, + // on OS X) or SetWindow() (below, on all platforms) can destroy this + // frame. (FixUpPluginWindow() calls SetWindow()). So grab a safe + // reference to mInstanceOwner which we can use below, if needed. + nsRefPtr instanceOwnerRef(mInstanceOwner); + // refresh the plugin port as well #ifdef XP_MACOSX mInstanceOwner->FixUpPluginWindow(nsPluginInstanceOwner::ePluginPaintEnable); + // Bail now if our frame has been destroyed. + if (!instanceOwnerRef->GetFrame()) { + return NS_ERROR_FAILURE; + } #endif window->window = mInstanceOwner->GetPluginPort(); @@ -640,10 +650,6 @@ nsPluginFrame::CallSetWindow(bool aCheckIsHidden) window->width = intBounds.width / intScaleFactor; window->height = intBounds.height / intScaleFactor; - // Calling SetWindow might destroy this frame. We need to use the instance - // owner to clean up so hold a ref. - nsRefPtr instanceOwnerRef(mInstanceOwner); - // This will call pi->SetWindow and take care of window subclassing // if needed, see bug 132759. Calling SetWindow can destroy this frame // so check for that before doing anything else with this frame's memory. @@ -1751,13 +1757,13 @@ nsPluginFrame::HandleEvent(nsPresContext* aPresContext, mInstanceOwner->ConsiderNewEventloopNestingLevel(); - if (anEvent->mMessage == NS_PLUGIN_ACTIVATE) { + if (anEvent->mMessage == ePluginActivate) { nsIFocusManager* fm = nsFocusManager::GetFocusManager(); nsCOMPtr elem = do_QueryInterface(GetContent()); if (fm && elem) return fm->SetFocus(elem, 0); } - else if (anEvent->mMessage == NS_PLUGIN_FOCUS) { + else if (anEvent->mMessage == ePluginFocus) { nsIFocusManager* fm = nsFocusManager::GetFocusManager(); if (fm) { nsCOMPtr content = GetContent(); diff --git a/netwerk/base/nsSocketTransportService2.cpp b/netwerk/base/nsSocketTransportService2.cpp index 95e6475253..f1356bb956 100644 --- a/netwerk/base/nsSocketTransportService2.cpp +++ b/netwerk/base/nsSocketTransportService2.cpp @@ -64,6 +64,8 @@ nsSocketTransportService::nsSocketTransportService() , mIdleCount(0) , mSentBytesCount(0) , mReceivedBytesCount(0) + , mEventQueueLock("nsSocketTransportService::mEventQueueLock") + , mPendingSocketQ(mEventQueueLock) , mSendBufferSize(0) , mKeepaliveIdleTimeS(600) , mKeepaliveRetryIntervalS(1) @@ -159,7 +161,10 @@ nsSocketTransportService::NotifyWhenCanAttachSocket(nsIRunnable *event) return Dispatch(event, NS_DISPATCH_NORMAL); } - mPendingSocketQ.PutEvent(event); + { + MutexAutoLock lock(mEventQueueLock); + mPendingSocketQ.PutEvent(event, lock); + } return NS_OK; } @@ -212,7 +217,11 @@ nsSocketTransportService::DetachSocket(SocketContext *listHead, SocketContext *s // notify the first element on the pending socket queue... // nsCOMPtr event; - if (mPendingSocketQ.GetPendingEvent(getter_AddRefs(event))) { + { + MutexAutoLock lock(mEventQueueLock); + mPendingSocketQ.GetPendingEvent(getter_AddRefs(event), lock); + } + if (event) { // move event from pending queue to dispatch queue return Dispatch(event, NS_DISPATCH_NORMAL); } diff --git a/netwerk/base/nsSocketTransportService2.h b/netwerk/base/nsSocketTransportService2.h index 7c54fceb1e..9f802f736b 100644 --- a/netwerk/base/nsSocketTransportService2.h +++ b/netwerk/base/nsSocketTransportService2.h @@ -213,6 +213,7 @@ private: // pending socket queue - see NotifyWhenCanAttachSocket //------------------------------------------------------------------------- + mozilla::Mutex mEventQueueLock; nsEventQueue mPendingSocketQ; // queue of nsIRunnable objects // Preference Monitor for SendBufferSize and Keepalive prefs. diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index 8f06ca99b4..aa66e73e3d 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -31,39 +31,35 @@ NS_EVENT_MESSAGE(eAfterKeyUp, eWindowEventFirst + 37) NS_EVENT_MESSAGE(eResize, eWindowEventFirst + 60) NS_EVENT_MESSAGE(eScroll, eWindowEventFirst + 61) -// A plugin was clicked or otherwise focused. NS_PLUGIN_ACTIVATE should be -// used when the window is not active. NS_PLUGIN_FOCUS should be used when +// A plugin was clicked or otherwise focused. ePluginActivate should be +// used when the window is not active. ePluginFocus should be used when // the window is active. In the latter case, the dispatcher of the event // is expected to ensure that the plugin's widget is focused beforehand. -NS_EVENT_MESSAGE(NS_PLUGIN_ACTIVATE, eWindowEventFirst + 62) -NS_EVENT_MESSAGE(NS_PLUGIN_FOCUS, eWindowEventFirst + 63) +NS_EVENT_MESSAGE(ePluginActivate, eWindowEventFirst + 62) +NS_EVENT_MESSAGE(ePluginFocus, eWindowEventFirst + 63) -NS_EVENT_MESSAGE(NS_OFFLINE, eWindowEventFirst + 64) -NS_EVENT_MESSAGE(NS_ONLINE, eWindowEventFirst + 65) +NS_EVENT_MESSAGE(eOffline, eWindowEventFirst + 64) +NS_EVENT_MESSAGE(eOnline, eWindowEventFirst + 65) // NS_BEFORERESIZE_EVENT used to be here (eWindowEventFirst + 66) -// Indicates that the user is either idle or active -NS_EVENT_MESSAGE(NS_MOZ_USER_IDLE, eWindowEventFirst + 67) -NS_EVENT_MESSAGE(NS_MOZ_USER_ACTIVE, eWindowEventFirst + 68) +NS_EVENT_MESSAGE(eLanguageChange, eWindowEventFirst + 70) -NS_EVENT_MESSAGE(NS_LANGUAGECHANGE, eWindowEventFirst + 70) - -NS_EVENT_MESSAGE(NS_MOUSE_MESSAGE_START, 300) -NS_EVENT_MESSAGE(NS_MOUSE_MOVE, NS_MOUSE_MESSAGE_START) -NS_EVENT_MESSAGE(NS_MOUSE_BUTTON_UP, NS_MOUSE_MESSAGE_START + 1) -NS_EVENT_MESSAGE(NS_MOUSE_BUTTON_DOWN, NS_MOUSE_MESSAGE_START + 2) -NS_EVENT_MESSAGE(NS_MOUSE_ENTER_WIDGET, NS_MOUSE_MESSAGE_START + 22) -NS_EVENT_MESSAGE(NS_MOUSE_EXIT_WIDGET, NS_MOUSE_MESSAGE_START + 23) -NS_EVENT_MESSAGE(NS_MOUSE_DOUBLECLICK, NS_MOUSE_MESSAGE_START + 24) -NS_EVENT_MESSAGE(NS_MOUSE_CLICK, NS_MOUSE_MESSAGE_START + 27) -NS_EVENT_MESSAGE(NS_MOUSE_ACTIVATE, NS_MOUSE_MESSAGE_START + 30) -NS_EVENT_MESSAGE(NS_MOUSE_OVER, NS_MOUSE_MESSAGE_START + 31) -NS_EVENT_MESSAGE(NS_MOUSE_OUT, NS_MOUSE_MESSAGE_START + 32) -NS_EVENT_MESSAGE(NS_MOUSE_MOZHITTEST, NS_MOUSE_MESSAGE_START + 33) -NS_EVENT_MESSAGE(NS_MOUSEENTER, NS_MOUSE_MESSAGE_START + 34) -NS_EVENT_MESSAGE(NS_MOUSELEAVE, NS_MOUSE_MESSAGE_START + 35) -NS_EVENT_MESSAGE(NS_MOUSE_MOZLONGTAP, NS_MOUSE_MESSAGE_START + 36) +NS_EVENT_MESSAGE(eMouseEventFirst, 300) +NS_EVENT_MESSAGE(NS_MOUSE_MOVE, eMouseEventFirst) +NS_EVENT_MESSAGE(NS_MOUSE_BUTTON_UP, eMouseEventFirst + 1) +NS_EVENT_MESSAGE(NS_MOUSE_BUTTON_DOWN, eMouseEventFirst + 2) +NS_EVENT_MESSAGE(NS_MOUSE_ENTER_WIDGET, eMouseEventFirst + 22) +NS_EVENT_MESSAGE(NS_MOUSE_EXIT_WIDGET, eMouseEventFirst + 23) +NS_EVENT_MESSAGE(NS_MOUSE_DOUBLECLICK, eMouseEventFirst + 24) +NS_EVENT_MESSAGE(NS_MOUSE_CLICK, eMouseEventFirst + 27) +NS_EVENT_MESSAGE(NS_MOUSE_ACTIVATE, eMouseEventFirst + 30) +NS_EVENT_MESSAGE(NS_MOUSE_OVER, eMouseEventFirst + 31) +NS_EVENT_MESSAGE(NS_MOUSE_OUT, eMouseEventFirst + 32) +NS_EVENT_MESSAGE(NS_MOUSE_MOZHITTEST, eMouseEventFirst + 33) +NS_EVENT_MESSAGE(NS_MOUSEENTER, eMouseEventFirst + 34) +NS_EVENT_MESSAGE(NS_MOUSELEAVE, eMouseEventFirst + 35) +NS_EVENT_MESSAGE(NS_MOUSE_MOZLONGTAP, eMouseEventFirst + 36) // Pointer spec events NS_EVENT_MESSAGE(NS_POINTER_EVENT_START, 4400) diff --git a/widget/WidgetEventImpl.cpp b/widget/WidgetEventImpl.cpp index 6f1aee074f..3bc59a080b 100644 --- a/widget/WidgetEventImpl.cpp +++ b/widget/WidgetEventImpl.cpp @@ -151,8 +151,8 @@ WidgetEvent::HasIMEEventMessage() const bool WidgetEvent::HasPluginActivationEventMessage() const { - return mMessage == NS_PLUGIN_ACTIVATE || - mMessage == NS_PLUGIN_FOCUS; + return mMessage == ePluginActivate || + mMessage == ePluginFocus; } /****************************************************************************** diff --git a/xpcom/tests/gtest/TestThreads.cpp b/xpcom/tests/gtest/TestThreads.cpp index 166836a6a3..4f6055dce9 100644 --- a/xpcom/tests/gtest/TestThreads.cpp +++ b/xpcom/tests/gtest/TestThreads.cpp @@ -11,9 +11,10 @@ #include "nsCOMPtr.h" #include "nsIServiceManager.h" #include "nsXPCOM.h" +#include "mozilla/Monitor.h" #include "gtest/gtest.h" -class nsRunner MOZ_FINAL : public nsIRunnable { +class nsRunner final : public nsIRunnable { ~nsRunner() {} public: NS_DECL_THREADSAFE_ISUPPORTS @@ -61,7 +62,7 @@ TEST(Threads, Main) PR_Sleep(PR_MillisecondsToInterval(100)); // hopefully the runner will quit here } -class nsStressRunner MOZ_FINAL : public nsIRunnable { +class nsStressRunner final : public nsIRunnable { public: NS_DECL_THREADSAFE_ISUPPORTS @@ -124,6 +125,116 @@ TEST(Threads, Stress) } } +mozilla::Monitor* gAsyncShutdownReadyMonitor; +mozilla::Monitor* gBeginAsyncShutdownMonitor; + +class AsyncShutdownPreparer : public nsIRunnable { +public: + NS_DECL_THREADSAFE_ISUPPORTS + + NS_IMETHOD Run() override { + EXPECT_FALSE(mWasRun); + mWasRun = true; + + mozilla::MonitorAutoLock lock(*gAsyncShutdownReadyMonitor); + lock.Notify(); + + return NS_OK; + } + + explicit AsyncShutdownPreparer() : mWasRun(false) {} + +private: + virtual ~AsyncShutdownPreparer() { + EXPECT_TRUE(mWasRun); + } + +protected: + bool mWasRun; +}; + +NS_IMPL_ISUPPORTS(AsyncShutdownPreparer, nsIRunnable) + +class AsyncShutdownWaiter : public nsIRunnable { +public: + NS_DECL_THREADSAFE_ISUPPORTS + + NS_IMETHOD Run() override { + EXPECT_FALSE(mWasRun); + mWasRun = true; + + nsCOMPtr t; + nsresult rv; + + { + mozilla::MonitorAutoLock lock(*gBeginAsyncShutdownMonitor); + + rv = NS_NewThread(getter_AddRefs(t), new AsyncShutdownPreparer()); + EXPECT_TRUE(NS_SUCCEEDED(rv)); + + lock.Wait(); + } + + rv = t->AsyncShutdown(); + EXPECT_TRUE(NS_SUCCEEDED(rv)); + + return NS_OK; + } + + explicit AsyncShutdownWaiter() : mWasRun(false) {} + +private: + virtual ~AsyncShutdownWaiter() { + EXPECT_TRUE(mWasRun); + } + +protected: + bool mWasRun; +}; + +NS_IMPL_ISUPPORTS(AsyncShutdownWaiter, nsIRunnable) + +class SameThreadSentinel : public nsIRunnable { +public: + NS_DECL_ISUPPORTS + + NS_IMETHOD Run() override { + mozilla::MonitorAutoLock lock(*gBeginAsyncShutdownMonitor); + lock.Notify(); + return NS_OK; + } + +private: + virtual ~SameThreadSentinel() {} +}; + +NS_IMPL_ISUPPORTS(SameThreadSentinel, nsIRunnable) + +TEST(Threads, AsyncShutdown) +{ + gAsyncShutdownReadyMonitor = new mozilla::Monitor("gAsyncShutdownReady"); + gBeginAsyncShutdownMonitor = new mozilla::Monitor("gBeginAsyncShutdown"); + + nsCOMPtr t; + nsresult rv; + + { + mozilla::MonitorAutoLock lock(*gAsyncShutdownReadyMonitor); + + rv = NS_NewThread(getter_AddRefs(t), new AsyncShutdownWaiter()); + EXPECT_TRUE(NS_SUCCEEDED(rv)); + + lock.Wait(); + } + + NS_DispatchToCurrentThread(new SameThreadSentinel()); + rv = t->Shutdown(); + EXPECT_TRUE(NS_SUCCEEDED(rv)); + + delete gAsyncShutdownReadyMonitor; + delete gBeginAsyncShutdownMonitor; +} + static void threadProc(void *arg) { // printf(" running thread %d\n", (int) arg); diff --git a/xpcom/threads/LazyIdleThread.cpp b/xpcom/threads/LazyIdleThread.cpp index 1ed33ef036..1a138b59c1 100644 --- a/xpcom/threads/LazyIdleThread.cpp +++ b/xpcom/threads/LazyIdleThread.cpp @@ -456,6 +456,13 @@ LazyIdleThread::GetPRThread(PRThread** aPRThread) return NS_ERROR_NOT_AVAILABLE; } +NS_IMETHODIMP +LazyIdleThread::AsyncShutdown() +{ + ASSERT_OWNING_THREAD(); + return NS_ERROR_NOT_IMPLEMENTED; +} + NS_IMETHODIMP LazyIdleThread::Shutdown() { diff --git a/xpcom/threads/nsEventQueue.cpp b/xpcom/threads/nsEventQueue.cpp index a725ce23a7..4a225c052d 100644 --- a/xpcom/threads/nsEventQueue.cpp +++ b/xpcom/threads/nsEventQueue.cpp @@ -27,18 +27,18 @@ GetLog() #endif #define LOG(args) MOZ_LOG(GetLog(), mozilla::LogLevel::Debug, args) -nsEventQueue::nsEventQueue() - : mReentrantMonitor("nsEventQueue.mReentrantMonitor") - , mHead(nullptr) +nsEventQueue::nsEventQueue(Mutex& aLock) + : mHead(nullptr) , mTail(nullptr) , mOffsetHead(0) , mOffsetTail(0) + , mEventsAvailable(aLock, "[nsEventQueue.mEventsAvailable]") { } nsEventQueue::~nsEventQueue() { - // It'd be nice to be able to assert that no one else is holding the monitor, + // It'd be nice to be able to assert that no one else is holding the lock, // but NSPR doesn't really expose APIs for it. NS_ASSERTION(IsEmpty(), "Non-empty event queue being destroyed; events being leaked."); @@ -49,33 +49,30 @@ nsEventQueue::~nsEventQueue() } bool -nsEventQueue::GetEvent(bool aMayWait, nsIRunnable** aResult) +nsEventQueue::GetEvent(bool aMayWait, nsIRunnable** aResult, + MutexAutoLock& aProofOfLock) { - { - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - - while (IsEmpty()) { - if (!aMayWait) { - if (aResult) { - *aResult = nullptr; - } - return false; + while (IsEmpty()) { + if (!aMayWait) { + if (aResult) { + *aResult = nullptr; } - LOG(("EVENTQ(%p): wait begin\n", this)); - mon.Wait(); - LOG(("EVENTQ(%p): wait end\n", this)); + return false; } + LOG(("EVENTQ(%p): wait begin\n", this)); + mEventsAvailable.Wait(); + LOG(("EVENTQ(%p): wait end\n", this)); + } - if (aResult) { - *aResult = mHead->mEvents[mOffsetHead++]; + if (aResult) { + *aResult = mHead->mEvents[mOffsetHead++]; - // Check if mHead points to empty Page - if (mOffsetHead == EVENTS_PER_PAGE) { - Page* dead = mHead; - mHead = mHead->mNext; - FreePage(dead); - mOffsetHead = 0; - } + // Check if mHead points to empty Page + if (mOffsetHead == EVENTS_PER_PAGE) { + Page* dead = mHead; + mHead = mHead->mNext; + FreePage(dead); + mOffsetHead = 0; } } @@ -83,28 +80,9 @@ nsEventQueue::GetEvent(bool aMayWait, nsIRunnable** aResult) } void -nsEventQueue::PutEvent(nsIRunnable* aRunnable) +nsEventQueue::PutEvent(already_AddRefed&& aRunnable, + MutexAutoLock& aProofOfLock) { - nsCOMPtr event(aRunnable); - PutEvent(event.forget()); -} - -void -nsEventQueue::PutEvent(already_AddRefed&& aRunnable) -{ - // Avoid calling AddRef+Release while holding our monitor. - nsCOMPtr event(aRunnable); - - if (ChaosMode::isActive(ChaosFeature::ThreadScheduling)) { - // With probability 0.5, yield so other threads have a chance to - // dispatch events to this queue first. - if (ChaosMode::randomUint32LessThan(2)) { - PR_Sleep(PR_INTERVAL_NO_WAIT); - } - } - - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - if (!mHead) { mHead = NewPage(); MOZ_ASSERT(mHead); @@ -121,17 +99,24 @@ nsEventQueue::PutEvent(already_AddRefed&& aRunnable) mOffsetTail = 0; } - event.swap(mTail->mEvents[mOffsetTail]); + nsIRunnable*& queueLocation = mTail->mEvents[mOffsetTail]; + MOZ_ASSERT(!queueLocation); + queueLocation = aRunnable.take(); ++mOffsetTail; LOG(("EVENTQ(%p): notify\n", this)); - mon.NotifyAll(); + mEventsAvailable.Notify(); +} + +void +nsEventQueue::PutEvent(nsIRunnable* aRunnable, MutexAutoLock& aProofOfLock) +{ + nsCOMPtr event(aRunnable); + PutEvent(event.forget(), aProofOfLock); } size_t -nsEventQueue::Count() +nsEventQueue::Count(MutexAutoLock& aProofOfLock) { - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - // It is obvious count is 0 when the queue is empty. if (!mHead) { return 0; diff --git a/xpcom/threads/nsEventQueue.h b/xpcom/threads/nsEventQueue.h index 4b5b6f02e9..664798b295 100644 --- a/xpcom/threads/nsEventQueue.h +++ b/xpcom/threads/nsEventQueue.h @@ -8,25 +8,29 @@ #define nsEventQueue_h__ #include -#include "mozilla/ReentrantMonitor.h" +#include "mozilla/CondVar.h" +#include "mozilla/Mutex.h" #include "nsIRunnable.h" #include "nsCOMPtr.h" #include "mozilla/AlreadyAddRefed.h" +class nsThreadPool; + // A threadsafe FIFO event queue... class nsEventQueue { - typedef mozilla::ReentrantMonitor ReentrantMonitor; - public: - nsEventQueue(); + typedef mozilla::MutexAutoLock MutexAutoLock; + + explicit nsEventQueue(mozilla::Mutex& aLock); ~nsEventQueue(); // This method adds a new event to the pending event queue. The queue holds // a strong reference to the event after this method returns. This method // cannot fail. - void PutEvent(nsIRunnable* aEvent); - void PutEvent(already_AddRefed&& aEvent); + void PutEvent(nsIRunnable* aEvent, MutexAutoLock& aProofOfLock); + void PutEvent(already_AddRefed&& aEvent, + MutexAutoLock& aProofOfLock); // This method gets an event from the event queue. If mayWait is true, then // the method will block the calling thread until an event is available. If @@ -34,30 +38,24 @@ public: // or not an event is pending. When the resulting event is non-null, the // caller is responsible for releasing the event object. This method does // not alter the reference count of the resulting event. - bool GetEvent(bool aMayWait, nsIRunnable** aEvent); + bool GetEvent(bool aMayWait, nsIRunnable** aEvent, + MutexAutoLock& aProofOfLock); // This method returns true if there is a pending event. - bool HasPendingEvent() + bool HasPendingEvent(MutexAutoLock& aProofOfLock) { - return GetEvent(false, nullptr); + return GetEvent(false, nullptr, aProofOfLock); } // This method returns the next pending event or null. - bool GetPendingEvent(nsIRunnable** runnable) + bool GetPendingEvent(nsIRunnable** aRunnable, MutexAutoLock& aProofOfLock) { - return GetEvent(false, runnable); + return GetEvent(false, aRunnable, aProofOfLock); } - // Expose the event queue's monitor for "power users" - ReentrantMonitor& GetReentrantMonitor() - { - return mReentrantMonitor; - } - - size_t Count(); + size_t Count(MutexAutoLock&); private: - bool IsEmpty() { return !mHead || (mHead == mTail && mOffsetHead == mOffsetTail); @@ -89,13 +87,26 @@ private: free(aPage); } - ReentrantMonitor mReentrantMonitor; - Page* mHead; Page* mTail; uint16_t mOffsetHead; // offset into mHead where next item is removed uint16_t mOffsetTail; // offset into mTail where next item is added + mozilla::CondVar mEventsAvailable; + + // These methods are made available to nsThreadPool as a hack, since + // nsThreadPool needs to have its threads sleep for fixed amounts of + // time as well as being able to wake up all threads when thread + // limits change. + friend class nsThreadPool; + void Wait(PRIntervalTime aInterval) + { + mEventsAvailable.Wait(aInterval); + } + void NotifyAll() + { + mEventsAvailable.NotifyAll(); + } }; #endif // nsEventQueue_h__ diff --git a/xpcom/threads/nsIThread.idl b/xpcom/threads/nsIThread.idl index feeae695ec..e9bd22c59c 100644 --- a/xpcom/threads/nsIThread.idl +++ b/xpcom/threads/nsIThread.idl @@ -17,7 +17,7 @@ * * See nsIThreadManager for the API used to create and locate threads. */ -[scriptable, uuid(9c889946-a73a-4af3-ae9a-ea64f7d4e3ca)] +[scriptable, uuid(594feb13-6164-4054-b5a1-ad62e10ea15d)] interface nsIThread : nsIEventTarget { /** @@ -82,4 +82,26 @@ interface nsIThread : nsIEventTarget * not the current thread. */ boolean processNextEvent(in boolean mayWait); + + /** + * Shutdown the thread asynchronously. This method immediately prevents + * further dispatch of events to the thread, and it causes any pending events + * to run to completion before this thread joins with the current thread. + * + * UNLIKE shutdown() this does not process events on the current thread. + * Instead it merely ensures that the current thread continues running until + * this thread has shut down. + * + * This method MAY NOT be executed from the thread itself. Instead, it is + * meant to be executed from another thread (usually the thread that created + * this thread or the main application thread). When this function returns, + * the thread will continue running until it exhausts its event queue. + * + * @throws NS_ERROR_UNEXPECTED + * Indicates that this method was erroneously called when this thread was + * the current thread, that this thread was not created with a call to + * nsIThreadManager::NewThread, or if this method was called more than once + * on the thread object. + */ + void asyncShutdown(); }; diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 6e65717864..e64a5d77b6 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -21,6 +21,7 @@ #include "nsIClassInfoImpl.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" +#include "nsQueryObject.h" #include "pratom.h" #include "mozilla/CycleCollectedJSRuntime.h" #include "mozilla/Logging.h" @@ -238,28 +239,43 @@ private: struct nsThreadShutdownContext { + // NB: This will be the last reference. + nsRefPtr terminatingThread; nsThread* joiningThread; - bool shutdownAck; + bool awaitingShutdownAck; }; // This event is responsible for notifying nsThread::Shutdown that it is time -// to call PR_JoinThread. -class nsThreadShutdownAckEvent : public nsRunnable +// to call PR_JoinThread. It implements nsICancelableRunnable so that it can +// run on a DOM Worker thread (where all events must implement +// nsICancelableRunnable.) +class nsThreadShutdownAckEvent : public nsRunnable, + public nsICancelableRunnable { public: explicit nsThreadShutdownAckEvent(nsThreadShutdownContext* aCtx) : mShutdownContext(aCtx) { } - NS_IMETHOD Run() + NS_DECL_ISUPPORTS_INHERITED + NS_IMETHOD Run() override { - mShutdownContext->shutdownAck = true; + mShutdownContext->terminatingThread->ShutdownComplete(mShutdownContext); return NS_OK; } + NS_IMETHOD Cancel() override + { + return Run(); + } private: + virtual ~nsThreadShutdownAckEvent() { } + nsThreadShutdownContext* mShutdownContext; }; +NS_IMPL_ISUPPORTS_INHERITED(nsThreadShutdownAckEvent, nsRunnable, + nsICancelableRunnable) + // This event is responsible for setting mShutdownContext class nsThreadShutdownEvent : public nsRunnable { @@ -342,9 +358,12 @@ nsThread::ThreadFunc(void* aArg) // Wait for and process startup event nsCOMPtr event; - if (!self->GetEvent(true, getter_AddRefs(event))) { - NS_WARNING("failed waiting for thread startup event"); - return; + { + MutexAutoLock lock(self->mLock); + if (!self->mEvents->GetEvent(true, getter_AddRefs(event), lock)) { + NS_WARNING("failed waiting for thread startup event"); + return; + } } event->Run(); // unblocks nsThread::Init event = nullptr; @@ -369,11 +388,18 @@ nsThread::ThreadFunc(void* aArg) // mEventsAreDoomed atomically with the removal of the last event. The key // invariant here is that we will never permit PutEvent to succeed if the // event would be left in the queue after our final call to - // NS_ProcessPendingEvents. + // NS_ProcessPendingEvents. We also have to keep processing events as long + // as we have outstanding mRequestedShutdownContexts. while (true) { + // Check and see if we're waiting on any threads. + while (self->mRequestedShutdownContexts.Length()) { + // We can't stop accepting events just yet. Block and check again. + NS_ProcessNextEvent(self, true); + } + { MutexAutoLock lock(self->mLock); - if (!self->mEvents->HasPendingEvent()) { + if (!self->mEvents->HasPendingEvent(lock)) { // No events in the queue, so we will stop now. Don't let any more // events be added, since they won't be processed. It is critical // that no PutEvent can occur between testing that the event queue is @@ -394,7 +420,8 @@ nsThread::ThreadFunc(void* aArg) nsThreadManager::get()->UnregisterCurrentThread(self); // Dispatch shutdown ACK - event = new nsThreadShutdownAckEvent(self->mShutdownContext); + MOZ_ASSERT(self->mShutdownContext->terminatingThread == self); + event = do_QueryObject(new nsThreadShutdownAckEvent(self->mShutdownContext)); self->mShutdownContext->joiningThread->Dispatch(event, NS_DISPATCH_NORMAL); // Release any observer of the thread here. @@ -417,6 +444,7 @@ nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize) : mLock("nsThread.mLock") , mScriptObserver(nullptr) , mEvents(&mEventsRoot) + , mEventsRoot(mLock) , mPriority(PRIORITY_NORMAL) , mThread(nullptr) , mNestedEventLoopDepth(0) @@ -456,7 +484,7 @@ nsThread::Init() // that mThread is set properly. { MutexAutoLock lock(mLock); - mEventsRoot.PutEvent(startup); // retain a reference + mEventsRoot.PutEvent(startup, lock); // retain a reference } // Wait for thread to call ThreadManager::SetupCurrentThread, which completes @@ -502,7 +530,7 @@ nsThread::PutEvent(already_AddRefed&& aEvent, nsNestedEventTarget* nsIRunnable* temp2 = temp.forget().take(); // can't use unused << aEvent here due to Windows (boo) return temp2 ? NS_ERROR_UNEXPECTED : NS_ERROR_UNEXPECTED; // to make compiler not bletch on us } - queue->PutEvent(Move(aEvent)); + queue->PutEvent(Move(aEvent), lock); // Make sure to grab the observer before dropping the lock, otherwise the // event that we just placed into the queue could run and eventually delete @@ -603,9 +631,9 @@ nsThread::GetPRThread(PRThread** aResult) } NS_IMETHODIMP -nsThread::Shutdown() +nsThread::AsyncShutdown() { - LOG(("THRD(%p) shutdown\n", this)); + LOG(("THRD(%p) async shutdown\n", this)); // XXX If we make this warn, then we hit that warning at xpcom shutdown while // shutting down a thread in a thread pool. That happens b/c the thread @@ -614,37 +642,61 @@ nsThread::Shutdown() return NS_OK; } + return !!ShutdownInternal(/* aSync = */ false) ? NS_OK : NS_ERROR_UNEXPECTED; +} + +nsThreadShutdownContext* +nsThread::ShutdownInternal(bool aSync) +{ + MOZ_ASSERT(mThread); + if (NS_WARN_IF(mThread == PR_GetCurrentThread())) { - return NS_ERROR_UNEXPECTED; + return nullptr; } // Prevent multiple calls to this method { MutexAutoLock lock(mLock); if (!mShutdownRequired) { - return NS_ERROR_UNEXPECTED; + return nullptr; } mShutdownRequired = false; } - nsThreadShutdownContext context; - context.joiningThread = nsThreadManager::get()->GetCurrentThread(); - context.shutdownAck = false; + nsThread* currentThread = nsThreadManager::get()->GetCurrentThread(); + MOZ_ASSERT(currentThread); + + nsAutoPtr& context = + *currentThread->mRequestedShutdownContexts.AppendElement(); + context = new nsThreadShutdownContext(); + + context->terminatingThread = this; + context->joiningThread = currentThread; + context->awaitingShutdownAck = aSync; // Set mShutdownContext and wake up the thread in case it is waiting for // events to process. - nsCOMPtr event = new nsThreadShutdownEvent(this, &context); + nsCOMPtr event = new nsThreadShutdownEvent(this, context); // XXXroc What if posting the event fails due to OOM? PutEvent(event.forget(), nullptr); // We could still end up with other events being added after the shutdown // task, but that's okay because we process pending events in ThreadFunc // after setting mShutdownContext just before exiting. + return context; +} - // Process events on the current thread until we receive a shutdown ACK. - // Allows waiting; ensure no locks are held that would deadlock us! - while (!context.shutdownAck) { - NS_ProcessNextEvent(context.joiningThread, true); +void +nsThread::ShutdownComplete(nsThreadShutdownContext* aContext) +{ + MOZ_ASSERT(mThread); + MOZ_ASSERT(aContext->terminatingThread == this); + + if (aContext->awaitingShutdownAck) { + // We're in a synchronous shutdown, so tell whatever is up the stack that + // we're done and unwind the stack so it can call us again. + aContext->awaitingShutdownAck = false; + return; } // Now, it should be safe to join without fear of dead-locking. @@ -664,6 +716,34 @@ nsThread::Shutdown() } #endif + // Delete aContext. + MOZ_ALWAYS_TRUE( + aContext->joiningThread->mRequestedShutdownContexts.RemoveElement(aContext)); +} + +NS_IMETHODIMP +nsThread::Shutdown() +{ + LOG(("THRD(%p) sync shutdown\n", this)); + + // XXX If we make this warn, then we hit that warning at xpcom shutdown while + // shutting down a thread in a thread pool. That happens b/c the thread + // in the thread pool is already shutdown by the thread manager. + if (!mThread) { + return NS_OK; + } + + nsThreadShutdownContext* context = ShutdownInternal(/* aSync = */ true); + NS_ENSURE_TRUE(context, NS_ERROR_UNEXPECTED); + + // Process events on the current thread until we receive a shutdown ACK. + // Allows waiting; ensure no locks are held that would deadlock us! + while (context->awaitingShutdownAck) { + NS_ProcessNextEvent(context->joiningThread, true); + } + + ShutdownComplete(context); + return NS_OK; } @@ -674,7 +754,10 @@ nsThread::HasPendingEvents(bool* aResult) return NS_ERROR_NOT_SAME_THREAD; } - *aResult = mEvents->GetEvent(false, nullptr); + { + MutexAutoLock lock(mLock); + *aResult = mEvents->HasPendingEvent(lock); + } return NS_OK; } @@ -833,7 +916,10 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult) // If we are shutting down, then do not wait for new events. nsCOMPtr event; - mEvents->GetEvent(reallyWait, getter_AddRefs(event)); + { + MutexAutoLock lock(mLock); + mEvents->GetEvent(reallyWait, getter_AddRefs(event), lock); + } *aResult = (event.get() != nullptr); @@ -989,7 +1075,7 @@ nsThread::PushEventQueue(nsIEventTarget** aResult) return NS_ERROR_NOT_SAME_THREAD; } - nsChainedEventQueue* queue = new nsChainedEventQueue(); + nsChainedEventQueue* queue = new nsChainedEventQueue(mLock); queue->mEventTarget = new nsNestedEventTarget(this, queue); { @@ -1031,8 +1117,8 @@ nsThread::PopEventQueue(nsIEventTarget* aInnermostTarget) mEvents = mEvents->mNext; nsCOMPtr event; - while (queue->GetEvent(false, getter_AddRefs(event))) { - mEvents->PutEvent(event.forget()); + while (queue->GetEvent(false, getter_AddRefs(event), lock)) { + mEvents->PutEvent(event.forget(), lock); } // Don't let the event target post any more events. diff --git a/xpcom/threads/nsThread.h b/xpcom/threads/nsThread.h index b42a939c07..77a38ee75b 100644 --- a/xpcom/threads/nsThread.h +++ b/xpcom/threads/nsThread.h @@ -77,6 +77,8 @@ public: uint32_t RecursionDepth() const; + void ShutdownComplete(struct nsThreadShutdownContext* aContext); + protected: class nsChainedEventQueue; @@ -103,43 +105,44 @@ protected: } // Wrappers for event queue methods: - bool GetEvent(bool aMayWait, nsIRunnable** aEvent) - { - return mEvents->GetEvent(aMayWait, aEvent); - } nsresult PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget); nsresult PutEvent(already_AddRefed&& aEvent, nsNestedEventTarget* aTarget); nsresult DispatchInternal(already_AddRefed&& aEvent, uint32_t aFlags, nsNestedEventTarget* aTarget); + struct nsThreadShutdownContext* ShutdownInternal(bool aSync); + // Wrapper for nsEventQueue that supports chaining. class nsChainedEventQueue { public: - nsChainedEventQueue() + explicit nsChainedEventQueue(mozilla::Mutex& aLock) : mNext(nullptr) + , mQueue(aLock) { } - bool GetEvent(bool aMayWait, nsIRunnable** aEvent) + bool GetEvent(bool aMayWait, nsIRunnable** aEvent, + mozilla::MutexAutoLock& aProofOfLock) { - return mQueue.GetEvent(aMayWait, aEvent); + return mQueue.GetEvent(aMayWait, aEvent, aProofOfLock); } - void PutEvent(nsIRunnable* aEvent) + void PutEvent(nsIRunnable* aEvent, mozilla::MutexAutoLock& aProofOfLock) { - mQueue.PutEvent(aEvent); + mQueue.PutEvent(aEvent, aProofOfLock); } - void PutEvent(already_AddRefed&& aEvent) + void PutEvent(already_AddRefed&& aEvent, + mozilla::MutexAutoLock& aProofOfLock) { - mQueue.PutEvent(mozilla::Move(aEvent)); + mQueue.PutEvent(mozilla::Move(aEvent), aProofOfLock); } - bool HasPendingEvent() + bool HasPendingEvent(mozilla::MutexAutoLock& aProofOfLock) { - return mQueue.HasPendingEvent(); + return mQueue.HasPendingEvent(aProofOfLock); } nsChainedEventQueue* mNext; @@ -193,7 +196,10 @@ protected: uint32_t mNestedEventLoopDepth; uint32_t mStackSize; + // The shutdown context for ourselves. struct nsThreadShutdownContext* mShutdownContext; + // The shutdown contexts for any other threads we've asked to shut down. + nsTArray> mRequestedShutdownContexts; bool mShutdownRequired; // Set to true when events posted to this thread will never run. diff --git a/xpcom/threads/nsThreadPool.cpp b/xpcom/threads/nsThreadPool.cpp index f8d06bf239..f30d65896f 100644 --- a/xpcom/threads/nsThreadPool.cpp +++ b/xpcom/threads/nsThreadPool.cpp @@ -48,13 +48,16 @@ NS_IMPL_QUERY_INTERFACE_CI(nsThreadPool, nsIThreadPool, nsIEventTarget, NS_IMPL_CI_INTERFACE_GETTER(nsThreadPool, nsIThreadPool, nsIEventTarget) nsThreadPool::nsThreadPool() - : mThreadLimit(DEFAULT_THREAD_LIMIT) + : mMutex("[nsThreadPool.mMutex]") + , mEvents(mMutex) + , mThreadLimit(DEFAULT_THREAD_LIMIT) , mIdleThreadLimit(DEFAULT_IDLE_THREAD_LIMIT) , mIdleThreadTimeout(DEFAULT_IDLE_THREAD_TIMEOUT) , mIdleCount(0) , mStackSize(nsIThreadManager::DEFAULT_STACK_SIZE) , mShutdown(false) { + LOG(("THRD-P(%p) constructor!!!\n", this)); } nsThreadPool::~nsThreadPool() @@ -79,7 +82,7 @@ nsThreadPool::PutEvent(already_AddRefed&& aEvent) bool spawnThread = false; uint32_t stackSize = 0; { - ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); + MutexAutoLock lock(mMutex); if (NS_WARN_IF(mShutdown)) { return NS_ERROR_NOT_AVAILABLE; @@ -92,11 +95,11 @@ nsThreadPool::PutEvent(already_AddRefed&& aEvent) if (mThreads.Count() < (int32_t)mThreadLimit && // Spawn a new thread if we don't have enough idle threads to serve // pending events immediately. - mEvents.Count() >= mIdleCount) { + mEvents.Count(lock) >= mIdleCount) { spawnThread = true; } - mEvents.PutEvent(Move(aEvent)); + mEvents.PutEvent(Move(aEvent), lock); stackSize = mStackSize; } @@ -115,7 +118,7 @@ nsThreadPool::PutEvent(already_AddRefed&& aEvent) bool killThread = false; { - ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); + MutexAutoLock lock(mMutex); if (mThreads.Count() < (int32_t)mThreadLimit) { mThreads.AppendObject(thread); } else { @@ -124,15 +127,9 @@ nsThreadPool::PutEvent(already_AddRefed&& aEvent) } LOG(("THRD-P(%p) put [%p kill=%d]\n", this, thread.get(), killThread)); if (killThread) { - // Pending events are processed on the current thread during - // nsIThread::Shutdown() execution, so if nsThreadPool::Dispatch() is called - // under caller's lock then deadlock could occur. This happens e.g. in case - // of nsStreamCopier. To prevent this situation, dispatch a shutdown event - // to the current thread instead of calling nsIThread::Shutdown() directly. - - nsCOMPtr r = NS_NewRunnableMethod(thread, - &nsIThread::Shutdown); - NS_DispatchToCurrentThread(r); + // We never dispatched any events to the thread, so we can shut it down + // asynchronously without worrying about anything. + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->AsyncShutdown())); } else { thread->Dispatch(this, NS_DISPATCH_NORMAL); } @@ -157,10 +154,10 @@ nsThreadPool::ShutdownThread(nsIThread* aThread) NS_IMETHODIMP nsThreadPool::Run() { - LOG(("THRD-P(%p) enter\n", this)); - mThreadNaming.SetThreadPoolName(mName); + LOG(("THRD-P(%p) enter %s\n", this, mName.BeginReading())); + nsCOMPtr current; nsThreadManager::get()->GetCurrentThread(getter_AddRefs(current)); @@ -171,7 +168,7 @@ nsThreadPool::Run() nsCOMPtr listener; { - ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); + MutexAutoLock lock(mMutex); listener = mListener; } @@ -182,8 +179,9 @@ nsThreadPool::Run() do { nsCOMPtr event; { - ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); - if (!mEvents.GetPendingEvent(getter_AddRefs(event))) { + MutexAutoLock lock(mMutex); + + if (!mEvents.GetPendingEvent(getter_AddRefs(event), lock)) { PRIntervalTime now = PR_IntervalNow(); PRIntervalTime timeout = PR_MillisecondsToInterval(mIdleThreadTimeout); @@ -215,8 +213,9 @@ nsThreadPool::Run() shutdownThreadOnExit = mThreads.RemoveObject(current); } else { PRIntervalTime delta = timeout - (now - idleSince); - LOG(("THRD-P(%p) waiting [%d]\n", this, delta)); - mon.Wait(delta); + LOG(("THRD-P(%p) %s waiting [%d]\n", this, mName.BeginReading(), delta)); + mEvents.Wait(delta); + LOG(("THRD-P(%p) done waiting\n", this)); } } else if (wasIdle) { wasIdle = false; @@ -224,7 +223,7 @@ nsThreadPool::Run() } } if (event) { - LOG(("THRD-P(%p) running [%p]\n", this, event.get())); + LOG(("THRD-P(%p) %s running [%p]\n", this, mName.BeginReading(), event.get())); event->Run(); } } while (!exitThread); @@ -281,7 +280,7 @@ nsThreadPool::Dispatch(already_AddRefed&& aEvent, uint32_t aFlags) NS_IMETHODIMP nsThreadPool::IsOnCurrentThread(bool* aResult) { - ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); + MutexAutoLock lock(mMutex); if (NS_WARN_IF(mShutdown)) { return NS_ERROR_NOT_AVAILABLE; } @@ -303,9 +302,9 @@ nsThreadPool::Shutdown() nsCOMArray threads; nsCOMPtr listener; { - ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); + MutexAutoLock lock(mMutex); mShutdown = true; - mon.NotifyAll(); + mEvents.NotifyAll(); threads.AppendObjects(mThreads); mThreads.Clear(); @@ -336,14 +335,15 @@ nsThreadPool::GetThreadLimit(uint32_t* aValue) NS_IMETHODIMP nsThreadPool::SetThreadLimit(uint32_t aValue) { - ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); + MutexAutoLock lock(mMutex); + LOG(("THRD-P(%p) thread limit [%u]\n", this, aValue)); mThreadLimit = aValue; if (mIdleThreadLimit > mThreadLimit) { mIdleThreadLimit = mThreadLimit; } if (static_cast(mThreads.Count()) > mThreadLimit) { - mon.NotifyAll(); // wake up threads so they observe this change + mEvents.NotifyAll(); // wake up threads so they observe this change } return NS_OK; } @@ -358,7 +358,8 @@ nsThreadPool::GetIdleThreadLimit(uint32_t* aValue) NS_IMETHODIMP nsThreadPool::SetIdleThreadLimit(uint32_t aValue) { - ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); + MutexAutoLock lock(mMutex); + LOG(("THRD-P(%p) idle thread limit [%u]\n", this, aValue)); mIdleThreadLimit = aValue; if (mIdleThreadLimit > mThreadLimit) { mIdleThreadLimit = mThreadLimit; @@ -366,7 +367,7 @@ nsThreadPool::SetIdleThreadLimit(uint32_t aValue) // Do we need to kill some idle threads? if (mIdleCount > mIdleThreadLimit) { - mon.NotifyAll(); // wake up threads so they observe this change + mEvents.NotifyAll(); // wake up threads so they observe this change } return NS_OK; } @@ -381,13 +382,13 @@ nsThreadPool::GetIdleThreadTimeout(uint32_t* aValue) NS_IMETHODIMP nsThreadPool::SetIdleThreadTimeout(uint32_t aValue) { - ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); + MutexAutoLock lock(mMutex); uint32_t oldTimeout = mIdleThreadTimeout; mIdleThreadTimeout = aValue; // Do we need to notify any idle threads that their sleep time has shortened? if (mIdleThreadTimeout < oldTimeout && mIdleCount > 0) { - mon.NotifyAll(); // wake up threads so they observe this change + mEvents.NotifyAll(); // wake up threads so they observe this change } return NS_OK; } @@ -395,7 +396,7 @@ nsThreadPool::SetIdleThreadTimeout(uint32_t aValue) NS_IMETHODIMP nsThreadPool::GetThreadStackSize(uint32_t* aValue) { - ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); + MutexAutoLock lock(mMutex); *aValue = mStackSize; return NS_OK; } @@ -403,7 +404,7 @@ nsThreadPool::GetThreadStackSize(uint32_t* aValue) NS_IMETHODIMP nsThreadPool::SetThreadStackSize(uint32_t aValue) { - ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); + MutexAutoLock lock(mMutex); mStackSize = aValue; return NS_OK; } @@ -411,7 +412,7 @@ nsThreadPool::SetThreadStackSize(uint32_t aValue) NS_IMETHODIMP nsThreadPool::GetListener(nsIThreadPoolListener** aListener) { - ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); + MutexAutoLock lock(mMutex); NS_IF_ADDREF(*aListener = mListener); return NS_OK; } @@ -421,7 +422,7 @@ nsThreadPool::SetListener(nsIThreadPoolListener* aListener) { nsCOMPtr swappedListener(aListener); { - ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); + MutexAutoLock lock(mMutex); mListener.swap(swappedListener); } return NS_OK; @@ -431,7 +432,7 @@ NS_IMETHODIMP nsThreadPool::SetName(const nsACString& aName) { { - ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor()); + MutexAutoLock lock(mMutex); if (mThreads.Count()) { return NS_ERROR_NOT_AVAILABLE; } diff --git a/xpcom/threads/nsThreadPool.h b/xpcom/threads/nsThreadPool.h index 83707b53bf..b32ad191a8 100644 --- a/xpcom/threads/nsThreadPool.h +++ b/xpcom/threads/nsThreadPool.h @@ -16,6 +16,8 @@ #include "nsThreadUtils.h" #include "mozilla/Attributes.h" #include "mozilla/AlreadyAddRefed.h" +#include "mozilla/Mutex.h" +#include "mozilla/Monitor.h" class nsThreadPool final : public nsIThreadPool @@ -41,6 +43,7 @@ private: nsresult PutEvent(already_AddRefed&& aEvent); nsCOMArray mThreads; + mozilla::Mutex mMutex; nsEventQueue mEvents; uint32_t mThreadLimit; uint32_t mIdleThreadLimit;