| // META: title=Web Locks API: navigator.locks.query ordering |
| // META: script=resources/helpers.js |
| // META: global=window,dedicatedworker,sharedworker,serviceworker |
| |
| 'use strict'; |
| |
| // Grab a lock and hold until a release function is called. Resolves |
| // to a release function. |
| function getLockAndHoldUntilReleased(name, options) { |
| let release; |
| const promise = new Promise(resolve => { release = resolve; }); |
| return new Promise(resolve => { |
| navigator.locks.request(name, options || {}, lock => { |
| resolve(release); |
| return promise; |
| }).catch(_ => {}); |
| }); |
| } |
| |
| promise_test(async t => { |
| const res1 = uniqueName(t); |
| const res2 = uniqueName(t); |
| const res3 = uniqueName(t); |
| |
| // These will never be released. |
| await Promise.all([ |
| getLockAndHoldUntilReleased(res1), |
| getLockAndHoldUntilReleased(res2), |
| getLockAndHoldUntilReleased(res3) |
| ]); |
| |
| // These requests should be blocked. |
| navigator.locks.request(res3, {mode: 'shared'}, lock => {}); |
| navigator.locks.request(res2, {mode: 'shared'}, lock => {}); |
| navigator.locks.request(res1, {mode: 'shared'}, lock => {}); |
| |
| const state = await navigator.locks.query(); |
| |
| const relevant_pending_names = state.pending.map(lock => lock.name) |
| .filter(name => [res1, res2, res3].includes(name)); |
| |
| assert_array_equals(relevant_pending_names, [res3, res2, res1], |
| 'Pending locks should appear in order.'); |
| }, 'Requests appear in state in order made'); |
| |
| promise_test(async t => { |
| const res1 = uniqueName(t); |
| const res2 = uniqueName(t); |
| const res3 = uniqueName(t); |
| |
| // These should be granted, and will be held until released. |
| const [release1, release2, release3] = await Promise.all([ |
| getLockAndHoldUntilReleased(res1), |
| getLockAndHoldUntilReleased(res2), |
| getLockAndHoldUntilReleased(res3) |
| ]); |
| |
| // These requests should be blocked. |
| const requests = [ |
| getLockAndHoldUntilReleased(res1), |
| getLockAndHoldUntilReleased(res2), |
| getLockAndHoldUntilReleased(res3) |
| ]; |
| |
| // Ensure the requests have had a chance to get queued by |
| // waiting for something else to make it through the queue. |
| await navigator.locks.request(uniqueName(t), lock => {}); |
| |
| // Now release the previous holders. |
| release2(); |
| release3(); |
| release1(); |
| |
| // Wait until the subsequent requests make it through. |
| await Promise.all(requests); |
| |
| const state = await navigator.locks.query(); |
| const relevant_held_names = state.held.map(lock => lock.name) |
| .filter(name => [res1, res2, res3].includes(name)); |
| |
| assert_array_equals(relevant_held_names, [res2, res3, res1], |
| 'Held locks should appear in granted order.'); |
| }, 'Held locks appear in state in order granted'); |
| |
| promise_test(async t => { |
| const res1 = uniqueName(t); |
| const res2 = uniqueName(t); |
| const res3 = uniqueName(t); |
| |
| // These should be granted, and will be held until stolen. |
| await Promise.all([ |
| getLockAndHoldUntilReleased(res1), |
| getLockAndHoldUntilReleased(res2), |
| getLockAndHoldUntilReleased(res3) |
| ]); |
| |
| // Steal in a different order. |
| await Promise.all([ |
| getLockAndHoldUntilReleased(res3, {steal: true}), |
| getLockAndHoldUntilReleased(res1, {steal: true}), |
| getLockAndHoldUntilReleased(res2, {steal: true}) |
| ]); |
| |
| const state = await navigator.locks.query(); |
| const relevant_held_names = state.held.map(lock => lock.name) |
| .filter(name => [res1, res2, res3].includes(name)); |
| |
| assert_array_equals(relevant_held_names, [res3, res1, res2], |
| 'Held locks should appear in granted order.'); |
| }, 'Held locks appear in state in order granted, including when stolen'); |