Skip to content

Editorial: use a dedicated parallel queue for the Clients API - #1842

Draft
monica-ch wants to merge 1 commit into
w3c:mainfrom
monica-ch:clients-api-parallel-queue
Draft

Editorial: use a dedicated parallel queue for the Clients API#1842
monica-ch wants to merge 1 commit into
w3c:mainfrom
monica-ch:clients-api-parallel-queue

Conversation

@monica-ch

@monica-chmonica-ch commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Closes#1840. Follow-up to #1755.

Give each {{Clients}} object a dedicated parallel queue and route the operations on {{Clients}} through it, so operations on the same {{Clients}} object do not race with each other (e.g. matchAll iterating over service worker clients while claim is mutating their [=active service worker=]).

Changes

  • Add a <dfn export for="Clients">Clients parallel queue</dfn> (a [=parallel queue=]) attached to each {{Clients}} object, following the same pattern as the name to cache map parallel queue introduced in Editorial: serialize CacheStorage access on a dedicated parallel queue #1838.
  • Route the following method algorithms through it, replacing their existing top-level Run … in parallel block:
    • {{Clients/get(id)}}
    • {{Clients/matchAll(options)}}
    • {{Clients/openWindow(url)}}
    • {{Clients/claim()}}
  • Wrap the two remaining bare Resolve |promise| with undefined. steps (in get() and claim()) in Queue a task on |promise|'s [=responsible event loop=] using the [=DOM manipulation task source=], matching the pattern already used in matchAll() and consistent with the queue-a-task refactor from Editorial: Queue a task to resolve/reject promise or when fire an event. #1755.

Total change: +10 / −6 in index.bs, one commit.

Rationale

Yoshi flagged this concern during #1755 review:

I just wondered what happens if one of the service worker clients has been removed or gets execution ready flag during the sub step execution, and suggest to run Clients API algorithm within the dedicated parallel queue to prevent unexpected modifications to clients.

#1836 landed the queue-a-task-for-resolve fixes for these methods. This PR delivers the sibling parallel-queue work that was explicitly deferred in the split plan.

Out of scope

  • {{Client/postMessage(message, options)}} — lives on {{Client}}, not {{Clients}}, and does a single-client lookup rather than iterating the full client list. Can be a further follow-up if needed.
  • {{WindowClient/focus()}} and {{WindowClient/navigate(url)}} — already skip the in parallel block entirely and use Queue a task on the client's own event loop, so no parallel-queue treatment is meaningful for them.

Related


Preview | Diff

Comment threadindex.bs Outdated
Comment on lines +1388 to +1389
Each {{Clients}} object has an associated <dfn>Clients parallel queue</dfn> (a [=parallel queue=]) used to serialize the {{Clients}} object's operations.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recognizing this patch is a WIP, I think this is currently under-constrained and it probably makes sense to do this like you've done it for the Cache API in #1838 which is basically one-per-storage-key. (Whereas this currently seems to be 1-per-ServiceWorker instance.)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, 1-per-Clients is under-constrained. Will rework to 1-per-storage-key matching #1838.

Quick clarification before I push: #1838 hangs the queue off [=name to cache map=] because that map is itself per-storage-key. Clients don't have an equivalent map — the [=/service worker clients=] list is UA-wide, filtered by storage key. Would you prefer a UA-level <dfn>Clients parallel queue</dfn> map keyed by storage key, or a prose "for each storage key, the UA has an associated Clients parallel queue" form?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(this got cross-referenced below, but the right answer definitely is not obvious to me, so I asked in the storage spec at whatwg/storage#194)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for kicking off the discussion. Reworked per Anne's suggestion, UA now owns a map keyed by storage key, per-Clients methods enqueue via a relevant service worker clients parallel queue helper (mirrors #1838's relevant name to cache map. PTAL.

Give each storage key its own parallel queue, held by the user agent, and route Clients.get, Clients.matchAll, Clients.openWindow, and Clients.claim through it. This serializes operations across all Clients objects that share a storage key, rather than only within a single ServiceWorkerGlobalScope.
Introduces three dfns: a user-agent-owned service worker clients parallel queue map (storage key to parallel queue), a per-storage-key service worker clients parallel queue helper with lazy initialization, and a per-Clients relevant service worker clients parallel queue helper mirroring the relevant name to cache map pattern from PR 1838.
Closes: w3c#1840
@monica-ch
monica-chforce-pushed the clients-api-parallel-queue branch from 8911a7f to 73a7c0bCompareAugust 18, 2026 21:15
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use a dedicated parallel queue for the Clients API

2 participants

@monica-ch@asutherland
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Editorial: use a dedicated parallel queue for the Clients API by monica-ch · Pull Request #1842 · w3c/ServiceWorker · GitHub
Skip to content

