将 Pthread 编译为 worker 时 Emscripten 中的 `Module` 变量机制

The `Module` variable's mechanism in Emscripten while compiling Pthread to workers

在将 Pthread 编译为 Web Worker + Wasm 时,我对 Emsripten 中的 Module 变量感到困惑

有一个简单的 Pthread 代码,它只是在每个线程的共享变量 sum 中添加 1。 (simple.c附在最后。)

我们可以使用命令编译Pthread代码:

$ emcc simple.c  -s USE_PTHREADS=1  -s PTHREAD_POOL_SIZE=4 -o simple.html

Emscripten 将生成 simple.htmlsimple.jssimple.worker.jssimple.wasm

simple.worker.js中有一个片段:

// simple.worker.js
var Module = {};

// ...

Module['instantiateWasm'] = function(info, receiveInstance) {
  // Instantiate from the module posted from the main thread.
  // We can just use sync instantiation in the worker.
  var instance = new WebAssembly.Instance(Module['wasmModule'], info);
  // We don't need the module anymore; new threads will be spawned from the main thread.
  Module['wasmModule'] = null;
  receiveInstance(instance); // The second 'module' parameter is intentionally null here, we don't need to keep a ref to the Module object from here.
  return instance.exports;
};

注意它在worker中声明了var Module = {},定义了Module['instantiateWasm'].

然而,Module['instantiateWasm'] 仅由 simple.js 调用,代码片段如下所示:

//simple.js

var Module = {}

// ...
  if (Module['instantiateWasm']) {
    try {
      var exports = Module['instantiateWasm'](info, receiveInstance);
      return exports;
    } catch(e) {
      err('Module.instantiateWasm callback failed with error: ' + e);
      return false;
    }
  }
// ...

正如我们所见,simple.js 也声明了 var Module = {}

AFAIK,VAR 全局变量无法跨主线程及其工作线程访问。 我不明白为什么 simple.js 可以调用 Module['instantiateWasm'] 作为 simple.jsModulesimple.worker.jsModule 应该不是一回事。


线程代码:

// simple.c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#define NUMTHRDS 4
#define MAGNIFICATION 1e9

typedef struct
{
    int thread_id;
    double *sum;
} Arg;

pthread_t callThd[NUMTHRDS];
pthread_mutex_t mutexsum;

void *count_pi(void *arg)
{

    Arg *data = (Arg *)arg;
    int thread_id = data->thread_id;
    double *sum = data->sum;
    pthread_mutex_lock(&mutexsum);
    *sum += 1;
    pthread_mutex_unlock(&mutexsum);

    printf("Thread %d: sum=%f\n", thread_id, *sum);

    pthread_exit((void *)0);
}

int main(int argc, char *argv[])
{
    pthread_mutex_init(&mutexsum, NULL);

    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    double *sum = malloc(sizeof(*sum));
    *sum = 0;

    Arg arg[NUMTHRDS];
    for (int i = 0; i < NUMTHRDS; i++)
    {
        arg[i].thread_id = i;
        arg[i].sum = sum;
        pthread_create(&callThd[i], &attr, count_pi, (void *)&arg[i]);
    }

    pthread_attr_destroy(&attr);

    void *status;
    for (int i = 0; i < NUMTHRDS; i++)
    {
        pthread_join(callThd[i], &status);
    }

    printf("Final Sum =  %f \n", *sum);

    free(sum);

    pthread_mutex_destroy(&mutexsum);
    pthread_exit(NULL);
}

一个主程序将自己发送给worker。

// simple.js
// Ask the new worker to load up the Emscripten-compiled page. This is a heavy operation.
worker.postMessage({
  'cmd': 'load',
  // If the application main .js file was loaded from a Blob, then it is not possible
  // to access the URL of the current script that could be passed to a Web Worker so that
  // it could load up the same file. In that case, developer must either deliver the Blob
  // object in Module['mainScriptUrlOrBlob'], or a URL to it, so that pthread Workers can
  // independently load up the same main application file.
  'urlOrBlob': Module['mainScriptUrlOrBlob'] || _scriptDir,
  'wasmMemory': wasmMemory,
  'wasmModule': wasmModule,
  'DYNAMIC_BASE': DYNAMIC_BASE,
  'DYNAMICTOP_PTR': DYNAMICTOP_PTR
});

工人导入那个。

// simple.worker.js
if (typeof e.data.urlOrBlob === 'string') {
  importScripts(e.data.urlOrBlob);
} else {
  var objectUrl = URL.createObjectURL(e.data.urlOrBlob);
  importScripts(objectUrl);
  URL.revokeObjectURL(objectUrl);
}

因此Module不是共享的,而是独立初始化的。