query.https.any.js (8808B)
1 // META: title=Web Locks API: navigator.locks.query method 2 // META: script=resources/helpers.js 3 4 'use strict'; 5 6 // Returns an array of the modes for the locks with matching name. 7 function modes(list, name) { 8 return list.filter(item => item.name === name).map(item => item.mode); 9 } 10 // Returns an array of the clientIds for the locks with matching name. 11 function clients(list, name) { 12 return list.filter(item => item.name === name).map(item => item.clientId); 13 } 14 15 promise_test(async t => { 16 const res = uniqueName(t); 17 18 await navigator.locks.request(res, async lock1 => { 19 // Attempt to request this again - should be blocked. 20 let lock2_acquired = false; 21 navigator.locks.request(res, lock2 => { lock2_acquired = true; }); 22 23 // Verify that it was blocked. 24 await navigator.locks.request(res, {ifAvailable: true}, async lock3 => { 25 assert_false(lock2_acquired, 'second request should be blocked'); 26 assert_equals(lock3, null, 'third request should have failed'); 27 28 const state = await navigator.locks.query(); 29 30 assert_own_property(state, 'pending', 'State has `pending` property'); 31 assert_true(Array.isArray(state.pending), 32 'State `pending` property is an array'); 33 const pending_info = state.pending[0]; 34 assert_own_property(pending_info, 'name', 35 'Pending info dictionary has `name` property'); 36 assert_own_property(pending_info, 'mode', 37 'Pending info dictionary has `mode` property'); 38 assert_own_property(pending_info, 'clientId', 39 'Pending info dictionary has `clientId` property'); 40 41 assert_own_property(state, 'held', 'State has `held` property'); 42 assert_true(Array.isArray(state.held), 43 'State `held` property is an array'); 44 const held_info = state.held[0]; 45 assert_own_property(held_info, 'name', 46 'Held info dictionary has `name` property'); 47 assert_own_property(held_info, 'mode', 48 'Held info dictionary has `mode` property'); 49 assert_own_property(held_info, 'clientId', 50 'Held info dictionary has `clientId` property'); 51 }); 52 }); 53 }, 'query() returns dictionaries with expected properties'); 54 55 56 57 promise_test(async t => { 58 const res = uniqueName(t); 59 60 await navigator.locks.request(res, async lock1 => { 61 const state = await navigator.locks.query(); 62 assert_array_equals(modes(state.held, res), ['exclusive'], 63 'Held lock should appear once'); 64 }); 65 66 await navigator.locks.request(res, {mode: 'shared'}, async lock1 => { 67 const state = await navigator.locks.query(); 68 assert_array_equals(modes(state.held, res), ['shared'], 69 'Held lock should appear once'); 70 }); 71 }, 'query() reports individual held locks'); 72 73 promise_test(async t => { 74 const res1 = uniqueName(t); 75 const res2 = uniqueName(t); 76 77 await navigator.locks.request(res1, async lock1 => { 78 await navigator.locks.request(res2, {mode: 'shared'}, async lock2 => { 79 const state = await navigator.locks.query(); 80 assert_array_equals(modes(state.held, res1), ['exclusive'], 81 'Held lock should appear once'); 82 assert_array_equals(modes(state.held, res2), ['shared'], 83 'Held lock should appear once'); 84 }); 85 }); 86 }, 'query() reports multiple held locks'); 87 88 promise_test(async t => { 89 const res = uniqueName(t); 90 91 await navigator.locks.request(res, async lock1 => { 92 // Attempt to request this again - should be blocked. 93 let lock2_acquired = false; 94 navigator.locks.request(res, lock2 => { lock2_acquired = true; }); 95 96 // Verify that it was blocked. 97 await navigator.locks.request(res, {ifAvailable: true}, async lock3 => { 98 assert_false(lock2_acquired, 'second request should be blocked'); 99 assert_equals(lock3, null, 'third request should have failed'); 100 101 const state = await navigator.locks.query(); 102 assert_array_equals(modes(state.pending, res), ['exclusive'], 103 'Pending lock should appear once'); 104 assert_array_equals(modes(state.held, res), ['exclusive'], 105 'Held lock should appear once'); 106 }); 107 }); 108 }, 'query() reports pending and held locks'); 109 110 promise_test(async t => { 111 const res = uniqueName(t); 112 113 await navigator.locks.request(res, {mode: 'shared'}, async lock1 => { 114 await navigator.locks.request(res, {mode: 'shared'}, async lock2 => { 115 const state = await navigator.locks.query(); 116 assert_array_equals(modes(state.held, res), ['shared', 'shared'], 117 'Held lock should appear twice'); 118 }); 119 }); 120 }, 'query() reports held shared locks with appropriate count'); 121 122 promise_test(async t => { 123 const res = uniqueName(t); 124 125 await navigator.locks.request(res, async lock1 => { 126 let lock2_acquired = false, lock3_acquired = false; 127 navigator.locks.request(res, {mode: 'shared'}, 128 lock2 => { lock2_acquired = true; }); 129 navigator.locks.request(res, {mode: 'shared'}, 130 lock3 => { lock3_acquired = true; }); 131 132 await navigator.locks.request(res, {ifAvailable: true}, async lock4 => { 133 assert_equals(lock4, null, 'lock should not be available'); 134 assert_false(lock2_acquired, 'second attempt should be blocked'); 135 assert_false(lock3_acquired, 'third attempt should be blocked'); 136 137 const state = await navigator.locks.query(); 138 assert_array_equals(modes(state.held, res), ['exclusive'], 139 'Held lock should appear once'); 140 141 assert_array_equals(modes(state.pending, res), ['shared', 'shared'], 142 'Pending lock should appear twice'); 143 }); 144 }); 145 }, 'query() reports pending shared locks with appropriate count'); 146 147 promise_test(async t => { 148 const res1 = uniqueName(t); 149 const res2 = uniqueName(t); 150 151 await navigator.locks.request(res1, async lock1 => { 152 await navigator.locks.request(res2, async lock2 => { 153 const state = await navigator.locks.query(); 154 155 const res1_clients = clients(state.held, res1); 156 const res2_clients = clients(state.held, res2); 157 158 assert_equals(res1_clients.length, 1, 'Each lock should have one holder'); 159 assert_equals(res2_clients.length, 1, 'Each lock should have one holder'); 160 161 assert_array_equals(res1_clients, res2_clients, 162 'Both locks should have same clientId'); 163 }); 164 }); 165 }, 'query() reports the same clientId for held locks from the same context'); 166 167 promise_test(async t => { 168 const res = uniqueName(t); 169 170 const worker = new Worker('resources/worker.js'); 171 t.add_cleanup(() => { worker.terminate(); }); 172 173 await postToWorkerAndWait( 174 worker, {op: 'request', name: res, mode: 'shared'}); 175 176 await navigator.locks.request(res, {mode: 'shared'}, async lock => { 177 const state = await navigator.locks.query(); 178 const res_clients = clients(state.held, res); 179 assert_equals(res_clients.length, 2, 'Clients should have same resource'); 180 assert_not_equals(res_clients[0], res_clients[1], 181 'Clients should have different ids'); 182 }); 183 }, 'query() reports different ids for held locks from different contexts'); 184 185 promise_test(async t => { 186 const res1 = uniqueName(t); 187 const res2 = uniqueName(t); 188 189 const worker = new Worker('resources/worker.js'); 190 t.add_cleanup(() => { worker.terminate(); }); 191 192 // Acquire 1 in the worker. 193 await postToWorkerAndWait(worker, {op: 'request', name: res1}) 194 195 // Acquire 2 here. 196 await new Promise(resolve => { 197 navigator.locks.request(res2, lock => { 198 resolve(); 199 return new Promise(() => {}); // Never released. 200 }); 201 }); 202 203 // Request 2 in the worker. 204 postToWorkerAndWait(worker, {op: 'request', name: res2}); 205 assert_true((await postToWorkerAndWait(worker, { 206 op: 'request', name: res2, ifAvailable: true 207 })).failed, 'Lock request should have failed'); 208 209 // Request 1 here. 210 navigator.locks.request( 211 res1, t.unreached_func('Lock should not be acquired')); 212 213 // Verify that we're seeing a deadlock. 214 const state = await navigator.locks.query(); 215 const res1_held_clients = clients(state.held, res1); 216 const res2_held_clients = clients(state.held, res2); 217 const res1_pending_clients = clients(state.pending, res1); 218 const res2_pending_clients = clients(state.pending, res2); 219 220 assert_equals(res1_held_clients.length, 1); 221 assert_equals(res2_held_clients.length, 1); 222 assert_equals(res1_pending_clients.length, 1); 223 assert_equals(res2_pending_clients.length, 1); 224 225 assert_equals(res1_held_clients[0], res2_pending_clients[0]); 226 assert_equals(res2_held_clients[0], res1_pending_clients[0]); 227 }, 'query() can observe a deadlock');