Editorial: use a dedicated parallel queue for the Clients API - #1842

Draft
monica-ch wants to merge 1 commit into
w3c:mainfrom
monica-ch:clients-api-parallel-queue
Draft

Editorial: use a dedicated parallel queue for the Clients API#1842
monica-ch wants to merge 1 commit into
w3c:mainfrom
monica-ch:clients-api-parallel-queue

Conversation

@monica-ch

@monica-chmonica-ch commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Closes#1840. Follow-up to #1755.

Give each {{Clients}} object a dedicated parallel queue and route the operations on {{Clients}} through it, so operations on the same {{Clients}} object do not race with each other (e.g. matchAll iterating over service worker clients while claim is mutating their [=active service worker=]).

Changes

  • Add a <dfn export for="Clients">Clients parallel queue</dfn> (a [=parallel queue=]) attached to each {{Clients}} object, following the same pattern as the name to cache map parallel queue introduced in Editorial: serialize CacheStorage access on a dedicated parallel queue #1838.
  • Route the following method algorithms through it, replacing their existing top-level Run … in parallel block:
    • {{Clients/get(id)}}
    • {{Clients/matchAll(options)}}
    • {{Clients/openWindow(url)}}
    • {{Clients/claim()}}
  • Wrap the two remaining bare Resolve |promise| with undefined. steps (in get() and claim()) in Queue a task on |promise|'s [=responsible event loop=] using the [=DOM manipulation task source=], matching the pattern already used in matchAll() and consistent with the queue-a-task refactor from Editorial: Queue a task to resolve/reject promise or when fire an event. #1755.

Total change: +10 / −6 in index.bs, one commit.

Rationale

Yoshi flagged this concern during #1755 review:

I just wondered what happens if one of the service worker clients has been removed or gets execution ready flag during the sub step execution, and suggest to run Clients API algorithm within the dedicated parallel queue to prevent unexpected modifications to clients.

#1836 landed the queue-a-task-for-resolve fixes for these methods. This PR delivers the sibling parallel-queue work that was explicitly deferred in the split plan.

Out of scope

  • {{Client/postMessage(message, options)}} — lives on {{Client}}, not {{Clients}}, and does a single-client lookup rather than iterating the full client list. Can be a further follow-up if needed.
  • {{WindowClient/focus()}} and {{WindowClient/navigate(url)}} — already skip the in parallel block entirely and use Queue a task on the client's own event loop, so no parallel-queue treatment is meaningful for them.

Related


Preview | Diff

Comment threadindex.bs Outdated
Comment on lines +1388 to +1389
Each {{Clients}} object has an associated <dfn>Clients parallel queue</dfn> (a [=parallel queue=]) used to serialize the {{Clients}} object's operations.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recognizing this patch is a WIP, I think this is currently under-constrained and it probably makes sense to do this like you've done it for the Cache API in #1838 which is basically one-per-storage-key. (Whereas this currently seems to be 1-per-ServiceWorker instance.)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, 1-per-Clients is under-constrained. Will rework to 1-per-storage-key matching #1838.

Quick clarification before I push: #1838 hangs the queue off [=name to cache map=] because that map is itself per-storage-key. Clients don't have an equivalent map — the [=/service worker clients=] list is UA-wide, filtered by storage key. Would you prefer a UA-level <dfn>Clients parallel queue</dfn> map keyed by storage key, or a prose "for each storage key, the UA has an associated Clients parallel queue" form?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(this got cross-referenced below, but the right answer definitely is not obvious to me, so I asked in the storage spec at whatwg/storage#194)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for kicking off the discussion. Reworked per Anne's suggestion, UA now owns a map keyed by storage key, per-Clients methods enqueue via a relevant service worker clients parallel queue helper (mirrors #1838's relevant name to cache map. PTAL.

Give each storage key its own parallel queue, held by the user agent, and route Clients.get, Clients.matchAll, Clients.openWindow, and Clients.claim through it. This serializes operations across all Clients objects that share a storage key, rather than only within a single ServiceWorkerGlobalScope.
Introduces three dfns: a user-agent-owned service worker clients parallel queue map (storage key to parallel queue), a per-storage-key service worker clients parallel queue helper with lazy initialization, and a per-Clients relevant service worker clients parallel queue helper mirroring the relevant name to cache map pattern from PR 1838.
Closes: w3c#1840
@monica-ch
monica-chforce-pushed the clients-api-parallel-queue branch from 8911a7f to 73a7c0bCompareAugust 18, 2026 21:15
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use a dedicated parallel queue for the Clients API

2 participants

@monica-ch@asutherland
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Editorial: use a dedicated parallel queue for the Clients API by monica-ch · Pull Request #1842 · w3c/ServiceWorker · GitHub
Skip to content

