Project

General

Profile

Download (2.3 KB) Statistics
| Branch: | Tag: | Revision:

haketilo / common / lock.js @ 6bae771d

1
/**
2
 * Myext lock (aka binary semaphore aka mutex)
3
 *
4
 * Copyright (C) 2021 Wojtek Kosior
5
 *
6
 * This code is dual-licensed under:
7
 * - Asshole license 1.0,
8
 * - GPLv3 or (at your option) any later version
9
 *
10
 * "dual-licensed" means you can choose the license you prefer.
11
 *
12
 * This code is released under a permissive license because I disapprove of
13
 * copyright and wouldn't be willing to sue a violator. Despite not putting
14
 * this code under copyleft (which is also kind of copyright), I do not want
15
 * it to be made proprietary. Hence, the permissive alternative to GPL is the
16
 * Asshole license 1.0 that allows me to call you an asshole if you use it.
17
 * This means you're legally ok regardless of how you utilize this code but if
18
 * you make it into something nonfree, you're an asshole.
19
 *
20
 * You should have received a copy of both GPLv3 and Asshole license 1.0
21
 * together with this code. If not, please see:
22
 * - https://www.gnu.org/licenses/gpl-3.0.en.html
23
 * - https://koszko.org/asshole-license.txt
24
 */
25

    
26
/*
27
 * Javascript runs single-threaded, with an event loop. Because of that,
28
 * explicit synchronization is often not needed. An exception is when we use
29
 * an API function that must wait. Ajax is an example. Callback passed to ajax
30
 * call doesn't get called immediately, but after some time. In the meantime
31
 * some other piece of code might get to execute and modify some variables.
32
 * Access to WebExtension local storage is another situation where this problem
33
 * can occur.
34
 *
35
 * This is a solution. A lock object, that can be used to delay execution of
36
 * some code until other code finishes its critical work. Locking is wrapped
37
 * in a promise.
38
 */
39

    
40
"use strict";
41

    
42
(() => {
43
    function make_lock() {
44
	return {free: true, queue: []};
45
    }
46

    
47
    function _lock(lock, cb) {
48
	if (lock.free) {
49
	    lock.free = false;
50
	    setTimeout(cb);
51
	} else {
52
	    lock.queue.push(cb);
53
	}
54
    }
55

    
56
    function lock(lock) {
57
	return new Promise((resolve, reject) => _lock(lock, resolve));
58
    }
59

    
60
    function unlock(lock) {
61
	if (lock.free)
62
	    throw new Exception("Attempting to release a free lock");
63

    
64
	if (lock.queue.length === 0) {
65
	    lock.free = true;
66
	} else {
67
	    let cb = lock.queue[0];
68
	    lock.queue.splice(0, 1);
69
	    setTimeout(cb);
70
	}
71
    }
72

    
73
    window.make_lock = make_lock;
74
    window.lock = lock;
75
    window.unlock = unlock;
76
})();
(4-4/9)