1

I am using a WebAssembly based software that uses multi-threading that requires SharedArrayBuffer. It runs fine both in Chromium local/deployed, and Firefox 89 deployed, but since the best performance is under Firefox, I want to test and tune it on my machine, so I run python -m SimpleHTTPServer. In this situation, when I open 127.0.0.1:8000 or 0.0.0.0:8000 in Firefox, SharedArrayBuffer is undefined. Perhaps this is a security setting, but when using localhost, I'm really not interested in Firefox's interpretation of the situation -- this should just run. How can I make it work? Do I need a different web server, different settings?

0__
  • 66,707
  • 21
  • 171
  • 266
  • 1
    You might be missing the necessary HTTP headers: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer/Planned_changes – CherryDT Jul 13 '21 at 08:15
  • @CherryDT any idea how to set them up with simplehttpserver, or with nginx? – 0__ Jul 13 '21 at 08:20
  • I thought of an easier way, because it sounds like you don't pass the object through `postMessage` anyway - see my answer – CherryDT Jul 13 '21 at 08:29

2 Answers2

2

As you guessed correctly, it has to do with security restrictions. There have been changes in regards to the use of SharedArrayBuffer that have already been implemented in Firefox 79 and will land in Chrome shortly as well (starting with Chrome 92). (Time of writing this: July 13, 2021.)

The main purpose is to restrict the use of SharedArrayBuffers in postMessage. Any such attempt will throw an error unless certain restrictive COOP/COEP headers are set to prevent cross-origin attacks:

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

Unfortunately, without these headers, there is also no global SharedArrayBuffer constructor. Apparently, this restriction may be lifted in the future. The objects themselves still work though (only passing them through postMessage would throw), but you need a different way to instantiate them. You can use WebAssembly.Memory instead:

const memory = new WebAssembly.Memory({ initial: 10, maximum: 100, shared: true })
// memory.buffer is instanceof SharedMemoryBuffer

You could now go one step further and recover the constructor from that. Therefore, with the following code as "shim", your existing code should work as long as it doesn't try to pass the buffer through postMessage:

if (typeof SharedArrayBuffer === 'undefined') {
  const dummyMemory = new WebAssembly.Memory({ initial: 0, maximum: 0, shared: true })
  globalThis.SharedArrayBuffer = dummyMemory.buffer.constructor
}

// Now, `new SharedArrayBuffer(1024)` works again

Further reading:

CherryDT
  • 25,571
  • 5
  • 49
  • 74
0

As @CherryDT pointed out in the comment, the problem is missing headers for the local server. Searching the net, there is a blog that walks through the process of developing WebAssembly in Firefox with a python web server. Instead of python -m SimpleHTTPServer, one has to add a file ./wasm-server.py with this contents (for Python 2):

# Python 2

import SimpleHTTPServer
import SocketServer

class WasmHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def end_headers(self):        
        self.send_header("Cross-Origin-Opener-Policy", "same-origin")
        self.send_header("Cross-Origin-Embedder-Policy", "require-corp")
        SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self)


# Python 3.7.5 adds in the WebAssembly Media Type. Version 2.x doesn't
# have this so add it in.
WasmHandler.extensions_map['.wasm'] = 'application/wasm'


if __name__ == '__main__':
    PORT = 8080
    httpd = SocketServer.TCPServer(("", PORT), WasmHandler)
    print("Listening on port {}. Press Ctrl+C to stop.".format(PORT))
    httpd.serve_forever()

then it is possible to test the application at 127.0.0.1:8080

0__
  • 66,707
  • 21
  • 171
  • 266