Editorial: use a dedicated parallel queue for the Clients API - #1842

Draft
monica-ch wants to merge 1 commit into
w3c:mainfrom
monica-ch:clients-api-parallel-queue
Draft

Editorial: use a dedicated parallel queue for the Clients API#1842
monica-ch wants to merge 1 commit into
w3c:mainfrom
monica-ch:clients-api-parallel-queue

Conversation

@monica-ch

@monica-chmonica-ch commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Closes#1840. Follow-up to #1755.

Give each {{Clients}} object a dedicated parallel queue and route the operations on {{Clients}} through it, so operations on the same {{Clients}} object do not race with each other (e.g. matchAll iterating over service worker clients while claim is mutating their [=active service worker=]).

Changes

  • Add a <dfn export for="Clients">Clients parallel queue</dfn> (a [=parallel queue=]) attached to each {{Clients}} object, following the same pattern as the name to cache map parallel queue introduced in Editorial: serialize CacheStorage access on a dedicated parallel queue #1838.
  • Route the following method algorithms through it, replacing their existing top-level Run … in parallel block:
    • {{Clients/get(id)}}
    • {{Clients/matchAll(options)}}
    • {{Clients/openWindow(url)}}
    • {{Clients/claim()}}
  • Wrap the two remaining bare Resolve |promise| with undefined. steps (in get() and claim()) in Queue a task on |promise|'s [=responsible event loop=] using the [=DOM manipulation task source=], matching the pattern already used in matchAll() and consistent with the queue-a-task refactor from Editorial: Queue a task to resolve/reject promise or when fire an event. #1755.

Total change: +10 / −6 in index.bs, one commit.

Rationale

Yoshi flagged this concern during #1755 review:

I just wondered what happens if one of the service worker clients has been removed or gets execution ready flag during the sub step execution, and suggest to run Clients API algorithm within the dedicated parallel queue to prevent unexpected modifications to clients.

#1836 landed the queue-a-task-for-resolve fixes for these methods. This PR delivers the sibling parallel-queue work that was explicitly deferred in the split plan.

Out of scope

  • {{Client/postMessage(message, options)}} — lives on {{Client}}, not {{Clients}}, and does a single-client lookup rather than iterating the full client list. Can be a further follow-up if needed.
  • {{WindowClient/focus()}} and {{WindowClient/navigate(url)}} — already skip the in parallel block entirely and use Queue a task on the client's own event loop, so no parallel-queue treatment is meaningful for them.

Related


Preview | Diff

Comment threadindex.bs Outdated
Comment on lines +1388 to +1389
Each {{Clients}} object has an associated <dfn>Clients parallel queue</dfn> (a [=parallel queue=]) used to serialize the {{Clients}} object's operations.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recognizing this patch is a WIP, I think this is currently under-constrained and it probably makes sense to do this like you've done it for the Cache API in #1838 which is basically one-per-storage-key. (Whereas this currently seems to be 1-per-ServiceWorker instance.)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, 1-per-Clients is under-constrained. Will rework to 1-per-storage-key matching #1838.

Quick clarification before I push: #1838 hangs the queue off [=name to cache map=] because that map is itself per-storage-key. Clients don't have an equivalent map — the [=/service worker clients=] list is UA-wide, filtered by storage key. Would you prefer a UA-level <dfn>Clients parallel queue</dfn> map keyed by storage key, or a prose "for each storage key, the UA has an associated Clients parallel queue" form?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(this got cross-referenced below, but the right answer definitely is not obvious to me, so I asked in the storage spec at whatwg/storage#194)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for kicking off the discussion. Reworked per Anne's suggestion, UA now owns a map keyed by storage key, per-Clients methods enqueue via a relevant service worker clients parallel queue helper (mirrors #1838's relevant name to cache map. PTAL.

Give each storage key its own parallel queue, held by the user agent, and route Clients.get, Clients.matchAll, Clients.openWindow, and Clients.claim through it. This serializes operations across all Clients objects that share a storage key, rather than only within a single ServiceWorkerGlobalScope.
Introduces three dfns: a user-agent-owned service worker clients parallel queue map (storage key to parallel queue), a per-storage-key service worker clients parallel queue helper with lazy initialization, and a per-Clients relevant service worker clients parallel queue helper mirroring the relevant name to cache map pattern from PR 1838.
Closes: w3c#1840
@monica-ch
monica-chforce-pushed the clients-api-parallel-queue branch from 8911a7f to 73a7c0bCompareAugust 18, 2026 21:15
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use a dedicated parallel queue for the Clients API

2 participants

@monica-ch@asutherland
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Editorial: use a dedicated parallel queue for the Clients API by monica-ch · Pull Request #1842 · w3c/ServiceWorker · GitHub
Skip to content

