Best way to prevent race condition in multiple chrome.storage API calls?

  • Something is requesting a task
  • Something else pulls a list of tasks from the repository and checks for the presence of tasks there.
  • If there are tasks, it deletes one, and the smaller "list of tasks" is put back into the repository.

Between stages 2 and 3, a race condition may occur if several requests arise, and the same task will be performed twice.

Is it permissible to "lock" the "task" table when one task is "unloaded" to prevent any other queries?

Which solution has the least impact on performance, such as delayed execution, and how should it be implemented in javascript with the chrome.storage API?

Some code, for example:

function decide_response ( ) { if(script.replay_type == "reissue") { function next_task( tasks ) { var no_tasks = (tasks.length == 0); if( no_tasks ) { target_complete_responses.close_requester(); } else { var next_task = tasks.pop(); function notify_execute () { target_complete_responses.notify_requester_execute( next_task ); } setTable("tasks", tasks, notify_execute); } } getTable( "tasks", next_tasks ); ... } ... } 
+8
javascript locking race-condition google-chrome-extension
source share
1 answer

I think you can do without locking by taking advantage of the fact that javascript is single-threaded in context, even with the asynchronous chrome.storage API. Until you use chrome.storage.sync, that is - if there are or are not changes from the cloud, I think that all bets are disabled.

I would do something like this (wrote off the cuff, didn't test, didn't handle the errors):

 var getTask = (function() { // Private list of requests. var callbackQueue = []; // This function is called when chrome.storage.local.set() has // completed storing the updated task list. var tasksWritten = function(nComplete) { // Remove completed requests from the queue. callbackQueue = callbackQueue.slice(nComplete); // Handle any newly arrived requests. if (callbackQueue.length) chrome.storage.local.get('tasks', distributeTasks); }; // This function is called via chrome.storage.local.get() with the // task list. var distributeTasks = function(items) { // Invoke callbacks with tasks. var tasks = items['tasks']; for (var i = 0; i < callbackQueue.length; ++i) callbackQueue[i](tasks[i] || null); // Update and store the task list. Pass the number of requests // handled as an argument to the set() handler because the queue // length may change by the time the handler is invoked. chrome.storage.local.set( { 'tasks': tasks.slice(callbackQueue.length) }, function() { tasksWritten(callbackQueue.length); } ); }; // This is the public function task consumers call to get a new // task. The task is returned via the callback argument. return function(callback) { if (callbackQueue.push(callback) === 1) chrome.storage.local.get('tasks', distributeTasks); }; })(); 

This is where requests from consumers are stored as callbacks in a queue in local memory. When a new request arrives, the callback is added to the queue, and iff selects the task list, this is the only request in the queue. Otherwise, we can assume that the queue is already being processed (this is an implicit lock that allows only one execution chain to access the list of tasks).

When a task list is selected, tasks are distributed by request. Please note that there may be more than one request if there is more profit before the sample is completed. This code simply passes the null value to the callback if there are more requests than jobs. Instead, block requests until more tasks arrive, do not make callbacks and reload the request when adding tasks. If tasks can be dynamically performed as well as consumed, remember that race conditions must also be averted, but not shown here.

It is important to prevent the task list from being read again until the updated task list is saved. To do this, requests are not removed from the queue until the update is completed. Then we must be sure to process any requests received at this time (a short circuit to the call to chrome.storage.local.get () is possible, but I did it this way for simplicity).

This approach should be quite effective in the sense that it should minimize updates to the task list while still responding as quickly as possible. There is no explicit lock or wait. If you have task users in other contexts, configure the chrome.extension message handler, which calls the getTask () function.

+4
source share

All Articles