Skip to content

Commit

Permalink
[WebLocks]: Modifying weblocks algos to be O(1)
Browse files Browse the repository at this point in the history
The behaviour of the request/release operations of web locks are
modified to be O(1) instead of their currently O(n) worst case runtime.
Additionally the query-order wpt is modified to reflect the new spec
requirement that the state returned by navigator.locks.query need only
respect ordering for requested locks per resource.

Bug: 913014
Change-Id: I819f8c27c995cb698a7c8b2c75ee80d32c744f07
Spec: https://wicg.github.io/web-locks/#algorithms
  • Loading branch information
andreas-butler authored and chromium-wpt-export-bot committed Jan 10, 2019
1 parent d5c621f commit 184c86f
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 112 deletions.
110 changes: 0 additions & 110 deletions web-locks/query-order.tentative.https.any.js

This file was deleted.

130 changes: 130 additions & 0 deletions web-locks/query-ordering.tentative.https.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Web Locks API: navigator.locks.query ordering</title>
<link rel=help href="https://wicg.github.io/web-locks/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/helpers.js"></script>
<style>iframe { display: none; }</style>
<script>
'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(_ => {});
});
}

// Returns a promise resolved by the next message event.
function nextMessage() {
return new Promise(resolve => {
window.addEventListener('message', event => {
resolve(event.data);
}, {once: true});
});
}

// Tests the ordering constraints on the requested lock state returned by
// navigator.locks.query(). Three separate iframes are instantiated to make
// lock requests on the same resource, first in one order and then in another,
// different order. For each set of requests, it is verified that the requests
// appear in the result of navigator.locks.query() in the same order in which
// they were made.
//
// It is necessary to use separate iframes here so that the lock requests have
// distinguishable client_ids (otherwise it would not be possible to
// distinguish the requests and thus impossible to verify ordering).
promise_test(async testCase => {
const resourceName = uniqueName(testCase);

// Set up clients.
const frame1 = await iframe('resources/iframe.html');
const frame2 = await iframe('resources/iframe.html');
const frame3 = await iframe('resources/iframe.html');
testCase.add_cleanup(() => { frame1.remove(); });
testCase.add_cleanup(() => { frame2.remove(); });
testCase.add_cleanup(() => { frame3.remove(); });

// Collect the client ids.
const clientId1 =
(await postToFrameAndWait(frame1, {op: 'client_id',
name: resourceName})).client_id;
const clientId2 =
(await postToFrameAndWait(frame2, {op: 'client_id',
name: resourceName})).client_id;
const clientId3 =
(await postToFrameAndWait(frame3, {op: 'client_id',
name: resourceName})).client_id;

// Preemptively take the lock.
const firstRequestGroupReleaseFunction =
await getLockAndHoldUntilReleased(resourceName);

// Queue the first group of lock requests from the different clients. These
// will be blocked until firstRequestGroupReleaseFunction() is called.
let lockId1;
let lockId2;
const lockPromise1 =
postToFrameAndWait(frame1, {op: 'request', name: resourceName})
.then(val => {lockId1 = val.lock_id;});
const lockPromise2 =
postToFrameAndWait(frame2, {op: 'request', name: resourceName})
.then(val => {lockId2 = val.lock_id;});

// This third request will later be granted and held in order to block a
// second group of requests to test a different client ordering. It is not
// meant to be released.
postToFrameAndWait(frame3, {op: 'request', name: resourceName});

// Request and wait for the release of a separate lock to ensure all previous
// requests are processed.
const checkpointName = uniqueName(testCase, 'checkpoint');
const checkpointId = (await postToFrameAndWait(
frame3,
{op: 'request', name: checkpointName})).lock_id;
await postToFrameAndWait(frame3, {op: 'release', lock_id: checkpointId});

// Query the state and test the ordering of requested locks.
const state = await navigator.locks.query();
const relevant_pending_ids = state.pending
.filter(lock => [clientId1, clientId2, clientId3].includes(lock.clientId))
.map(lock => lock.clientId);
assert_array_equals(
[clientId1, clientId2, clientId3],
relevant_pending_ids,
'Querying the state should return requested locks in the order they were '
+ 'requested.');

// Add the second group of requests from the clients in a new order.
postToFrameAndWait(frame3, {op: 'request', name: resourceName});
postToFrameAndWait(frame1, {op: 'request', name: resourceName});
postToFrameAndWait(frame2, {op: 'request', name: resourceName});

// Release locks such that only the newly added locks are requested. This
// acts like a checkpoint for the newly queued requests.
firstRequestGroupReleaseFunction();
await lockPromise1;
await postToFrameAndWait(frame1, {op: 'release', lock_id: lockId1});
await lockPromise2;
await postToFrameAndWait(frame2, {op: 'release', lock_id: lockId2});

// Query the state and test the new ordering.
const state2 = await navigator.locks.query();
const relevant_pending_ids2 = state2.pending
.filter(lock => [clientId1, clientId2, clientId3].includes(lock.clientId))
.map(lock => lock.clientId);
assert_array_equals(
[clientId3, clientId1, clientId2],
relevant_pending_ids2,
'Querying the state should return requested locks in the order they were '
+ 'requested.');

}, 'Requests appear in state in order made.');
</script>
4 changes: 2 additions & 2 deletions web-locks/resources/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
// test case name. This is useful to avoid lock interference between
// test cases.
let res_num = 0;
self.uniqueName = testCase => {
return `${self.location.pathname}-${testCase.name}-${++res_num}`;
self.uniqueName = (testCase, prefix) => {
return `${self.location.pathname}-${prefix}-${testCase.name}-${++res_num}`;
};

// Inject an iframe showing the given url into the page, and resolve
Expand Down
9 changes: 9 additions & 0 deletions web-locks/resources/iframe.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@
held.delete(e.data.lock_id);
respond({ack: 'release', lock_id: e.data.lock_id});
break;

case 'client_id':
navigator.locks.request(e.data.name, async lock => {
const lock_state = await navigator.locks.query();
const held_lock =
lock_state.held.filter(l => l.name === lock.name)[0];
respond({ack: 'client_id', client_id: held_lock.clientId});
});
break;
}
});
</script>

0 comments on commit 184c86f

Please sign in to comment.