Editorial: use a dedicated parallel queue for the Clients API - #1842

Draft
monica-ch wants to merge 1 commit into
w3c:mainfrom
monica-ch:clients-api-parallel-queue
Draft

Editorial: use a dedicated parallel queue for the Clients API#1842
monica-ch wants to merge 1 commit into
w3c:mainfrom
monica-ch:clients-api-parallel-queue

Conversation

@monica-ch

@monica-chmonica-ch commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Closes#1840. Follow-up to #1755.

Give each {{Clients}} object a dedicated parallel queue and route the operations on {{Clients}} through it, so operations on the same {{Clients}} object do not race with each other (e.g. matchAll iterating over service worker clients while claim is mutating their [=active service worker=]).

Changes

  • Add a <dfn export for="Clients">Clients parallel queue</dfn> (a [=parallel queue=]) attached to each {{Clients}} object, following the same pattern as the name to cache map parallel queue introduced in Editorial: serialize CacheStorage access on a dedicated parallel queue #1838.
  • Route the following method algorithms through it, replacing their existing top-level Run … in parallel block:
    • {{Clients/get(id)}}
    • {{Clients/matchAll(options)}}
    • {{Clients/openWindow(url)}}
    • {{Clients/claim()}}
  • Wrap the two remaining bare Resolve |promise| with undefined. steps (in get() and claim()) in Queue a task on |promise|'s [=responsible event loop=] using the [=DOM manipulation task source=], matching the pattern already used in matchAll() and consistent with the queue-a-task refactor from Editorial: Queue a task to resolve/reject promise or when fire an event. #1755.

Total change: +10 / −6 in index.bs, one commit.

Rationale

Yoshi flagged this concern during #1755 review:

I just wondered what happens if one of the service worker clients has been removed or gets execution ready flag during the sub step execution, and suggest to run Clients API algorithm within the dedicated parallel queue to prevent unexpected modifications to clients.

#1836 landed the queue-a-task-for-resolve fixes for these methods. This PR delivers the sibling parallel-queue work that was explicitly deferred in the split plan.

Out of scope

  • {{Client/postMessage(message, options)}} — lives on {{Client}}, not {{Clients}}, and does a single-client lookup rather than iterating the full client list. Can be a further follow-up if needed.
  • {{WindowClient/focus()}} and {{WindowClient/navigate(url)}} — already skip the in parallel block entirely and use Queue a task on the client's own event loop, so no parallel-queue treatment is meaningful for them.

Related


Preview | Diff

Comment threadindex.bs Outdated
Comment on lines +1388 to +1389
Each {{Clients}} object has an associated <dfn>Clients parallel queue</dfn> (a [=parallel queue=]) used to serialize the {{Clients}} object's operations.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recognizing this patch is a WIP, I think this is currently under-constrained and it probably makes sense to do this like you've done it for the Cache API in #1838 which is basically one-per-storage-key. (Whereas this currently seems to be 1-per-ServiceWorker instance.)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, 1-per-Clients is under-constrained. Will rework to 1-per-storage-key matching #1838.

Quick clarification before I push: #1838 hangs the queue off [=name to cache map=] because that map is itself per-storage-key. Clients don't have an equivalent map — the [=/service worker clients=] list is UA-wide, filtered by storage key. Would you prefer a UA-level <dfn>Clients parallel queue</dfn> map keyed by storage key, or a prose "for each storage key, the UA has an associated Clients parallel queue" form?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(this got cross-referenced below, but the right answer definitely is not obvious to me, so I asked in the storage spec at whatwg/storage#194)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for kicking off the discussion. Reworked per Anne's suggestion, UA now owns a map keyed by storage key, per-Clients methods enqueue via a relevant service worker clients parallel queue helper (mirrors #1838's relevant name to cache map. PTAL.

Give each storage key its own parallel queue, held by the user agent, and route Clients.get, Clients.matchAll, Clients.openWindow, and Clients.claim through it. This serializes operations across all Clients objects that share a storage key, rather than only within a single ServiceWorkerGlobalScope.
Introduces three dfns: a user-agent-owned service worker clients parallel queue map (storage key to parallel queue), a per-storage-key service worker clients parallel queue helper with lazy initialization, and a per-Clients relevant service worker clients parallel queue helper mirroring the relevant name to cache map pattern from PR 1838.
Closes: w3c#1840
@monica-ch
monica-chforce-pushed the clients-api-parallel-queue branch from 8911a7f to 73a7c0bCompareAugust 18, 2026 21:15
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use a dedicated parallel queue for the Clients API

2 participants

@monica-ch@asutherland
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Editorial: use a dedicated parallel queue for the Clients API by monica-ch · Pull Request #1842 · w3c/ServiceWorker · GitHub
Skip to content

