@mentra/crust 3.5.0-dev.1238 → 3.5.0-dev.1240
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.
|
@@ -262,9 +262,11 @@ class JSCRuntime private constructor(private val appContext: Context) {
|
|
|
262
262
|
// anyway, so this is safe.
|
|
263
263
|
val qjs = try {
|
|
264
264
|
executor.submit<QuickJs> {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
265
|
+
QuickJsNativeLifecycle.withNative {
|
|
266
|
+
QuickJs.create(Dispatchers.Unconfined).apply {
|
|
267
|
+
memoryLimit = 32L * 1024 * 1024 // 32 MB heap per miniapp
|
|
268
|
+
maxStackSize = 512L * 1024 // 512 KB stack
|
|
269
|
+
}
|
|
268
270
|
}
|
|
269
271
|
}.get()
|
|
270
272
|
} catch (e: Throwable) {
|
|
@@ -575,7 +577,7 @@ class JSCRuntime private constructor(private val appContext: Context) {
|
|
|
575
577
|
try {
|
|
576
578
|
record.executor.submit {
|
|
577
579
|
try {
|
|
578
|
-
record.qjs.close()
|
|
580
|
+
QuickJsNativeLifecycle.withNative { record.qjs.close() }
|
|
579
581
|
} catch (e: Throwable) {
|
|
580
582
|
Log.w(TAG, "QuickJs.close() threw for $packageName: ${e.message}")
|
|
581
583
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
package com.mentra.crust.jsc
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* quickjs-kt 1.0.15 keeps one process-wide JavaVM and an instance counter.
|
|
5
|
+
* Neither update is atomic. Two miniapps creating or closing at the same time
|
|
6
|
+
* can drop that VM while another context is still open, and the next call
|
|
7
|
+
* logs "Cannot get jni env because the vm is not cached".
|
|
8
|
+
*
|
|
9
|
+
* Every [com.dokar.quickjs.QuickJs.create] and [com.dokar.quickjs.QuickJs.close]
|
|
10
|
+
* in this process goes through [withNative]. Evaluation stays outside the lock.
|
|
11
|
+
*/
|
|
12
|
+
internal object QuickJsNativeLifecycle {
|
|
13
|
+
private val lock = Any()
|
|
14
|
+
|
|
15
|
+
fun <T> withNative(block: () -> T): T = synchronized(lock) { block() }
|
|
16
|
+
}
|
|
@@ -3,7 +3,6 @@ package com.mentra.crust.jsc
|
|
|
3
3
|
import com.dokar.quickjs.QuickJs
|
|
4
4
|
import com.dokar.quickjs.QuickJsException
|
|
5
5
|
import com.dokar.quickjs.binding.function
|
|
6
|
-
import java.util.concurrent.Executors
|
|
7
6
|
import kotlinx.coroutines.Dispatchers
|
|
8
7
|
import kotlinx.coroutines.runBlocking
|
|
9
8
|
import kotlinx.coroutines.withTimeout
|
|
@@ -16,7 +15,11 @@ import org.junit.Test
|
|
|
16
15
|
* bindings called from JS, and independent close orders.
|
|
17
16
|
*/
|
|
18
17
|
class QuickJsLifecycleTest {
|
|
19
|
-
private fun create() =
|
|
18
|
+
private fun create() = QuickJsNativeLifecycle.withNative {
|
|
19
|
+
QuickJs.create(Dispatchers.Unconfined)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private fun QuickJs.release() = QuickJsNativeLifecycle.withNative { close() }
|
|
20
23
|
|
|
21
24
|
@Test fun `closing one instance keeps another instance's callbacks working`() = runBlocking {
|
|
22
25
|
var calls = 0
|
|
@@ -24,31 +27,31 @@ class QuickJsLifecycleTest {
|
|
|
24
27
|
survivor.function("hostCallback") { calls++; 42 }
|
|
25
28
|
val other = create()
|
|
26
29
|
assertEquals(42, survivor.evaluate<Int>("hostCallback()"))
|
|
27
|
-
other.
|
|
30
|
+
other.release()
|
|
28
31
|
withTimeout(1000) { assertEquals(42, survivor.evaluate<Int>("hostCallback()")) }
|
|
29
32
|
assertEquals(2, calls)
|
|
30
33
|
repeat(20) {
|
|
31
|
-
create().
|
|
34
|
+
create().release()
|
|
32
35
|
assertEquals(42, survivor.evaluate<Int>("hostCallback()"))
|
|
33
36
|
}
|
|
34
|
-
val workers = Executors.newFixedThreadPool(4)
|
|
37
|
+
val workers = java.util.concurrent.Executors.newFixedThreadPool(4)
|
|
35
38
|
try {
|
|
36
|
-
(1..40).map { workers.submit { create().
|
|
39
|
+
(1..40).map { workers.submit { create().release() } }.forEach { it.get() }
|
|
37
40
|
assertEquals(42, survivor.evaluate<Int>("hostCallback()"))
|
|
38
41
|
} finally {
|
|
39
42
|
workers.shutdownNow()
|
|
40
43
|
}
|
|
41
|
-
survivor.
|
|
44
|
+
survivor.release()
|
|
42
45
|
val fresh = create()
|
|
43
46
|
fresh.function("hostCallback") { 7 }
|
|
44
47
|
assertEquals(7, fresh.evaluate<Int>("hostCallback()"))
|
|
45
|
-
fresh.
|
|
48
|
+
fresh.release()
|
|
46
49
|
val older = create()
|
|
47
50
|
val newer = create()
|
|
48
51
|
newer.function("hostCallback") { 9 }
|
|
49
|
-
older.
|
|
52
|
+
older.release()
|
|
50
53
|
assertEquals(9, newer.evaluate<Int>("hostCallback()"))
|
|
51
|
-
newer.
|
|
54
|
+
newer.release()
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
@Test fun `a binding exception is a catchable JS Error carrying the message`() = runBlocking {
|
|
@@ -58,7 +61,7 @@ class QuickJsLifecycleTest {
|
|
|
58
61
|
"try { __dispatch(); 'no throw' } catch (e) { (e instanceof Error) + '|' + e.message }",
|
|
59
62
|
)
|
|
60
63
|
assertEquals("true|INVALID_ARGS: iface", caught)
|
|
61
|
-
qjs.
|
|
64
|
+
qjs.release()
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
@Test fun `non-ASCII strings cross the bridge unchanged`() = runBlocking {
|
|
@@ -69,7 +72,7 @@ class QuickJsLifecycleTest {
|
|
|
69
72
|
qjs.function("hostEcho") { args -> received = args[0] as String; args[0] }
|
|
70
73
|
assertEquals(text, qjs.evaluate<String>("hostEcho(hostText())"))
|
|
71
74
|
assertEquals(text, received)
|
|
72
|
-
qjs.
|
|
75
|
+
qjs.release()
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
@Test fun `unusual thrown values fail as exceptions, not crashes`() = runBlocking {
|
|
@@ -82,6 +85,6 @@ class QuickJsLifecycleTest {
|
|
|
82
85
|
}
|
|
83
86
|
assertEquals(2, qjs.evaluate<Int>("1 + 1"))
|
|
84
87
|
}
|
|
85
|
-
qjs.
|
|
88
|
+
qjs.release()
|
|
86
89
|
}
|
|
87
90
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mentra/crust",
|
|
3
|
-
"version": "3.5.0-dev.
|
|
3
|
+
"version": "3.5.0-dev.1240",
|
|
4
4
|
"description": "Mentra Native Module",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"license": "Apache-2.0",
|
|
36
36
|
"homepage": "https://github.com/Mentra-Community/MentraOS/tree/dev/mobile/modules/crust#readme",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@mentra/jspolyfill": "3.5.0-dev.
|
|
38
|
+
"@mentra/jspolyfill": "3.5.0-dev.1240"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@expo/config-plugins": ">=8.0.0",
|