superlock 1.3.5 → 1.4.1

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
- ![Last version](https://img.shields.io/github/tag/Kikobeats/superlock.svg?style=flat-square)
2
- [![Coverage Status](https://img.shields.io/coveralls/github/Kikobeats/superlock?style=flat-square)](https://coveralls.io/github/Kikobeats/superlock)
3
- [![NPM Status](https://img.shields.io/npm/dm/superlock.svg?style=flat-square)](https://www.npmjs.org/package/superlock)
1
+ [![Last version](https://img.shields.io/github/v/tag/Kikobeats/superlock?style=flat-square)](https://github.com/Kikobeats/superlock/releases)
2
+ [![Coverage Status](https://img.shields.io/coverallsCoverage/github/Kikobeats/superlock?style=flat-square)](https://coveralls.io/github/Kikobeats/superlock)
3
+ [![NPM Status](https://img.shields.io/npm/dm/superlock?style=flat-square)](https://www.npmjs.com/package/superlock)
4
4
 
5
5
  > A mutex/semaphore implementation made easy to use.
6
6
 
package/package.json CHANGED
@@ -2,12 +2,13 @@
2
2
  "name": "superlock",
3
3
  "description": "Minimal async mutex and semaphore for serializing critical sections in Node.js.",
4
4
  "homepage": "https://github.com/Kikobeats/superlock",
5
- "version": "1.3.5",
5
+ "version": "1.4.1",
6
6
  "types": "src/index.d.ts",
7
7
  "main": "src/index.js",
8
8
  "exports": {
9
9
  ".": "./src/index.js",
10
- "./create": "./src/create.js"
10
+ "./create": "./src/create.js",
11
+ "./fair": "./src/fair.js"
11
12
  },
12
13
  "author": {
13
14
  "email": "josefrancisco.verdu@gmail.com",
package/src/create.js CHANGED
@@ -1,45 +1,6 @@
1
1
  'use strict'
2
2
 
3
- class Node {
4
- constructor (data) {
5
- this.data = data
6
- }
7
- }
8
-
9
- class LinkedList {
10
- constructor () {
11
- this.length = 0
12
- }
13
-
14
- enqueue (data) {
15
- const node = new Node(data)
16
- node.prev = this.tail
17
- if (this.tail) this.tail.next = node
18
- else this.head = node
19
- this.tail = node
20
- this.length++
21
- return node
22
- }
23
-
24
- dequeue () {
25
- if (!this.head) return
26
- const { data } = this.head
27
- this.remove(this.head)
28
- return data
29
- }
30
-
31
- remove (node) {
32
- if (node.prev) node.prev.next = node.next
33
- else this.head = node.next
34
- if (node.next) node.next.prev = node.prev
35
- else this.tail = node.prev
36
- this.length--
37
- }
38
-
39
- size () {
40
- return this.length
41
- }
42
- }
3
+ const LinkedList = require('./linked-list')
43
4
 
44
5
  module.exports = (slots = 1) => {
45
6
  const queue = new LinkedList()
package/src/fair.js ADDED
@@ -0,0 +1,86 @@
1
+ 'use strict'
2
+
3
+ const LinkedList = require('./linked-list')
4
+
5
+ const SHARED = Symbol('shared')
6
+ const NONE = Symbol('none')
7
+
8
+ module.exports = (slots = 1) => {
9
+ const queues = new Map()
10
+ const ring = new LinkedList()
11
+ let waiting = 0
12
+ let last = NONE
13
+
14
+ const drop = entry => {
15
+ ring.remove(entry.ringNode)
16
+ queues.delete(entry.key)
17
+ }
18
+
19
+ const enqueue = (key, waiter) => {
20
+ let entry = queues.get(key)
21
+ if (!entry) {
22
+ entry = { key, queue: new LinkedList(), ringNode: ring.enqueue(key) }
23
+ queues.set(key, entry)
24
+ }
25
+ waiting++
26
+ waiter.entry = entry
27
+ waiter.node = entry.queue.enqueue(waiter)
28
+ }
29
+
30
+ const grant = () => {
31
+ if (ring.size() === 0) return
32
+ const current = queues.get(last)
33
+ const node =
34
+ current && current.ringNode.next ? current.ringNode.next : ring.head
35
+ const entry = queues.get(node.data)
36
+ const waiter = entry.queue.dequeue()
37
+ waiting--
38
+ if (entry.queue.size() === 0) drop(entry)
39
+ last = entry.key
40
+ waiter.acquire()
41
+ }
42
+
43
+ const release = () => {
44
+ ++slots
45
+ grant()
46
+ }
47
+
48
+ const acquire = (resolve, key) => {
49
+ --slots
50
+ last = key
51
+ resolve(release)
52
+ }
53
+
54
+ const lock = (signal, key) =>
55
+ new Promise(resolve => {
56
+ if (signal != null && typeof signal.addEventListener !== 'function') {
57
+ throw new TypeError('`signal` needs to be an AbortSignal.')
58
+ }
59
+ const id = key === undefined ? SHARED : key
60
+ if (signal?.aborted) return resolve(null)
61
+ if (!lock.isLocked()) return acquire(resolve, id)
62
+
63
+ const waiter = { acquire: () => acquire(resolve, id) }
64
+ enqueue(id, waiter)
65
+
66
+ if (signal != null) {
67
+ const onAbort = () => {
68
+ waiter.entry.queue.remove(waiter.node)
69
+ waiting--
70
+ if (waiter.entry.queue.size() === 0) drop(waiter.entry)
71
+ resolve(null)
72
+ }
73
+ waiter.acquire = () => {
74
+ signal.removeEventListener('abort', onAbort)
75
+ acquire(resolve, id)
76
+ }
77
+ signal.addEventListener('abort', onAbort, { once: true })
78
+ }
79
+ })
80
+
81
+ lock.isLocked = () => slots === 0
82
+
83
+ lock.awaiting = () => waiting
84
+
85
+ return lock
86
+ }
package/src/index.d.ts CHANGED
@@ -9,6 +9,15 @@ interface LockInterface extends Lock {
9
9
 
10
10
  declare function createLock(slots?: number): LockInterface;
11
11
 
12
+ declare type FairLock = (signal: AbortSignal | undefined | null, key?: unknown) => Promise<Release | null>;
13
+
14
+ interface FairLockInterface extends FairLock {
15
+ isLocked: () => boolean;
16
+ awaiting: () => number;
17
+ }
18
+
19
+ declare function createFairLock(slots?: number): FairLockInterface;
20
+
12
21
  declare type WithLock = <T>(fn: () => Promise<T>, signal?: AbortSignal) => Promise<T | undefined>;
13
22
 
14
23
  interface WithLockInterface extends WithLock {
@@ -18,4 +27,4 @@ interface WithLockInterface extends WithLock {
18
27
 
19
28
  declare function withLock(opts?: number): WithLockInterface;
20
29
 
21
- export { withLock, createLock, WithLockInterface };
30
+ export { withLock, createLock, createFairLock, WithLockInterface, FairLockInterface };
package/src/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
 
3
+ const createFairLock = require('./fair')
3
4
  const createLock = require('./create')
4
5
 
5
6
  const withLock = opts => {
@@ -21,4 +22,4 @@ const withLock = opts => {
21
22
  return withLock
22
23
  }
23
24
 
24
- module.exports = { withLock, createLock }
25
+ module.exports = { withLock, createLock, createFairLock }
@@ -0,0 +1,42 @@
1
+ 'use strict'
2
+
3
+ class Node {
4
+ constructor (data) {
5
+ this.data = data
6
+ }
7
+ }
8
+
9
+ module.exports = class LinkedList {
10
+ constructor () {
11
+ this.length = 0
12
+ }
13
+
14
+ enqueue (data) {
15
+ const node = new Node(data)
16
+ node.prev = this.tail
17
+ if (this.tail) this.tail.next = node
18
+ else this.head = node
19
+ this.tail = node
20
+ this.length++
21
+ return node
22
+ }
23
+
24
+ dequeue () {
25
+ if (!this.head) return
26
+ const { data } = this.head
27
+ this.remove(this.head)
28
+ return data
29
+ }
30
+
31
+ remove (node) {
32
+ if (node.prev) node.prev.next = node.next
33
+ else this.head = node.next
34
+ if (node.next) node.next.prev = node.prev
35
+ else this.tail = node.prev
36
+ this.length--
37
+ }
38
+
39
+ size () {
40
+ return this.length
41
+ }
42
+ }