Editorial: use a dedicated parallel queue for the Clients API - #1842

Draft
monica-ch wants to merge 1 commit into
w3c:mainfrom
monica-ch:clients-api-parallel-queue
Draft

Editorial: use a dedicated parallel queue for the Clients API#1842
monica-ch wants to merge 1 commit into
w3c:mainfrom
monica-ch:clients-api-parallel-queue

Conversation

@monica-ch

@monica-chmonica-ch commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Closes#1840. Follow-up to #1755.

Give each {{Clients}} object a dedicated parallel queue and route the operations on {{Clients}} through it, so operations on the same {{Clients}} object do not race with each other (e.g. matchAll iterating over service worker clients while claim is mutating their [=active service worker=]).

Changes

  • Add a <dfn export for="Clients">Clients parallel queue</dfn> (a [=parallel queue=]) attached to each {{Clients}} object, following the same pattern as the name to cache map parallel queue introduced in Editorial: serialize CacheStorage access on a dedicated parallel queue #1838.
  • Route the following method algorithms through it, replacing their existing top-level Run … in parallel block:
    • {{Clients/get(id)}}
    • {{Clients/matchAll(options)}}
    • {{Clients/openWindow(url)}}
    • {{Clients/claim()}}
  • Wrap the two remaining bare Resolve |promise| with undefined. steps (in get() and claim()) in Queue a task on |promise|'s [=responsible event loop=] using the [=DOM manipulation task source=], matching the pattern already used in matchAll() and consistent with the queue-a-task refactor from Editorial: Queue a task to resolve/reject promise or when fire an event. #1755.

Total change: +10 / −6 in index.bs, one commit.

Rationale

Yoshi flagged this concern during #1755 review:

I just wondered what happens if one of the service worker clients has been removed or gets execution ready flag during the sub step execution, and suggest to run Clients API algorithm within the dedicated parallel queue to prevent unexpected modifications to clients.

#1836 landed the queue-a-task-for-resolve fixes for these methods. This PR delivers the sibling parallel-queue work that was explicitly deferred in the split plan.

Out of scope

  • {{Client/postMessage(message, options)}} — lives on {{Client}}, not {{Clients}}, and does a single-client lookup rather than iterating the full client list. Can be a further follow-up if needed.
  • {{WindowClient/focus()}} and {{WindowClient/navigate(url)}} — already skip the in parallel block entirely and use Queue a task on the client's own event loop, so no parallel-queue treatment is meaningful for them.

Related


Preview | Diff

Comment threadindex.bs Outdated
Comment on lines +1388 to +1389
Each {{Clients}} object has an associated <dfn>Clients parallel queue</dfn> (a [=parallel queue=]) used to serialize the {{Clients}} object's operations.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recognizing this patch is a WIP, I think this is currently under-constrained and it probably makes sense to do this like you've done it for the Cache API in #1838 which is basically one-per-storage-key. (Whereas this currently seems to be 1-per-ServiceWorker instance.)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, 1-per-Clients is under-constrained. Will rework to 1-per-storage-key matching #1838.

Quick clarification before I push: #1838 hangs the queue off [=name to cache map=] because that map is itself per-storage-key. Clients don't have an equivalent map — the [=/service worker clients=] list is UA-wide, filtered by storage key. Would you prefer a UA-level <dfn>Clients parallel queue</dfn> map keyed by storage key, or a prose "for each storage key, the UA has an associated Clients parallel queue" form?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(this got cross-referenced below, but the right answer definitely is not obvious to me, so I asked in the storage spec at whatwg/storage#194)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for kicking off the discussion. Reworked per Anne's suggestion, UA now owns a map keyed by storage key, per-Clients methods enqueue via a relevant service worker clients parallel queue helper (mirrors #1838's relevant name to cache map. PTAL.

Give each storage key its own parallel queue, held by the user agent, and route Clients.get, Clients.matchAll, Clients.openWindow, and Clients.claim through it. This serializes operations across all Clients objects that share a storage key, rather than only within a single ServiceWorkerGlobalScope.
Introduces three dfns: a user-agent-owned service worker clients parallel queue map (storage key to parallel queue), a per-storage-key service worker clients parallel queue helper with lazy initialization, and a per-Clients relevant service worker clients parallel queue helper mirroring the relevant name to cache map pattern from PR 1838.
Closes: w3c#1840
@monica-ch
monica-chforce-pushed the clients-api-parallel-queue branch from 8911a7f to 73a7c0bCompareAugust 18, 2026 21:15
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use a dedicated parallel queue for the Clients API

2 participants

