@mark1russell7/collections 0.9.6 → 0.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@mark1russell7/collections",
4
- "version": "0.9.6",
4
+ "version": "0.9.7",
5
5
  "description": "Collections with no dependencies: lists, maps, queues, a summary tree, channels, lazy operations and dependency graphs.",
6
6
  "keywords": [
7
7
  "collections",
@@ -277,8 +277,12 @@ export function readWriteLock<C extends object>(): Middleware<C> {
277
277
  "forEach",
278
278
  "getOrDefault",
279
279
  "getOrUndefined",
280
+ "descendingIterator",
280
281
  ]);
281
282
 
283
+ // The reads that give an iterator. The lock copies the elements before it releases the read (C32).
284
+ const ITERATOR_READS = new Set(["keys", "values", "entries", "descendingIterator"]);
285
+
282
286
  return new Proxy(next, {
283
287
  get(target, prop, receiver) {
284
288
  const value = Reflect.get(target, prop, receiver);
@@ -287,15 +291,28 @@ export function readWriteLock<C extends object>(): Middleware<C> {
287
291
  return value;
288
292
  }
289
293
 
290
- const isReadMethod = typeof prop === "string" && READ_METHODS.has(prop);
294
+ // A method with a symbol key, for example Symbol.iterator, stays synchronous, so `for of` works (C32).
295
+ // The loop reads at once with no lock, as a plain method of synchronized().
296
+ if (typeof prop === "symbol") {
297
+ return function (this: unknown, ...args: unknown[]) {
298
+ return (value as Function).apply(target, args);
299
+ };
300
+ }
301
+
302
+ const isReadMethod = READ_METHODS.has(prop);
303
+ const isIteratorRead = ITERATOR_READS.has(prop);
291
304
 
292
305
  return async function (this: any, ...args: any[]) {
293
306
  if (isReadMethod) {
294
307
  // Acquire read lock
295
308
  await acquireRead();
296
309
  try {
297
- const result = (value as Function).apply(target, args);
298
- return result instanceof Promise ? await result : result;
310
+ const raw = (value as Function).apply(target, args);
311
+ const result = raw instanceof Promise ? await raw : raw;
312
+ // Before C32, the iteration started after the release of the read, so a write could change the elements.
313
+ return isIteratorRead && result != null && typeof result[Symbol.iterator] === "function"
314
+ ? Array.from(result as Iterable<unknown>)[Symbol.iterator]()
315
+ : result;
299
316
  } finally {
300
317
  releaseRead();
301
318
  }