Base: Implement GenericReceiverSet
This implements GenericReceiverSet similar to IpcReceiverSet. This allows us to wa it on a group of channels.
IpcReceiverSet was allowed to use IpcReceivers of different type, i.e., IpcReceiver<Foo> and IpcReceiver<Bar> in the same select query. This changes with GenericReceiverSet to only allow one type, i.e., GenericReceiver<Foo>. As this functionality was only used in the CoreResourceThread, we changed the setup slightly for the memory reporter.
With this we also change the implementation of CoreResourceThread to now use the GenericReceiverSet.
Signed-off-by: Narfinger [email protected]
Testing: New testcases were added to GenericReceiverSet and browsing works normally.
FYI, you need to use backticks, with single quotes Foo<bar> will render as 'Foo<bar>). I edited the description for you.
:x: 1 Tests Failed:
| Tests completed | Failed | Passed | Skipped |
|---|---|---|---|
| 831 | 1 | 830 | 0 |
View the top 1 failed test(s) by shortest run time
libservo::site_data::test_clear_cookiesStack Traces | 0.277s run time
thread 'test_clear_cookies' (25738) panicked at .../servo/tests/site_data.rs:57:5: assertion `left == right` failed left: Err(EvaluationFailure(None)) right: Ok(String("")) stack backtrace: 0: __rustc::rust_begin_unwind 1: core::panicking::panic_fmt 2: core::panicking::assert_failed_inner 3: core::panicking::assert_failed 4: core::ops::function::FnOnce::call_once note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
To view more test analytics, go to the Test Analytics Dashboard 📋 Got 3 mins? Take this short survey to help us improve Test Analytics.
The UT is stuck.. I've rerun multiple times but still...
I clicked "update branch" in UI. UT finally starts to run.
Emm the same test is still failing.
TRY 3 FAIL [ 0.176s] (223/831) libservo::site_data test_clear_cookies:
stderr ───
error: XDG_RUNTIME_DIR not set in the environment.
libEGL warning: egl: failed to create dri2 screen
thread 'ResourceManager' (28112) panicked at components/net/resource_thread.rs:278:33:
assertion `left == right` failed
left: 0
right: 2
stack backtrace:
0: __rustc::rust_begin_unwind
1: core::panicking::panic_fmt
2: core::panicking::assert_failed_inner
3: core::panicking::assert_failed
4: net::resource_thread::ResourceChannelManager::start
5: profile_traits::mem::ProfilerChan::run_with_memory_reporting
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
thread 'Script#1' (28158) panicked at components/script/dom/document.rs:5565:33:
called `Result::unwrap()` on an `Err` value: Disconnected
Ok let me check out the test.
Ok it should be fixed. There was a problem with how I calculated the indices. I also adjusted the tests so that this should be caught in the future.
🔨 Triggering try run (#20290670700) for Windows
Windows UT keeps panicking. (The Windows try label does not run UT tho..)
thread 'generic_channel::generic_receiversets_tests::test_ipc_side_multiple' (8436) panicked at components\shared\base\generic_channel.rs:681:9:
assertion `left == right` failed
left: 1
right: 2
stack backtrace:
0: std::panicking::panic_handler
at /rustc/f8297e351a40c1439a467bbbb6879088047f50b3/library\std\src\panicking.rs:698
1: core::panicking::panic_fmt
at /rustc/f8297e351a40c1439a467bbbb6879088047f50b3/library\core\src\panicking.rs:75
2: core::panicking::assert_failed_inner
at /rustc/f8297e351a40c1439a467bbbb6879088047f50b3/library\core\src\panicking.rs:434
3: core::panicking::assert_failed<usize,usize>
at /rustc/f8297e351a40c1439a467bbbb6879088047f50b3/library\core\src\panicking.rs:394
4: base::generic_channel::generic_receiversets_tests::test_ipc_side_multiple
5: base::main
6: core::ops::function::FnOnce::call_once
at /rustc/f8297e351a40c1439a467bbbb6879088047f50b3/library\core\src\ops\function.rs:250
✨ Try run (#20290670700) succeeded.
Ok let me look at it on a windows machine, just to make sure that it is not another error.
Ok should be fixed now. It seems that on windows ipc-channel does not return multiple messages if there are multiple messages in queue. So I removed the test that was expecting it.
@Narfinger We are probably still cursed😅
It's very interesting that three separate cookiestore tests reported new results.
I am not quite sure how we should proceed. I don't see how these tests could be failing now with the changes. Anybody an idea?
🔨 Triggering try run (#20309559658) for Linux (WPT)
Test results for linux-wpt from try job (#20309559658):Flaky unexpected result (30)
/_mozilla/css/offset_properties_inline.html (#40543)offsetTopoffsetLeft/_mozilla/mozilla/getBoundingClientRect.html (#39668)getBoundingClientRect 1
assert_equals: expected 62 but got 60.35
/_mozilla/webxr/create_session.https.htmlcreate_session
can't access property "simulateDeviceConnection", navigator.xr.test is undefined
/_mozilla/webxr/sessionavailable.https.html/_webgl/conformance/glsl/misc/shader-with-dfdx-no-ext.frag.html/_webgl/conformance/textures/misc/texture-upload-size.html (#21770)WebGL test #45WebGL test #47WebGL test #49WebGL test #51WebGL test #53
assert_true: Texture was smaller than the expected size 2x2 expected true got false
WebGL test #55
assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
WebGL test #57
assert_true: Texture was smaller than the expected size 2x2 expected true got false
WebGL test #59
assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
WebGL test #61WebGL test #63/_webgl/conformance2/textures/misc/tex-input-validation.html (#38890)/_webgl/conformance2/wasm/readpixels-2gb-in-4gb-wasm-memory.html/css/css-fonts/generic-family-keywords-003.html (#38994)@font-face matching for quoted and unquoted ui-serif (drawing text in a canvas)/fetch/content-length/api-and-duplicate-headers.any.worker.html (#35197)fetch() and duplicate Content-Length/Content-Type headers
promise_test: Unhandled rejection with value: object "TypeError: Network error occurred"
/html/browsers/browsing-the-web/navigating-across-documents/refresh/same-document-refresh.html (#34597)Same-Document Referrer from Refresh
assert_equals: original page loads expected "http://web-platform.test:8000/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh-with-section.sub.html?url=%23section" but got "http://web-platform.test:8000/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh-with-section.sub.html?url=%23section#section"
/html/semantics/embedded-content/media-elements/audio_loop_base.html (#41122)Check if audio.loop is set to true that expecting the seeking event is fired more than once/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-nav-location-assign.html (#32863)Navigating iframe loading='lazy' before it is loaded: location.assign
uncaught exception: Error: assert_equals: expected "http://web-platform.test:8000/html/semantics/embedded-content/the-iframe-element/support/blank.htm?nav" but got "http://web-platform.test:8000/html/semantics/embedded-content/the-iframe-element/support/blank.htm?src"
/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_other_frame_popup.sub.html (#39702)Sandboxed iframe can not navigate other frame's popup
Test timed out
/html/semantics/forms/form-submission-0/form-submit-iframe-then-location-navigate.html (#29634)/html/semantics/forms/form-submission-0/jsurl-form-submit.tentative.html (#36489)Verifies that form submissions scheduled inside javascript: urls take precedence over the javascript: url's return value./imagebitmap-renderingcontext/toBlob-origin-clean-offscreen.sub.html/mixed-content/tentative/autoupgrades/mixed-content-cors.https.sub.html (#41123)Cross-Origin audio should get upgraded even if CORS is set
assert_equals: Length of other host audio is correct expected 1 but got Infinity
/navigation-timing/test-navigation-type-reload.html (#33334)Reload domContentLoadedEventEnd > Original domContentLoadedEventEnd
assert_true: Reload domContentLoadedEventEnd > Original domContentLoadedEventEnd expected true got false
/png/apng/acTL-plays-one.html (#41218)/preload/prefetch-document.html (#37210)different-site document prefetch with 'as=document' should not be consumed
assert_equals: expected 2 but got 1
/preload/preload-xhr.html (#39092)Make an XHR request immediately after creating link rel=preload.
assert_equals: resources/dummy.xml?token=235ce333-ff64-41af-afdc-e7cf47f62822 expected 1 but got 0
/trusted-types/SharedWorker-setTimeout-setInterval.html/trusted-types/should-trusted-type-policy-creation-be-blocked-by-csp-001.html/trusted-types/trusted-types-reporting-for-Document-execCommand.html/wasm/webapi/empty-body.any.worker.html/webaudio/the-audio-api/the-audiobuffersourcenode-interface/ctor-audiobuffersource.html/webxr/xrBoundedReferenceSpace_updates.https.html/webxr/xrSession_sameObject.https.html/workers/Worker_ErrorEvent_bubbles_cancelable.htmStable unexpected results that are known to be intermittent (32)
/FileAPI/url/url-in-tags-revoke.window.html (#19978)Fetching a blob URL immediately before revoking it works in <script> tags./IndexedDB/idbcursor-continuePrimaryKey-exceptions.any.html (#39277)IDBCursor continuePrimaryKey() on object store cursor
assert_throws_dom: continuePrimaryKey() should throw if source is not an index function "function() {
cursor.continuePrimaryKey(2, 2);
}" threw object "TypeError: cursor.continuePrimaryKey is not a function" that is not a DOMException InvalidAccessError: property "code" is equal to undefined, expected 15
/IndexedDB/idbcursor-continuePrimaryKey-exceptions.any.worker.html (#39277)IDBCursor continuePrimaryKey() on object store cursor
assert_throws_dom: continuePrimaryKey() should throw if source is not an index function "function() {
cursor.continuePrimaryKey(2, 2);
}" threw object "TypeError: cursor.continuePrimaryKey is not a function" that is not a DOMException InvalidAccessError: property "code" is equal to undefined, expected 15
/IndexedDB/idbobjectstore_getAll.any.html (#39276)Get all values with transaction.commit()/IndexedDB/idbobjectstore_getAll.any.worker.html (#39400)Get all values with transaction.commit()/IndexedDB/idbrequest-onupgradeneeded.any.html (#38895)transaction oncomplete ordering relative to open request onsuccess/IndexedDB/idbrequest-onupgradeneeded.any.worker.html (#38971)transaction oncomplete ordering relative to open request onsuccess/IndexedDB/key-conversion-exceptions.any.html (#39305)IDBCursor continue() method with throwing/invalid keys
assert_throws_exactly: key conversion with throwing getter should rethrow function "() => {
receiver[method](key);
}" threw object "TypeError: receiver[method] is not a function" but we expected it to throw object "getter: throwing from getter"
IDBCursor update() method with throwing/invalid keys
assert_throws_exactly: throwing getter should rethrow during clone function "() => {
cursor.update(value);
}" threw object "TypeError: cursor.update is not a function" but we expected it to throw object "getter: throwing from getter"
/IndexedDB/key-conversion-exceptions.any.worker.html (#39284)IDBCursor continue() method with throwing/invalid keys
assert_throws_exactly: key conversion with throwing getter should rethrow function "() => {
receiver[method](key);
}" threw object "TypeError: receiver[method] is not a function" but we expected it to throw object "getter: throwing from getter"
IDBCursor update() method with throwing/invalid keys
assert_throws_exactly: throwing getter should rethrow during clone function "() => {
cursor.update(value);
}" threw object "TypeError: cursor.update is not a function" but we expected it to throw object "getter: throwing from getter"
/_mozilla/mozilla/sslfail.html (#10760)/_mozilla/mozilla/window_resize_event.html (#36741)Popup onresize event fires after resizeTo
Test timed out
/css/css-cascade/layer-cssom-order-reverse.html (#36094)Insert layer invalidates @font-face
assert_equals: expected "220px" but got "133px"
/css/css-cascade/layer-font-face-override.html (#35935)@font-face override update with appended sheet 1@font-face override update with appended sheet 2/fetch/api/redirect/redirect-keepalive.https.any.html (#32153)[keepalive][iframe][load] mixed content redirect; setting up/fetch/metadata/generated/css-font-face.sub.tentative.html (#34624)sec-fetch-storage-access - Not sent to non-trustworthy same-site destinationsec-fetch-storage-access - Not sent to non-trustworthy cross-site destination
promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
/html/browsers/browsing-the-web/navigating-across-documents/005.html (#27062)Link with onclick navigation and href navigation /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin.window.html (#29049)Same-origin navigation started from unload handler must be ignored/html/browsers/history/the-history-interface/traverse_the_history_2.html (#21383)Multiple history traversals, last would be aborted/html/browsers/history/the-history-interface/traverse_the_history_4.html (#21383)Multiple history traversals, last would be aborted
assert_array_equals: Pages opened during history navigation expected property 1 to be 5 but got 3 (expected array [6, 5] got [6, 3])
/html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)Area element should support autofocus
promise_test: Unhandled rejection with value: object "TypeError: can't access property "appendChild", w.document.querySelector(...) is null"
/html/semantics/embedded-content/media-elements/autoplay-default-feature-policy.https.sub.html (#41193)Default "autoplay" feature policy ["self"] allows same-origin iframes.
Test timed out
/html/semantics/embedded-content/media-elements/media_fragment_seek.html (#24114)Video should seek to time specified in media fragment syntax/html/semantics/forms/form-submission-0/multipart-formdata.window.html (#28725)multipart/form-data: Basic test (formdata event)
assert_equals: expected "\r\nContent-Disposition: form-data; name=\"basic\"\r\n\r\ntest\r\n--\r\n" but got ""
/html/semantics/forms/form-submission-0/text-plain.window.html (#28687)text/plain: Basic test (formdata event)
assert_equals: expected "basic=test\r\n" but got ""
text/plain: Basic File test (normal form)
assert_equals: expected "basic=file-test.txt\r\n" but got ""
text/plain: 0x00 in filename (normal form)/html/semantics/forms/form-submission-0/urlencoded2.window.html (#28687)application/x-www-form-urlencoded: Basic File test (normal form)
assert_equals: expected "basic=file-test.txt" but got ""
application/x-www-form-urlencoded: Basic File test (formdata event)application/x-www-form-urlencoded: 0x00 in value (formdata event)/html/semantics/scripting-1/the-script-element/execution-timing/077.html (#22139)adding several types of scripts through the DOM and removing some of them confuses scheduler
assert_array_equals: expected property 1 to be "Script #1 ran" but got "Script #3 ran" (expected array ["Script #2 ran", "Script #1 ran", "Script #3 ran", "Script #4 ran"] got ["Script #2 ran", "Script #3 ran", "Script #4 ran", "Script #1 ran"])
/html/webappapis/user-prompts/print-during-unload.html (#35944)print() during unload
assert_array_equals: expected property 1 to be "destination" but got "error: window.print is not a function" (expected array ["start", "destination"] got ["start", "error: window.print is not a function"])
/preload/preload-error.sub.html (#37177)404 (fetch): main
assert_greater_than: http://web-platform.test:8000/preload/resources/dummy.xml?pipe=status%28404%29&label=fetch should be loaded expected a number greater than 0 but got 0
CORS (fetch): main/service-workers/service-worker/fetch-event.https.html (#36234)Service Worker falls back to network in fetch event with POST form/trusted-types/trusted-types-navigation.html?06-10 (#37920)Navigate a frame via anchor with javascript:-urls in report-only mode.
Test timed out
Navigate a frame via anchor with javascript:-urls w/ default policy in report-only mode./trusted-types/trusted-types-navigation.html?26-30 (#38807)Navigate a window via form-submission with javascript:-urls in report-only mode.
Test timed out
Navigate a window via form-submission with javascript:-urls w/ default policy in report-only mode.Navigate a frame via form-submission with javascript:-urls in enforcing mode.Navigate a frame via form-submission with javascript:-urls w/ default policy in enforcing mode./webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.html (#29053)StorageKey: test 3P about:blank window opened from a 3P iframeStable unexpected results (3)
/cookiestore/cookieStore_delete_arguments.https.any.htmlcookieStore.delete with positional empty namecookieStore.delete with empty name in optionscookieStore.delete with maximum cookie name sizecookieStore.delete with a __Host- prefix should not have a domain
assert_unreached: Should have rejected: undefined Reached unreachable code
cookieStore.delete with whitespace/cookiestore/cookieStore_set_arguments.https.any.html/cookiestore/cookieStore_special_names.https.any.htmlcookieStore.set a nameless cookie cannot have __Host- prefix
assert_unreached: Should have rejected: undefined Reached unreachable code
cookieStore.set a nameless cookie cannot have __Secure- prefix
assert_unreached: Should have rejected: undefined Reached unreachable code
cookieStore.set a nameless cookie cannot have __Http- prefix
assert_unreached: Should have rejected: undefined Reached unreachable code
cookieStore.set a nameless cookie cannot have __Host-Http- prefix
assert_unreached: Should have rejected: undefined Reached unreachable code
cookieStore.set a nameless cookie cannot have __Host- prefix
assert_unreached: Should have rejected: undefined Reached unreachable code
cookieStore.set a nameless cookie cannot have __Host- prefix
assert_unreached: Should have rejected: undefined Reached unreachable code
cookieStore.set a nameless cookie cannot have __Secure- prefix
assert_unreached: Should have rejected: undefined Reached unreachable code
cookieStore.set a nameless cookie cannot have __Secure- prefix
assert_unreached: Should have rejected: undefined Reached unreachable code
cookieStore.set a nameless cookie cannot have __Http- prefix
assert_unreached: Should have rejected: undefined Reached unreachable code
cookieStore.set a nameless cookie cannot have __Http- prefix
assert_unreached: Should have rejected: undefined Reached unreachable code
⚠️ Try run (#20309559658) failed.
So I updated some cookie test which were going from timeout to error. I am not sure about the other tests that showed different values.
There's something very strange going on here. I've been stepping through a test that times out with these changes, and https://github.com/servo/servo/blob/main/components/script/dom/cookiestore.rs#L63 only appears to be called once despite multiple send operations being made from the resource thread.
Specifically, this subtest times out waiting for the get operation to resolve: https://github.com/servo/servo/blob/a926260fb07340710fe786685c7f2d89f0bf1b77/tests/wpt/tests/cookiestore/cookieStore_set_limit.https.any.js#L62
Oh wait, that's probably related to 0:02.19 pid:87629 [2025-12-19T20:15:12Z WARN script::dom::cookiestore] Error receiving a CookieStore message: Custom("EmptyName")
Ok, so here's one behaviour change that leads to the test results changing: by switching from IpcSender to GenericSender for the ResourceThreads, in single process mode we now transfer ownership of message types instead of serializing and deserializing them. https://github.com/servo/servo/blob/f106f1f0d0ff5d7fbf5a230b4f40202e020aac94/components/script/dom/cookiestore.rs#L429-L435 constructs a cookie value that gets sent across this channel, but in multiprocess mode this is serialized and then parsed during deserialization, which can lead to unexpected errors when values are used for construction that are not allowed during parsing.
I propose:
diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs
index c49f308772d..71904214f6f 100644
--- a/components/net/resource_thread.rs
+++ b/components/net/resource_thread.rs
@@ -258,7 +258,7 @@ impl ResourceChannelManager {
// Handles case where profiler thread shuts down before resource thread.
match received {
GenericSelectionResult::ChannelClosed(_) => continue,
- GenericSelectionResult::Error => log::error!("Found selection error"),
+ GenericSelectionResult::Error(error) => log::error!("Found selection error: {error}"),
GenericSelectionResult::MessageReceived(id, msg) => {
if id == reporter_id {
if let CoreResourceMsg::CollectMemoryReport(report_chan) = msg {
diff --git a/components/shared/base/generic_channel/generic_channelset.rs b/components/shared/base/generic_channel/generic_channelset.rs
index e36bf1596be..d9d489eb1cd 100644
--- a/components/shared/base/generic_channel/generic_channelset.rs
+++ b/components/shared/base/generic_channel/generic_channelset.rs
@@ -62,7 +62,7 @@ pub enum GenericSelectionResult<T> {
/// The channel has been closed for the [GenericReceiver] identified by the `u64` value.
ChannelClosed(u64),
/// An error occured decoding the message.
- Error,
+ Error(String),
}
impl<T: Serialize + for<'de> serde::Deserialize<'de>> From<IpcSelectionResult>
@@ -73,7 +73,7 @@ impl<T: Serialize + for<'de> serde::Deserialize<'de>> From<IpcSelectionResult>
IpcSelectionResult::MessageReceived(channel_id, ipc_message) => {
match ipc_message.to() {
Ok(value) => GenericSelectionResult::MessageReceived(channel_id, value),
- Err(_) => GenericSelectionResult::Error,
+ Err(error) => GenericSelectionResult::Error(error.to_string()),
}
},
IpcSelectionResult::ChannelClosed(channel_id) => {
@@ -133,8 +133,7 @@ impl<T: Serialize + for<'de> Deserialize<'de>> GenericReceiverSet<T> {
.collect()
})
.unwrap_or_else(|e| {
- log::info!("GenericError {:?}", e);
- vec![GenericSelectionResult::Error]
+ vec![GenericSelectionResult::Error(e.to_string())]
}),
GenericReceiverSetVariants::Crossbeam(receivers) => {
let mut sel = crossbeam_channel::Select::new();
This helps highlight in the logs when there's something funky going on that should be investigated.
So, this explains:
- TIMEOUT->OK (the IPC messages containing invalid cookies were previously dropped by the resource thread)
- TIMEOUT->CRASH (same, except now the invalid cookie gets set in an HTTP request and hyper doesn't like that)
- NOTRUN->FAIL/PASS (same; the tests have not had a chance to run before and now they run)
I can't reproduce the TIMEOUT->ERROR changes locally, though. I didn't see that happening in CI, either. Where did you observe those changes?