@monica-ch@asutherland
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Editorial: use a dedicated parallel queue for the Clients API by monica-ch · Pull Request #1842 · w3c/ServiceWorker · GitHub
Skip to content

Editorial: use a dedicated parallel queue for the Clients API - #1842

Draft
monica-ch wants to merge 1 commit into
w3c:mainfrom
monica-ch:clients-api-parallel-queue
Draft

Editorial: use a dedicated parallel queue for the Clients API#1842
monica-ch wants to merge 1 commit into
w3c:mainfrom
monica-ch:clients-api-parallel-queue

Conversation

@monica-ch

@monica-chmonica-ch commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Closes#1840. Follow-up to #1755.

Give each {{Clients}} object a dedicated parallel queue and route the operations on {{Clients}} through it, so operations on the same {{Clients}} object do not race with each other (e.g. matchAll iterating over service worker clients while claim is mutating their [=active service worker=]).

Changes

  • Add a <dfn export for="Clients">Clients parallel queue</dfn> (a [=parallel queue=]) attached to each {{Clients}} object, following the same pattern as the name to cache map parallel queue introduced in Editorial: serialize CacheStorage access on a dedicated parallel queue #1838.
  • Route the following method algorithms through it, replacing their existing top-level Run … in parallel block:
    • {{Clients/get(id)}}
    • {{Clients/matchAll(options)}}
    • {{Clients/openWindow(url)}}
    • {{Clients/claim()}}
  • Wrap the two remaining bare Resolve |promise| with undefined. steps (in get() and claim()) in Queue a task on |promise|'s [=responsible event loop=] using the [=DOM manipulation task source=], matching the pattern already used in matchAll() and consistent with the queue-a-task refactor from Editorial: Queue a task to resolve/reject promise or when fire an event. #1755.

Total change: +10 / −6 in index.bs, one commit.

Rationale

Yoshi flagged this concern during #1755 review:

I just wondered what happens if one of the service worker clients has been removed or gets execution ready flag during the sub step execution, and suggest to run Clients API algorithm within the dedicated parallel queue to prevent unexpected modifications to clients.

#1836 landed the queue-a-task-for-resolve fixes for these methods. This PR delivers the sibling parallel-queue work that was explicitly deferred in the split plan.

Out of scope

  • {{Client/postMessage(message, options)}} — lives on {{Client}}, not {{Clients}}, and does a single-client lookup rather than iterating the full client list. Can be a further follow-up if needed.
  • {{WindowClient/focus()}} and {{WindowClient/navigate(url)}} — already skip the in parallel block entirely and use Queue a task on the client's own event loop, so no parallel-queue treatment is meaningful for them.

Related


Preview | Diff

Comment threadindex.bs Outdated
Comment on lines +1388 to +1389
Each {{Clients}} object has an associated <dfn>Clients parallel queue</dfn> (a [=parallel queue=]) used to serialize the {{Clients}} object's operations.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recognizing this patch is a WIP, I think this is currently under-constrained and it probably makes sense to do this like you've done it for the Cache API in #1838 which is basically one-per-storage-key. (Whereas this currently seems to be 1-per-ServiceWorker instance.)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, 1-per-Clients is under-constrained. Will rework to 1-per-storage-key matching #1838.

Quick clarification before I push: #1838 hangs the queue off [=name to cache map=] because that map is itself per-storage-key. Clients don't have an equivalent map — the [=/service worker clients=] list is UA-wide, filtered by storage key. Would you prefer a UA-level <dfn>Clients parallel queue</dfn> map keyed by storage key, or a prose "for each storage key, the UA has an associated Clients parallel queue" form?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(this got cross-referenced below, but the right answer definitely is not obvious to me, so I asked in the storage spec at whatwg/storage#194)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for kicking off the discussion. Reworked per Anne's suggestion, UA now owns a map keyed by storage key, per-Clients methods enqueue via a relevant service worker clients parallel queue helper (mirrors #1838's relevant name to cache map. PTAL.

Give each storage key its own parallel queue, held by the user agent, and route Clients.get, Clients.matchAll, Clients.openWindow, and Clients.claim through it. This serializes operations across all Clients objects that share a storage key, rather than only within a single ServiceWorkerGlobalScope.
Introduces three dfns: a user-agent-owned service worker clients parallel queue map (storage key to parallel queue), a per-storage-key service worker clients parallel queue helper with lazy initialization, and a per-Clients relevant service worker clients parallel queue helper mirroring the relevant name to cache map pattern from PR 1838.
Closes: w3c#1840
@monica-ch
monica-chforce-pushed the clients-api-parallel-queue branch from 8911a7f to 73a7c0bCompareAugust 18, 2026 21:15
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use a dedicated parallel queue for the Clients API

2 participants

