Skip to content

Commit

Permalink
doc
Browse files Browse the repository at this point in the history
  • Loading branch information
toyobayashi committed May 14, 2024
1 parent a2a5ffe commit 9a066c7
Showing 1 changed file with 170 additions and 0 deletions.
170 changes: 170 additions & 0 deletions packages/wasi-threads/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# @emnapi/wasi-threads

This package makes [wasi-threads proposal](https://github.com/WebAssembly/wasi-threads) based WASI modules work in Node.js and browser.

## Quick Start

`index.html`

```html
<script src="./node_modules/@tybys/wasm-util/dist/wasm-util.js"></script>
<script src="./node_modules/@emnapi/wasi-threads/dist/wasi-threads.js"></script>
<script src="./index.js"></script>
```

`index.js`

```js
(function (main) {
const ENVIRONMENT_IS_NODE =
typeof process === 'object' && process !== null &&
typeof process.versions === 'object' && process.versions !== null &&
typeof process.versions.node === 'string'

if (ENVIRONMENT_IS_NODE) {
main(require)
} else {
const nodeWasi = { WASI: globalThis.wasmUtil.WASI }
const nodeWorkerThreads = {
Worker: globalThis.Worker
}
const _require = function (request) {
if (request === 'node:wasi' || request === 'wasi') return nodeWasi
if (request === 'node:worker_threads' || request === 'worker_threads') return nodeWorkerThreads
if (request === '@emnapi/wasi-threads') return globalThis.wasiThreads
throw new Error('Can not find module: ' + request)
}
main(_require)
}
})(async function () {
const { WASI } = require('wasi')
const Worker = require('worker_threads')
const { WASIThreads } = require('@emnapi/wasi-threads')

const wasi = new WASI({
version: 'preview1'
})
const wasiThreads = new WASIThreads({
wasi,
onCreateWorker: () => {
return new Worker('./worker.js', {
execArgv: ['--experimental-wasi-unstable-preview1']
})
}
})
const memory = new WebAssembly.Memory({
initial: 16777216 / 65536,
maximum: 2147483648 / 65536,
shared: true
})
let input
const file = 'path/to/your/wasi-module.wasm'
try {
input = require('fs').readFileSync(require('path').join(__dirname, file))
} catch (err) {
const response = await fetch(file)
input = await response.arrayBuffer()
}
let { module, instance } = await WebAssembly.instantiate(input, {
env: { memory },
wasi_snapshot_preview1: wasi.wasiImport,
...wasiThreads.getImportObject()
})

if (typeof instance.exports._start === 'function') {
const { exitCode } = wasiThreads.start(instance, module, memory)
return exitCode
} else {
instance = wasiThreads.initialize(instance, module, memory)
// instance.exports.exported_wasm_function()
}
})
```

`worker.js`

```js
(function (main) {
const ENVIRONMENT_IS_NODE =
typeof process === 'object' && process !== null &&
typeof process.versions === 'object' && process.versions !== null &&
typeof process.versions.node === 'string'

if (ENVIRONMENT_IS_NODE) {
const _require = function (request) {
return require(request)
}

const _init = function () {
const nodeWorkerThreads = require('worker_threads')
const parentPort = nodeWorkerThreads.parentPort

parentPort.on('message', (data) => {
globalThis.onmessage({ data })
})

Object.assign(globalThis, {
self: globalThis,
require,
Worker: nodeWorkerThreads.Worker,
importScripts: function (f) {
(0, eval)(require('fs').readFileSync(f, 'utf8') + '//# sourceURL=' + f)
},
postMessage: function (msg) {
parentPort.postMessage(msg)
}
})
}

main(_require, _init)
} else {
importScripts('./node_modules/@tybys/wasm-util/dist/wasm-util.js')
importScripts('./node_modules/@emnapi/wasi-threads/dist/wasi-threads.js')

const nodeWasi = { WASI: globalThis.wasmUtil.WASI }
const _require = function (request) {
if (request === '@emnapi/wasi-threads') return globalThis.wasiThreads
if (request === 'node:wasi' || request === 'wasi') return nodeWasi
throw new Error('Can not find module: ' + request)
}
const _init = function () {}
main(_require, _init)
}
})(function main (require, init) {
init()

const { WASI } = require('wasi')
const { ThreadMessageHandler, WASIThreads } = require('@emnapi/wasi-threads')

const handler = new ThreadMessageHandler({
async onLoad ({ wasmModule, wasmMemory }) {
const wasi = new WASI({
version: 'preview1'
})

const wasiThreads = new WASIThreads({
wasi,
childThread: true
})

const originalInstance = await WebAssembly.instantiate(wasmModule, {
env: {
memory: wasmMemory,
},
wasi_snapshot_preview1: wasi.wasiImport,
...wasiThreads.getImportObject()
})

// must call `initialize` instead of `start` in child thread
const instance = wasiThreads.initialize(originalInstance, wasmModule, wasmMemory)

return { module: wasmModule, instance }
}
})

globalThis.onmessage = function (e) {
handler.handle(e)
// handle other messages
}
})
```

0 comments on commit 9a066c7

Please sign in to comment.