I have a situation where a Worker is sharing a SharedArrayBuffer with the main thread. In order for everything to function properly, it's crucial that the worker has access to the SAB before the main thread tries to access it. (EDIT: The code creating the worker must be in a separate function which returns an array pointing to the SAB.) (Perhaps this is not even possible, you might say).
The initial code snippet appears as follows:
function init() {
var code = `onmessage = function(event) {
console.log('starting');
var buffer=event.data;
var arr = new Uint32Array(buffer);// I need to have this done before accessing the buffer again from the main
//some other code, manipulating the array
}`
var buffer = new SharedArrayBuffer(BUFFER_ELEMENT_SIZE);
var blob = new Blob([code], { "type": 'application/javascript' });
var url = window.URL || window.webkitURL;
var blobUrl = url.createObjectURL(blob);
var counter = new Worker(blobUrl);
counter.postMessage(buffer);
let res = new Uint32Array(buffer);
return res;
}
function test (){
let array = init();
console.log('main');
//accessing the SAB again
};
The worker code always executes after test()
, displaying main
first, then starting
.
Using setTimeout does not seem to resolve the issue. Consider the following modification to test
:
function test(){
let array = [];
console.log('main');
setTimeout(function(){
array = initSAB();
},0);
setTimeout(function(){
console.log('main');
//accessing the SAB again
},0);
console.log('end');
};
Now, the console logs end
initially, followed by main
, and finally starting
.
Interestingly, assigning the buffer to a global array outside of the test() function seems to solve the problem without requiring timeouts.
Here are my questions:
- Why doesn't the worker start immediately after the message is sent or received? Given that workers have their own event queue, shouldn't they operate independently of the main stack?
- Is there a detailed specification on when a worker begins working after receiving a message?
- Is there a way to ensure the worker has initiated before accessing the SAB again without relying on global variables? Using busy waiting could be an option, but it comes with risks. Is there any alternative method available?
Edit
To clarify further:
- In a completely parallel scenario, ideally the Worker should process the message promptly after it is posted. However, this does not seem to be the case.
- Most Browser APIs, including Workers, utilize a callback queue to handle API calls. If this were true, the message would have been handled prior to the timeout callbacks being executed.
- Another observation: If I attempt busy waiting postMessage by continuously checking the SAB until it changes value, the program will become stuck indefinitely. This implies that the browser does not post the message until the call stack is clear. This behavior, to my knowledge, is undocumented and puzzling.
In summary: I am interested in understanding how the browser determines when to post and handle messages by the worker, particularly if the call to postMessage is within a function. Although I found a workaround using global variables, I am curious about the underlying mechanics. If someone can demonstrate a functioning example, I would greatly appreciate it.
EDIT 2:
The code utilizing a global variable (the solution that works effectively) resembles the following:
function init() {
//Unchanged
}
var array = init(); //global
function test (){
console.log('main');
//accessing the SAB again
};
This sequence prints starting
, followed by main
on the console.
Furthermore, it's worth noting that when debugging the code with Firefox (not tested on Chrome), the desired outcome is achieved even without the global variable (starting
preceding main
). Can anyone provide an explanation for this discrepancy?