@monica-ch@asutherland
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Editorial: use a dedicated parallel queue for the Clients API by monica-ch · Pull Request #1842 · w3c/ServiceWorker · GitHub
Skip to content

Editorial: use a dedicated parallel queue for the Clients API - #1842

Draft
monica-ch wants to merge 1 commit into
w3c:mainfrom
monica-ch:clients-api-parallel-queue
Draft

Editorial: use a dedicated parallel queue for the Clients API#1842
monica-ch wants to merge 1 commit into
w3c:mainfrom
monica-ch:clients-api-parallel-queue

Conversation

@monica-ch

@monica-chmonica-ch commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Closes#1840. Follow-up to #1755.

Give each {{Clients}} object a dedicated parallel queue and route the operations on {{Clients}} through it, so operations on the same {{Clients}} object do not race with each other (e.g. matchAll iterating over service worker clients while claim is mutating their [=active service worker=]).

Changes

  • Add a <dfn export for="Clients">Clients parallel queue</dfn> (a [=parallel queue=]) attached to each {{Clients}} object, following the same pattern as the name to cache map parallel queue introduced in Editorial: serialize CacheStorage access on a dedicated parallel queue #1838.
  • Route the following method algorithms through it, replacing their existing top-level Run … in parallel block:
    • {{Clients/get(id)}}
    • {{Clients/matchAll(options)}}
    • {{Clients/openWindow(url)}}
    • {{Clients/claim()}}
  • Wrap the two remaining bare Resolve |promise| with undefined. steps (in get() and claim()) in Queue a task on |promise|'s [=responsible event loop=] using the [=DOM manipulation task source=], matching the pattern already used in matchAll() and consistent with the queue-a-task refactor from Editorial: Queue a task to resolve/reject promise or when fire an event. #1755.

Total change: +10 / −6 in index.bs, one commit.

Rationale

Yoshi flagged this concern during #1755 review:

I just wondered what happens if one of the service worker clients has been removed or gets execution ready flag during the sub step execution, and suggest to run Clients API algorithm within the dedicated parallel queue to prevent unexpected modifications to clients.

#1836 landed the queue-a-task-for-resolve fixes for these methods. This PR delivers the sibling parallel-queue work that was explicitly deferred in the split plan.

Out of scope

  • {{Client/postMessage(message, options)}} — lives on {{Client}}, not {{Clients}}, and does a single-client lookup rather than iterating the full client list. Can be a further follow-up if needed.
  • {{WindowClient/focus()}} and {{WindowClient/navigate(url)}} — already skip the in parallel block entirely and use Queue a task on the client's own event loop, so no parallel-queue treatment is meaningful for them.

Related


Preview | Diff

Comment threadindex.bs Outdated
Comment on lines +1388 to +1389
Each {{Clients}} object has an associated <dfn>Clients parallel queue</dfn> (a [=parallel queue=]) used to serialize the {{Clients}} object's operations.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recognizing this patch is a WIP, I think this is currently under-constrained and it probably makes sense to do this like you've done it for the Cache API in #1838 which is basically one-per-storage-key. (Whereas this currently seems to be 1-per-ServiceWorker instance.)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, 1-per-Clients is under-constrained. Will rework to 1-per-storage-key matching #1838.

Quick clarification before I push: #1838 hangs the queue off [=name to cache map=] because that map is itself per-storage-key. Clients don't have an equivalent map — the [=/service worker clients=] list is UA-wide, filtered by storage key. Would you prefer a UA-level <dfn>Clients parallel queue</dfn> map keyed by storage key, or a prose "for each storage key, the UA has an associated Clients parallel queue" form?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(this got cross-referenced below, but the right answer definitely is not obvious to me, so I asked in the storage spec at whatwg/storage#194)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for kicking off the discussion. Reworked per Anne's suggestion, UA now owns a map keyed by storage key, per-Clients methods enqueue via a relevant service worker clients parallel queue helper (mirrors #1838's relevant name to cache map. PTAL.

Give each storage key its own parallel queue, held by the user agent, and route Clients.get, Clients.matchAll, Clients.openWindow, and Clients.claim through it. This serializes operations across all Clients objects that share a storage key, rather than only within a single ServiceWorkerGlobalScope.
Introduces three dfns: a user-agent-owned service worker clients parallel queue map (storage key to parallel queue), a per-storage-key service worker clients parallel queue helper with lazy initialization, and a per-Clients relevant service worker clients parallel queue helper mirroring the relevant name to cache map pattern from PR 1838.
Closes: w3c#1840
@monica-ch
monica-chforce-pushed the clients-api-parallel-queue branch from 8911a7f to 73a7c0bCompareAugust 18, 2026 21:15
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use a dedicated parallel queue for the Clients API

2 participants

