1

I would like to implement a unique ID generator backed by browser's local storage. My concern is that the ++ operator for the storage DOM object is not implemented to run atomically.

Consider:

function generateUniqueID() {
  if(!localStorage.generator) {
    localStorage.generator = 0;
  }
  return localStorage.generator++;
}

Could people please comment if this code would run without any concurrency issues and generate unique IDs across multiple browser tabs hammering this generateUniqueID function?

AlexV
  • 544
  • 9
  • 19
  • 1
    [this question](https://stackoverflow.com/questions/22001112/is-localstorage-thread-safe) seems relevant. There's no specification for `++` that says it works any differently from `localStorage.generator = localStorage.generator + 1`, so there's no locking between reading and writing. – Barmar Sep 11 '19 at 22:30
  • it uses a mutex internally just to ensure that localStorage stays consistent. – Barmar Sep 11 '19 at 22:31
  • No, `++` is indeed not atomic. It's the same as writing `localStorage.generator = 1 + Number(localStorage.generator);` – Bergi Sep 11 '19 at 23:29
  • @Barmar thank you for pointing to the other SO question, which references the mutex implementation [requirement] (https://www.w3.org/TR/webstorage/#threads). It seems that the mutex protects the entire event processing routine until the next one is taken from the event loop. This effectively gives me exclusive access to the entire localStorage for the duration of execution block. On the down side, other tabs would be frozen until this script finishes execution and returns to the browser's event loop. Please correct me if I understand it wrong. – AlexV Sep 12 '19 at 00:39
  • That does sound right. Although there's a link in that paragraph to a nonexistent location in the official spec. – Barmar Sep 12 '19 at 00:57
  • But I wasn't able to reproduce that in our application that uses localStorage. I opened two windows, and each one was able to update a localStorage variable without causing the other to hang. – Barmar Sep 12 '19 at 01:01
  • Hmm, strange, I am using chrome on macOS and can confirm that I cannot run a script on two tabs simultaneously trying to access *the same* localStorage. – AlexV Sep 12 '19 at 01:04

1 Answers1

3

According to W3C Web Storage Recommendation (see here), §4.5 "Threads":

Because of the use of the storage mutex, multiple browsing contexts will be able to access the local storage areas simultaneously in such a manner that scripts cannot detect any concurrent script execution.

Thus, the length attribute of a Storage object, and the value of the various properties of that object, cannot change while a script is executing, other than in a way that is predictable by the script itself.

Which means to me is that the mutex is placed on first access (read or write) of the script to localStorage and kept until this event loop entry execution returns. Effectively, we have exclusive access to the localStorage for the duration of the execution chain.

I would imagine, if await/async are used, it would break the execution chain, and make the code executed across multiple event loop entries, thus breaking this contract. Even though the source code block looks monolithic. Beware.

The answer to the question is: Yes, the code in question would behave correctly, providing the browser keeps to the W3C recommendation. This is despite the fact that the "test" if(!localStorage.generator) and the "increment" localStorage.generator++ are in the different lines and in most other execution environments are not guaranteed to execute atomically.

AlexV
  • 544
  • 9
  • 19
  • As it's only a recommendation, that seems to imply some need to detect when the browser doesn't follow the recommendation so code making assumptions about this doesn't break later... – Michael Jun 29 '20 at 01:55
  • 1
    @Michael, In most cases, it's a good strategy to develop assuming that the browser complies with W3C Recommendations, and if the testing reveals that there is a problem with a particular browser, make a decision on it separately. Perhaps you would find acceptable workaround, or maybe you would ban that browser all together. The browser developers should aim at making their browsers conformant to all W3C recommendations, really. – AlexV Jun 29 '20 at 03:59