whatwg / fetch

Fetch Standard
https://fetch.spec.whatwg.org/
Other
2.1k stars 325 forks source link

Fetch and SharedArrayBuffer #897

Open juj opened 5 years ago

juj commented 5 years ago

Multithreaded WebAssembly applications may wish to perform HTTP POST uploads from data that lives within their WebAssembly heaps. That is, they may execute

var x = new XMLHttpRequest();
x.open('POST', 'http://localhost.com/', true);
var a = new Uint8Array(new SharedArrayBuffer(10));
x.send(a);

but that fails with an error

VM231:1 Uncaught TypeError: Failed to execute 'send' on 'XMLHttpRequest': The provided ArrayBufferView value must not be shared.

as a workaround, one can perform a deep copy of the data from a shared array view to an unshared array view with

var x = new XMLHttpRequest();
x.open('POST', 'http://localhost.com/', true);
var a = new Uint8Array(new Uint8Array(new SharedArrayBuffer(10)));
x.send(a);

This works, but having to do this potentially large deep intermediate copy of all data to upload can be costly.

Several other entry points of web apis have been expanded to allow SABs as inputs. Would it make sense for xhr.send() as well?

CC @lars-t-hansen

domenic commented 5 years ago

In general I think XHR is considered legacy and new capability should not be added to it, but the same question with regard to the fetch() API might make sense.

annevk commented 5 years ago

Given that Fetch and XHR share some infrastructure it might end up affecting both, but let's discuss it in whatwg/fetch indeed. I've moved the issue.

I guess the semantics here would be that the browser does a copy and not reads the bytes lazily?

lars-t-hansen commented 5 years ago

If the semantics are that the browser does a copy, you'll probably force it to make a copy, just to be sure that racing writes that would not be observable under a copy model are not in fact observed. What have we done elsewhere where we accept shared memory?

annevk commented 5 years ago

I don't think there really is precedent within the web platform necessarily. What do other software platforms do?

annevk commented 3 years ago

There is precedent now in https://encoding.spec.whatwg.org/#dom-textdecoder-decode.

@domenic @wanderview @ricea @ddragana thoughts on adding SharedArrayBuffer support to fetch()/Request/Response? Should we also have sharedArrayBuffer() on Body?

Or would we instead require folks to do this through streams somehow?

ricea commented 3 years ago

I haven't looked at what would be required in detail, but my first instinct is that allowing reading from a SharedArrayBuffer would be relatively safe, as we always end up copying the data without otherwise inspecting it anyway. However, if some hypothetical browser wanted to do a zero-copy optimisation of uploads then some mischief might be possible.

Reading into a SharedArrayBuffer with streams it would look something like (untested):

async function toSharedArrayBuffer(response) {
  const parts = [];
  let totalLength = 0;
  for await (const buffer of response.body) {
    parts.push(buffer);
    totalLength += buffer.byteLength;
  }
  const sab = new SharedArrayBuffer(totalLength);
  const u8 = new Uint8Array(sab);
  let offset = 0;
  for (const buffer of parts) {
    u8.set(buffer, offset);
    offset += buffer.byteLength;
  }
  return sab;
}

This is certainly long and fiddly enough that it would be useful to have a built-in.