core: writer-owned relocation
The worker no longer copies rows when an array cannot grow in place. It reserves a zeroed region, returns the staged descriptor with a commit flag, and queues every other request until the main thread (the only writer) has copied the old bytes and sent commit-rows. Writes that raced the old worker-side copy were lost; lights in the glTF viewer regularly ended up at the origin because of it. Amp-Thread-ID: https://ampcode.com/threads/T-01a0b2dd-db32-712d-99ce-12ce4a86d979 Co-authored-by: Heaust Azure <heaust.azure@gmail.com>
This commit is contained in:
+34
-14
@@ -75,23 +75,17 @@ export class YawnCore {
|
||||
|
||||
async createRows({ name, rows, stride, format }) {
|
||||
await this.ready;
|
||||
const descriptor = await this.#request("create-rows", { name, rows, stride, format });
|
||||
const array = this.#arrays.get(name)?.update(descriptor)
|
||||
?? new SharedRows(this.#buffer, descriptor);
|
||||
this.#arrays.set(name, array);
|
||||
return array;
|
||||
const result = await this.#request("create-rows", { name, rows, stride, format });
|
||||
return this.#adopt(result.rows, result.commit ? [name] : []);
|
||||
}
|
||||
|
||||
async createRowsBatch(rows) {
|
||||
await this.ready;
|
||||
if (!Array.isArray(rows) || !rows.length) throw new TypeError("ROWS");
|
||||
const descriptors = await this.#request("create-rows-batch", { rows });
|
||||
return descriptors.map(descriptor => {
|
||||
const array = this.#arrays.get(descriptor.name)?.update(descriptor)
|
||||
?? new SharedRows(this.#buffer, descriptor);
|
||||
this.#arrays.set(descriptor.name, array);
|
||||
return array;
|
||||
});
|
||||
const result = await this.#request("create-rows-batch", { rows });
|
||||
const arrays = result.rows.map(descriptor => this.#adopt(descriptor));
|
||||
this.#commit(result.commit);
|
||||
return arrays;
|
||||
}
|
||||
|
||||
async deleteRows(name) {
|
||||
@@ -103,11 +97,37 @@ export class YawnCore {
|
||||
/** Reserves `count` contiguous rows and resolves with the first row ID. */
|
||||
async allocateObject(name, count = 1) {
|
||||
await this.ready;
|
||||
const { id, rows } = await this.#request("allocate-object", { name, count });
|
||||
this.#arrays.get(name).update(rows);
|
||||
const { id, rows, commit } = await this.#request("allocate-object", { name, count });
|
||||
this.#adopt(rows, commit ? [name] : []);
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes a descriptor the worker returned. When the rows moved, this thread owns the copy:
|
||||
* the old bytes are copied synchronously into the new region and the worker is told to commit
|
||||
* before anything else can be requested, so no write to the array is ever lost.
|
||||
*/
|
||||
#adopt(descriptor, commit = []) {
|
||||
const current = this.#arrays.get(descriptor.name);
|
||||
if (!current) {
|
||||
const array = new SharedRows(this.#buffer, descriptor);
|
||||
this.#arrays.set(descriptor.name, array);
|
||||
return array;
|
||||
}
|
||||
if (descriptor.offset !== current.descriptor.offset) {
|
||||
const { offset, bytes } = current.descriptor;
|
||||
new Uint8Array(this.#buffer, descriptor.offset, bytes).set(new Uint8Array(this.#buffer, offset, bytes));
|
||||
}
|
||||
current.update(descriptor);
|
||||
this.#commit(commit);
|
||||
return current;
|
||||
}
|
||||
|
||||
#commit(names) {
|
||||
if (!names.length) return;
|
||||
this.#request("commit-rows", { names }).catch(error => this.#fail(error.code));
|
||||
}
|
||||
|
||||
async deleteObject(name, id) {
|
||||
await this.ready;
|
||||
return this.#request("delete-object", { name, id });
|
||||
|
||||
Reference in New Issue
Block a user