@monica-ch@asutherland
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Editorial: use a dedicated parallel queue for the Clients API by monica-ch · Pull Request #1842 · w3c/ServiceWorker · GitHub
Skip to content

Editorial: use a dedicated parallel queue for the Clients API - #1842

Draft
monica-ch wants to merge 1 commit into
w3c:mainfrom
monica-ch:clients-api-parallel-queue
Draft

Editorial: use a dedicated parallel queue for the Clients API#1842
monica-ch wants to merge 1 commit into
w3c:mainfrom
monica-ch:clients-api-parallel-queue

Conversation

@monica-ch

@monica-chmonica-ch commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Closes#1840. Follow-up to #1755.

Give each {{Clients}} object a dedicated parallel queue and route the operations on {{Clients}} through it, so operations on the same {{Clients}} object do not race with each other (e.g. matchAll iterating over service worker clients while claim is mutating their [=active service worker=]).

Changes

  • Add a <dfn export for="Clients">Clients parallel queue</dfn> (a [=parallel queue=]) attached to each {{Clients}} object, following the same pattern as the name to cache map parallel queue introduced in Editorial: serialize CacheStorage access on a dedicated parallel queue #1838.
  • Route the following method algorithms through it, replacing their existing top-level Run … in parallel block:
    • {{Clients/get(id)}}
    • {{Clients/matchAll(options)}}
    • {{Clients/openWindow(url)}}
    • {{Clients/claim()}}
  • Wrap the two remaining bare Resolve |promise| with undefined. steps (in get() and claim()) in Queue a task on |promise|'s [=responsible event loop=] using the [=DOM manipulation task source=], matching the pattern already used in matchAll() and consistent with the queue-a-task refactor from Editorial: Queue a task to resolve/reject promise or when fire an event. #1755.

Total change: +10 / −6 in index.bs, one commit.

Rationale

Yoshi flagged this concern during #1755 review:

I just wondered what happens if one of the service worker clients has been removed or gets execution ready flag during the sub step execution, and suggest to run Clients API algorithm within the dedicated parallel queue to prevent unexpected modifications to clients.

#1836 landed the queue-a-task-for-resolve fixes for these methods. This PR delivers the sibling parallel-queue work that was explicitly deferred in the split plan.

Out of scope

  • {{Client/postMessage(message, options)}} — lives on {{Client}}, not {{Clients}}, and does a single-client lookup rather than iterating the full client list. Can be a further follow-up if needed.
  • {{WindowClient/focus()}} and {{WindowClient/navigate(url)}} — already skip the in parallel block entirely and use Queue a task on the client's own event loop, so no parallel-queue treatment is meaningful for them.

Related


Preview | Diff

Comment threadindex.bs Outdated
Comment on lines +1388 to +1389
Each {{Clients}} object has an associated <dfn>Clients parallel queue</dfn> (a [=parallel queue=]) used to serialize the {{Clients}} object's operations.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recognizing this patch is a WIP, I think this is currently under-constrained and it probably makes sense to do this like you've done it for the Cache API in #1838 which is basically one-per-storage-key. (Whereas this currently seems to be 1-per-ServiceWorker instance.)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, 1-per-Clients is under-constrained. Will rework to 1-per-storage-key matching #1838.

Quick clarification before I push: #1838 hangs the queue off [=name to cache map=] because that map is itself per-storage-key. Clients don't have an equivalent map — the [=/service worker clients=] list is UA-wide, filtered by storage key. Would you prefer a UA-level <dfn>Clients parallel queue</dfn> map keyed by storage key, or a prose "for each storage key, the UA has an associated Clients parallel queue" form?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(this got cross-referenced below, but the right answer definitely is not obvious to me, so I asked in the storage spec at whatwg/storage#194)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for kicking off the discussion. Reworked per Anne's suggestion, UA now owns a map keyed by storage key, per-Clients methods enqueue via a relevant service worker clients parallel queue helper (mirrors #1838's relevant name to cache map. PTAL.

Give each storage key its own parallel queue, held by the user agent, and route Clients.get, Clients.matchAll, Clients.openWindow, and Clients.claim through it. This serializes operations across all Clients objects that share a storage key, rather than only within a single ServiceWorkerGlobalScope.
Introduces three dfns: a user-agent-owned service worker clients parallel queue map (storage key to parallel queue), a per-storage-key service worker clients parallel queue helper with lazy initialization, and a per-Clients relevant service worker clients parallel queue helper mirroring the relevant name to cache map pattern from PR 1838.
Closes: w3c#1840
@monica-ch
monica-chforce-pushed the clients-api-parallel-queue branch from 8911a7f to 73a7c0bCompareAugust 18, 2026 21:15
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use a dedicated parallel queue for the Clients API

2 participants

@monica-ch@asutherland