diff options
Diffstat (limited to 'common/lock.js')
-rw-r--r-- | common/lock.js | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/common/lock.js b/common/lock.js new file mode 100644 index 0000000..107287f --- /dev/null +++ b/common/lock.js @@ -0,0 +1,61 @@ +/** +* Myext lock (aka binary semaphore aka mutex) +* +* Copyright (C) 2021 Wojtek Kosior +* +* Dual-licensed under: +* - 0BSD license +* - GPLv3 or (at your option) any later version +*/ + +/* + * Javascript runs single-threaded, with an event loop. Because of that, + * explicit synchronization is often not needed. An exception is when we use + * an API function that must wait. Ajax is an example. Callback passed to ajax + * call doesn't get called immediately, but after some time. In the meantime + * some other piece of code might get to execute and modify some variables. + * Access to WebExtension local storage is another situation where this problem + * can occur. + * + * This is a solution. A lock object, that can be used to delay execution of + * some code until other code finishes its critical work. Locking is wrapped + * in a promise. + */ + +"use strict"; + +(() => { + function make_lock() { + return {free: true, queue: []}; + } + + function _lock(lock, cb) { + if (lock.free) { + lock.free = false; + setTimeout(cb); + } else { + lock.queue.push(cb); + } + } + + function lock(lock) { + return new Promise((resolve, reject) => _lock(lock, resolve)); + } + + function unlock(lock) { + if (lock.free) + throw new Exception("Attempting to release a free lock"); + + if (lock.queue.length === 0) { + lock.free = true; + } else { + let cb = lock.queue[0]; + lock.queue.splice(0, 1); + setTimeout(cb); + } + } + + window.make_lock = make_lock; + window.lock = lock; + window.unlock = unlock; +})(); |