diff --git a/nix/csound-check/CMakeLists.txt b/nix/csound-check/CMakeLists.txt index 7a6e2502..887bde35 100644 --- a/nix/csound-check/CMakeLists.txt +++ b/nix/csound-check/CMakeLists.txt @@ -25,13 +25,17 @@ target_link_libraries(csound-check PRIVATE ${CMAKE_BINARY_DIR}/checker-core.a ${ target_link_options(csound-check PRIVATE -Wl,--strip-all,--max-memory=67108864) -# Small metadata reader and a test plugin, using the same Csound ABI as the checker. +# Browser beta41 loads these plugins into shared memory, including in the +# metadata worker. Include the TLS contract required by its plugin loader. foreach(plugin plugin-types plugin-types-fixture) - add_executable(${plugin} ${CMAKE_SOURCE_DIR}/ide-check/${plugin}.c) + add_executable(${plugin} ${CMAKE_SOURCE_DIR}/ide-check/${plugin}.c + ${CMAKE_SOURCE_DIR}/ide-check/plugin-tls.c) target_include_directories(${plugin} PRIVATE ${libcsound_public_include_dirs}) - target_compile_options(${plugin} PRIVATE -fPIC) + target_compile_options(${plugin} PRIVATE -fPIC -matomics -mbulk-memory) target_link_options(${plugin} PRIVATE -nostdlib -nostartfiles -Wl,--shared,--experimental-pic,--no-entry,--import-table,--import-memory,--import-undefined + -Wl,--shared-memory,--max-memory=1073741824 + -Wl,--export=__wasm_init_tls,--export=__tls_size,--export=__tls_align -Wl,--export=__wasm_call_ctors,--export=csoundModuleInit,--export=csoundModuleInfo -Wl,--export-if-defined=csoundModuleCreate,--strip-all) endforeach() diff --git a/nix/csound-check/plugin-tls.c b/nix/csound-check/plugin-tls.c new file mode 100644 index 00000000..28e51f03 --- /dev/null +++ b/nix/csound-check/plugin-tls.c @@ -0,0 +1,4 @@ +/* Browser beta41 rejects a zero TLS alignment, even when TLS size is zero. + * Retain one byte so LLVM emits valid alignment for these otherwise TLS-free + * metadata plugins. Each engine thread gets its own slot. */ +static _Thread_local unsigned char ide_tls_anchor __attribute__((used, retain)); diff --git a/package-lock.json b/package-lock.json index 4ae8b3ee..aa961594 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,9 +16,9 @@ "@codemirror/lint": "^6.9.7", "@codemirror/state": "^6.7.6", "@codemirror/view": "^6.43.14", - "@csound/browser": "7.0.0-beta39", - "@csound/plugins": "7.0.0-beta.0", - "@csound/wasm-bin": "7.0.0-beta29", + "@csound/browser": "7.0.0-beta41", + "@csound/plugins": "7.0.0-beta.1", + "@csound/wasm-bin": "7.0.0-beta31", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@emotion/core": "^11.0.0", @@ -2147,9 +2147,9 @@ } }, "node_modules/@csound/browser": { - "version": "7.0.0-beta39", - "resolved": "https://registry.npmjs.org/@csound/browser/-/browser-7.0.0-beta39.tgz", - "integrity": "sha512-/POVGpLfRrQ3kVjCPBp5bTnfYgMUQRCUHtrAilwSjrlRMQbQvQw3qjtcNbwTAxdY6uhppbpy7GbUAU2kl+OHug==", + "version": "7.0.0-beta41", + "resolved": "https://registry.npmjs.org/@csound/browser/-/browser-7.0.0-beta41.tgz", + "integrity": "sha512-FKZ9v3HGtqksZkM+Oc7C1mUfvCk0y+PTL/iXYiCGDYj6gHVjoms34z9wc/BqYGDM+gzuAnimV65SlSb1mEqrZg==", "license": "Apache-2.0", "dependencies": { "eventemitter3": "^5.0.4", @@ -2168,18 +2168,18 @@ } }, "node_modules/@csound/plugins": { - "version": "7.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@csound/plugins/-/plugins-7.0.0-beta.0.tgz", - "integrity": "sha512-HBTPPC7vruGvfo+chh72+mwqDGsPNEJMFHEl40UdT2F/+NUvBuSgNXuPgOD+mla1STyuPa9kvKUQkYrQ67RQow==", + "version": "7.0.0-beta.1", + "resolved": "https://registry.npmjs.org/@csound/plugins/-/plugins-7.0.0-beta.1.tgz", + "integrity": "sha512-RoPM9OEMyrgOLckOU8QjbMIxqAw4pFIOUA9HVcKdbOcZmfzyn4KuyEpgCHHyGCriZiaVmqFH3ZGP1Nk1k9TVwQ==", "license": "LGPL-2.1-or-later", "engines": { "node": ">=22.13.0" } }, "node_modules/@csound/wasm-bin": { - "version": "7.0.0-beta29", - "resolved": "https://registry.npmjs.org/@csound/wasm-bin/-/wasm-bin-7.0.0-beta29.tgz", - "integrity": "sha512-OfrrLaS+h6Br7S9XaJWK9tBxj1JgPRxEySYx2XyZjgAnt+UIn9Dm8H2qBSJUJgBWIT2KBmFCyw4fcdcwtGGyOg==", + "version": "7.0.0-beta31", + "resolved": "https://registry.npmjs.org/@csound/wasm-bin/-/wasm-bin-7.0.0-beta31.tgz", + "integrity": "sha512-CaFhe9LZ/KwPQKTnVc8L5EJXaTgGCX/1/xepLlRETIv5kdbSEnhyWcrGxltxg2EPhgDK8gmvtN85cTCq21FxrQ==", "license": "LGPL-2.1" }, "node_modules/@csstools/color-helpers": { diff --git a/package.json b/package.json index 980a4b59..30158dd1 100644 --- a/package.json +++ b/package.json @@ -51,9 +51,9 @@ "@codemirror/lint": "^6.9.7", "@codemirror/state": "^6.7.6", "@codemirror/view": "^6.43.14", - "@csound/browser": "7.0.0-beta39", - "@csound/plugins": "7.0.0-beta.0", - "@csound/wasm-bin": "7.0.0-beta29", + "@csound/browser": "7.0.0-beta41", + "@csound/plugins": "7.0.0-beta.1", + "@csound/wasm-bin": "7.0.0-beta31", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@emotion/core": "^11.0.0", diff --git a/puppeteer-tests/fixtures/plugin-check.ts b/puppeteer-tests/fixtures/plugin-check.ts index e81d5927..9479cbba 100644 --- a/puppeteer-tests/fixtures/plugin-check.ts +++ b/puppeteer-tests/fixtures/plugin-check.ts @@ -14,8 +14,8 @@ import { requestedPlugins } from "../../src/components/editor/validation/plugins import { checkWithPlugins } from "../../src/components/editor/validation/plugins/check"; import { PluginMetadataCache } from "../../src/components/editor/validation/plugins/cache"; import { probePlugins } from "../../src/components/editor/validation/plugins/client"; -import cExample from "@csound/wasm-bin/lib/plugin_example.wasm?url"; -import cppExample from "@csound/wasm-bin/lib/plugin_example_cpp.wasm?url"; +import cExample from "@csound/wasm-bin/lib/plugin_example_shared.wasm?url"; +import cppExample from "@csound/wasm-bin/lib/plugin_example_cpp_shared.wasm?url"; declare const __CSOUND_PLUGIN_TYPES_URL__: string; let reads = 0; diff --git a/puppeteer-tests/tests/plugin-check.js b/puppeteer-tests/tests/plugin-check.js index 08c450d3..5f5f79c1 100644 --- a/puppeteer-tests/tests/plugin-check.js +++ b/puppeteer-tests/tests/plugin-check.js @@ -3,6 +3,15 @@ import { test } from "node:test"; import puppeteer from "puppeteer"; import { BROWSER_SETTINGS, target, targetName } from "../utils/config.js"; +// The shared-memory C example also exports runtime and thread diagnostics. +const cExampleOpcodes = [ + "mult", + "plugin_heap_base", + "plugin_local_data_check", + "plugin_runtime_check", + "plugin_thread_check" +]; + test( "plugin object and struct types reach diagnostics and completion through the worker", { @@ -115,7 +124,7 @@ test( await page.evaluate(() => window.pluginFixture.entries().sort() ), - ["hello440", "mult"], + ["hello440", ...cExampleOpcodes], JSON.stringify( await page.evaluate(() => window.pluginFixture.diagnostics() @@ -189,9 +198,11 @@ test( ); await page.evaluate(() => window.pluginFixture.replacePlugin()); await page.waitForFunction( - () => - JSON.stringify(window.pluginFixture.entries()) === - '["mult"]' + (expected) => + JSON.stringify(window.pluginFixture.entries().sort()) === + JSON.stringify(expected), + {}, + cExampleOpcodes ); await page.waitForFunction(() => window.pluginFixture diff --git a/src/components/editor/validation/plugins/inspect.test.ts b/src/components/editor/validation/plugins/inspect.test.ts index 71c33407..5264e487 100644 --- a/src/components/editor/validation/plugins/inspect.test.ts +++ b/src/components/editor/validation/plugins/inspect.test.ts @@ -15,7 +15,9 @@ const bytes = (name: string) => readFileSync(`node_modules/@csound/wasm-bin/lib/${name}.wasm`) ); it("reads real C and C++ plugin registrations without compiling or performing an orchestra", async () => { - expect((await inspectPlugins([bytes("plugin_example")])).opcodes).toEqual( + expect( + (await inspectPlugins([bytes("plugin_example_shared")])).opcodes + ).toEqual( expect.arrayContaining([ { opname: "mult.aa", outypes: "a", intypes: "aa" }, { opname: "mult.kk", outypes: "k", intypes: "kk" }, @@ -23,7 +25,7 @@ it("reads real C and C++ plugin registrations without compiling or performing an ]) ); expect( - (await inspectPlugins([bytes("plugin_example_cpp")])).opcodes + (await inspectPlugins([bytes("plugin_example_cpp_shared")])).opcodes ).toEqual([{ opname: "hello440", outypes: "a", intypes: "" }]); await expect(inspectPlugins([new Uint8Array([1, 2, 3])])).rejects.toThrow( "Invalid" @@ -38,8 +40,8 @@ it.skipIf(!existsSync(".wasm-build/csound-check.wasm"))( readFileSync(".wasm-build/csound-check.wasm") ); const plugins = await inspectPlugins([ - bytes("plugin_example"), - bytes("plugin_example_cpp") + bytes("plugin_example_shared"), + bytes("plugin_example_cpp_shared") ]); const run = (text: string, signatures = plugins) => runCheck(module, { diff --git a/src/plugins/catalog.json b/src/plugins/catalog.json index 80f98ed8..dcfcf329 100644 --- a/src/plugins/catalog.json +++ b/src/plugins/catalog.json @@ -1,6 +1,6 @@ { - "package": "7.0.0-beta.0", - "browser": "7.0.0-beta39", + "package": "7.0.0-beta.1", + "browser": "7.0.0-beta41", "groups": { "chua": "Chua", "image": "Image", @@ -8,10 +8,10 @@ "stk": "STK" }, "binaries": { - "chua": "b8069fe268f5a131fa4f7331859a691076888427060686d0166cd06b757f934b", - "image": "a9c5d9676040cb227c9b92b7cf9ed4ff9c27584fb6adb6ab5bf7b52c3ee76459", - "linearAlgebra": "ac660f9cf9bc1f228ef3259bb1f097a0ce2be43b67ad5fee0714a8edcb67aca9", - "stk": "bab14ef7c94795cd237d72861d10f400ac76411334868a9d68649bc85118e36d" + "chua": "800804b70c760a0e3d91a78a3e7dde78803bf784a21754ad0c8c79df9da141f5", + "image": "73aef13f25651ef3878a082d691b229586a3b4deca95f7f77f68efea36b07fef", + "linearAlgebra": "e6a4f2ec1e729233e61f3a2295b7f520360f53348cfb1dd810313eabb98c6da0", + "stk": "99a08e3d1dd13fe8169fc75a4b7d2c66118444162bb232ef1ffba438198589c4" }, "opcodes": [ {