Skip to content

Commit

Permalink
refactor: replace semaphore-async-await with simpler implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
faustbrian committed Aug 19, 2022
1 parent e5be76e commit 85e90bd
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 6 deletions.
4 changes: 1 addition & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions packages/trie/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@
"ethereum-cryptography": "^1.1.2",
"level": "^8.0.0",
"memory-level": "^1.0.0",
"readable-stream": "^3.6.0",
"semaphore-async-await": "^1.5.1"
"readable-stream": "^3.6.0"
},
"devDependencies": {
"0x": "^4.9.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/trie/src/trie/trie.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { RLP_EMPTY_STRING, isFalsy, isTruthy } from '@ethereumjs/util'
import { keccak256 } from 'ethereum-cryptography/keccak'
import Semaphore from 'semaphore-async-await'

import { LevelDB } from '../db'
import { verifyRangeProof } from '../proof/range'
import { ROOT_DB_KEY } from '../types'
import { bufferToNibbles, doKeysMatch, matchingNibbleLength } from '../util/nibbles'
import { TrieReadStream as ReadStream } from '../util/readStream'
import { Semaphore } from '../util/semaphore'
import { WalkController } from '../util/walkController'

import { BranchNode, ExtensionNode, LeafNode, decodeNode, decodeRawNode, isRawNode } from './node'
Expand Down
52 changes: 52 additions & 0 deletions packages/trie/src/util/semaphore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Based on https://github.com/jsoendermann/semaphore-async-await/blob/master/src/Semaphore.ts
export class Semaphore {
private permits: number
private promiseResolverQueue: Array<(v: boolean) => void> = []

/**
* Creates a semaphore.
* @param permits The number of permits, i.e. strands of execution being allowed
* to run in parallel.
* This number can be initialized with a negative integer.
*/
constructor(permits: number) {
this.permits = permits
}

/**
* Returns a promise used to wait for a permit to become available. This method should be awaited on.
* @returns A promise that gets resolved when execution is allowed to proceed.
*/
public async wait(): Promise<boolean> {
if (this.permits > 0) {
this.permits -= 1
return Promise.resolve(true)
}

// If there is no permit available, we return a promise that resolves once the semaphore gets
// signaled enough times that permits is equal to one.
return new Promise<boolean>((resolver) => this.promiseResolverQueue.push(resolver))
}

/**
* Increases the number of permits by one. If there are other functions waiting, one of them will
* continue to execute in a future iteration of the event loop.
*/
public signal(): void {
this.permits += 1

if (this.permits > 1 && this.promiseResolverQueue.length > 0) {
// eslint-disable-next-line no-console
console.warn('Semaphore.permits should never be > 0 when there is someone waiting.')
} else if (this.permits === 1 && this.promiseResolverQueue.length > 0) {
// If there is someone else waiting, immediately consume the permit that was released
// at the beginning of this function and let the waiting function resume.
this.permits -= 1

const nextResolver = this.promiseResolverQueue.shift()
if (nextResolver) {
nextResolver(true)
}
}
}
}
29 changes: 29 additions & 0 deletions packages/trie/test/util/semaphore.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Based on https://github.com/jsoendermann/semaphore-async-await/blob/master/__tests__/Semaphore.spec.ts
import * as tape from 'tape'

import { Semaphore } from '../../src/util/semaphore'

const wait = (ms: number) => new Promise((r) => setTimeout(r, ms))

tape('Semaphore', (t) => {
t.test('should lock', async (st) => {
let global = 0
const lock = new Semaphore(1)

const f = async () => {
await lock.wait()
const local = global
await wait(500)
global = local + 1
lock.signal()
}

void f()
void f()
await wait(1500)

st.equal(global, 2)
st.end()
})
t.end()
})

0 comments on commit 85e90bd

Please sign in to comment.