Skip to content

Instantly share code, notes, and snippets.

@hugoferreira
Last active May 17, 2018 23:31
Show Gist options
  • Select an option

  • Save hugoferreira/e52e373cf8c34971bbca6aae1bb6df6d to your computer and use it in GitHub Desktop.

Select an option

Save hugoferreira/e52e373cf8c34971bbca6aae1bb6df6d to your computer and use it in GitHub Desktop.
Asynchronous Bounded Queue
class AsyncQueue<T> {
waitingEnqueue = new Array<() => void>()
waitingDequeue = new Array<() => void>()
enqueuePointer = 0
dequeuePointer = 0
queue = Array<T>()
maxSize = 1
async enqueue(x: T) {
if ((this.queue.length + 1) > this.maxSize || this.waitingDequeue.length > 0) {
this.dequeuePointer += 1
await new Promise(r => this.waitingDequeue.unshift(r))
this.waitingDequeue.pop()
}
this.queue.unshift(x)
if (this.enqueuePointer > 0) {
this.waitingEnqueue[this.enqueuePointer-1]()
this.enqueuePointer -= 1
}
}
async dequeue() {
if (this.queue.length == 0 || this.waitingEnqueue.length > 0) {
this.enqueuePointer += 1
await new Promise(r => this.waitingEnqueue.unshift(r))
this.waitingEnqueue.pop()
}
if (this.dequeuePointer > 0) {
this.waitingDequeue[this.dequeuePointer - 1]()
this.dequeuePointer -= 1
}
return this.queue.pop()!
}
}
class AsyncSemaphore {
private promises = Array<() => void>()
constructor(private i: number) {}
signal() {
if (this.promises.length > 0) this.promises.pop()()
this.i += 1
}
async wait() {
if (this.i == 0 || this.promises.length > 0)
await new Promise(r => this.promises.unshift(r))
this.i -= 1
}
}
class AsyncQueue<T> {
private queue = Array<T>()
private waitingEnqueue: AsyncSemaphore
private waitingDequeue: AsyncSemaphore
constructor(readonly maxSize: number) {
this.waitingEnqueue = new AsyncSemaphore(0)
this.waitingDequeue = new AsyncSemaphore(maxSize)
}
async enqueue(x: T) {
await this.waitingDequeue.wait()
this.queue.unshift(x)
this.waitingEnqueue.signal()
}
async dequeue() {
await this.waitingEnqueue.wait()
this.waitingDequeue.signal()
return this.queue.pop()!
}
}
class AsyncQueue<T> {
waitingEnqueue = new Array<() => void>()
waitingDequeue = new Array<() => void>()
enqueuePointer = 0
dequeuePointer = 0
queue = Array<T>()
maxSize = 1
trace = 0
async enqueue(x: T) {
this.trace += 1
const localTrace = this.trace
if ((this.queue.length + 1) > this.maxSize || this.waitingDequeue.length > 0) {
console.debug(`[${localTrace}] Producer Waiting`)
this.dequeuePointer += 1
await new Promise(r => this.waitingDequeue.unshift(r))
this.waitingDequeue.pop()
console.debug(`[${localTrace}] Producer Ready`)
}
this.queue.unshift(x)
console.debug(`[${localTrace}] Enqueueing ${x} Queue is now [${this.queue.join(', ')}]`)
if (this.enqueuePointer > 0) {
console.debug(`[${localTrace}] Notify Consumer`)
this.waitingEnqueue[this.enqueuePointer-1]()
this.enqueuePointer -= 1
}
}
async dequeue() {
this.trace += 1
const localTrace = this.trace
console.debug(`[${localTrace}] Queue length before pop: ${this.queue.length}`)
if (this.queue.length == 0 || this.waitingEnqueue.length > 0) {
console.debug(`[${localTrace}] Consumer Waiting`)
this.enqueuePointer += 1
await new Promise(r => this.waitingEnqueue.unshift(r))
this.waitingEnqueue.pop()
console.debug(`[${localTrace}] Consumer Ready`)
}
const x = this.queue.pop()!
console.debug(`[${localTrace}] Queue length after pop: ${this.queue.length} Popping ${x}`)
if (this.dequeuePointer > 0) {
console.debug(`[${localTrace}] Notify Producer`)
this.waitingDequeue[this.dequeuePointer - 1]()
this.dequeuePointer -= 1
}
return x
}
}
async function produce<T>(q: AsyncQueue<T>, x: T) {
await q.enqueue(x)
}
async function consume<T>(q: AsyncQueue<T>) {
return await q.dequeue()
}
(async () => {
const q = new AsyncQueue<number>()
consume(q).then(console.log)
consume(q).then(console.log)
produce(q, 3)
produce(q, 4)
consume(q).then(console.log)
consume(q).then(console.log)
produce(q, 5)
produce(q, 6)
produce(q, 7)
produce(q, 8)
consume(q).then(console.log)
consume(q).then(console.log)
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment