Skip to content

Commit

Permalink
Merge pull request #103 from dokar3/dokar/improve-execute-pending-job
Browse files Browse the repository at this point in the history
Improve the calling of executePendingJob
  • Loading branch information
dokar3 committed Jul 1, 2024
2 parents f748724 + 944dfff commit 09dfacb
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.dokar.quickjs.test

import com.dokar.quickjs.binding.function
import com.dokar.quickjs.quickJs
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals

class JsPromiseTest {
@Test
fun asyncFunction() = runTest {
quickJs {
val result = evaluate<Int>(
"""
async function add(a, b) {
return a + b;
}
await add(1, 2)
""".trimIndent()
)
assertEquals(3, result)
}
}

@Test
fun promise() = runTest {
quickJs {
val result = evaluate<Int>(
"""
await new Promise((resolve) => resolve(1))
.then((result) => result + 1)
""".trimIndent()
)
assertEquals(2, result)
}
}

@Test
fun promiseDotAll() = runTest {
quickJs {
val result = evaluate<List<*>>(
"""
async function add(a, b) {
return a + b;
}
await Promise.all([add(1, 2), add(3, 4)])
""".trimIndent()
)
assertEquals(listOf(3L, 7L), result)
}
}

@Test
fun promiseWithoutAwait() = runTest {
quickJs {
val logs = mutableListOf<String>()

function<String, Unit>("log") { logs += it }

evaluate<Any?>(
"""
log("Start");
new Promise((resolve) => resolve())
.then(() => log("Then"));
log("End");
""".trimIndent()
)

assertEquals(listOf("Start", "End", "Then"), logs)
}
}
}
26 changes: 14 additions & 12 deletions quickjs/src/jniMain/kotlin/com/dokar/quickjs/QuickJs.jni.kt
Original file line number Diff line number Diff line change
Expand Up @@ -352,15 +352,14 @@ actual class QuickJs private constructor(
}

private suspend fun awaitAsyncJobs() {
/**
* This is our simple 'event loop'.
*/
jsMutex.withLock {
do {
// Execute JS Promises, putting this in while(true) is unnecessary
// since we have the same loop after every asyncFunction call
val executed = executePendingJob(context, globals)
} while (executed)
}
while (true) {
jsMutex.withLock {
do {
val executed = executePendingJob(context, globals)
} while (executed)
}
val jobs = jobsMutex.withLock { asyncJobs.filter { it.isActive } }
if (jobs.isEmpty()) {
// No jobs to run
Expand Down Expand Up @@ -415,15 +414,18 @@ actual class QuickJs private constructor(
}
}
jsMutex.withLock {
while (executePendingJob(context, globals)) {
// The job is completed, see what we can do next
}
// The job is completed, see what we can do next:
// - Execute subsequent Promises
// - Cancel all jobs and fail, if rejected and JS didn't handle it
do {
val executed = executePendingJob(context, globals)
} while (executed)
}
}
jobsMutex.withLockSync { asyncJobs += job }
job.invokeOnCompletion {
jobsMutex.withLockSync { asyncJobs -= job }
}
jobsMutex.withLockSync { asyncJobs += job }
}

private fun promiseHandlesFromArgs(args: Array<Any?>): Pair<Long, Long> {
Expand Down
29 changes: 17 additions & 12 deletions quickjs/src/nativeMain/kotlin/com/dokar/quickjs/QuickJs.native.kt
Original file line number Diff line number Diff line change
Expand Up @@ -292,15 +292,18 @@ actual class QuickJs private constructor(
}
}
jsMutex.withLock {
while (executePendingJob(runtime) == ExecuteJobResult.Success) {
// The job is completed, see what we can do next
}
// The job is completed, see what we can do next:
// - Execute subsequent Promises
// - Cancel all jobs and fail, if rejected and JS didn't handle it
do {
val result = executePendingJob(runtime)
} while (result == ExecuteJobResult.Success)
}
}
jobsMutex.withLockSync { asyncJobs += job }
job.invokeOnCompletion {
jobsMutex.withLockSync { asyncJobs -= job }
}
jobsMutex.withLockSync { asyncJobs += job }
}

private suspend inline fun evalAndAwait(
Expand All @@ -324,15 +327,17 @@ actual class QuickJs private constructor(
}

private suspend fun awaitAsyncJobs() {
jsMutex.withLock {
do {
// Execute JS Promises, putting this in while(true) is unnecessary
// since we have the same loop after every asyncFunction call
val execResult = executePendingJob(runtime)
if (execResult is ExecuteJobResult.Failure) {
throw execResult.error
}
} while (execResult == ExecuteJobResult.Success)
}
while (true) {
jsMutex.withLock {
do {
val execResult = executePendingJob(runtime)
if (execResult is ExecuteJobResult.Failure) {
throw execResult.error
}
} while (execResult == ExecuteJobResult.Success)
}
val jobs = jobsMutex.withLock { asyncJobs.filter { it.isActive } }
if (jobs.isEmpty()) {
// No jobs to run
Expand Down

0 comments on commit 09dfacb

Please sign in to comment.