Skip to content

Commit 9e4dd2a

Browse files
committed
🐙 ready to test
1 parent 46b47d9 commit 9e4dd2a

File tree

5 files changed

+330
-2
lines changed

5 files changed

+330
-2
lines changed

.gitignore

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
*.class
2+
3+
# Mobile Tools for Java (J2ME)
4+
.mtj.tmp/
5+
6+
# Package Files #
7+
*.jar
8+
*.war
9+
*.ear
10+
11+
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
12+
hs_err_pid*
13+
14+
# =========================
15+
# Operating System Files
16+
# =========================
17+
18+
# OSX
19+
# =========================
20+
21+
.DS_Store
22+
.AppleDouble
23+
.LSOverride
24+
25+
# Thumbnails
26+
._*
27+
28+
# Files that might appear in the root of a volume
29+
.DocumentRevisions-V100
30+
.fseventsd
31+
.Spotlight-V100
32+
.TemporaryItems
33+
.Trashes
34+
.VolumeIcon.icns
35+
36+
# Directories potentially created on remote AFP share
37+
.AppleDB
38+
.AppleDesktop
39+
Network Trash Folder
40+
Temporary Items
41+
.apdisk
42+
43+
# Windows
44+
# =========================
45+
46+
# Windows image file caches
47+
Thumbs.db
48+
ehthumbs.db
49+
50+
# Folder config file
51+
Desktop.ini
52+
53+
# Recycle Bin used on file shares
54+
$RECYCLE.BIN/
55+
56+
# Windows Installer files
57+
*.cab
58+
*.msi
59+
*.msm
60+
*.msp
61+
62+
# Windows shortcuts
63+
*.lnk
64+
/build/
65+
/dist/

BackoffLock.java

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import java.util.concurrent.*;
2+
import java.util.concurrent.locks.*;
3+
import java.util.concurrent.atomic.*;
4+
5+
// Exponential Backoff Lock uses an atomic value for
6+
// indicating that some thread has engaged the lock
7+
// and is executing its critical section (CS).
8+
//
9+
// Each thread that wants to enter CS waits until
10+
// the atomic lock is disengaged. Then it tries
11+
// to reengage it, but as several threads might
12+
// be trying the same, it may or may not succeed.
13+
// So it checks if it succeeded (with the same
14+
// atomic operation), and if so it done. If not
15+
// it backsoff for a random interval which is
16+
// doubled every retry (hence exponential), and
17+
// retries it all over again.
18+
//
19+
// Once the thread is done with CS, it simply
20+
// disengages the lock.
21+
//
22+
// As all thread wait (spin) for the lock to
23+
// disengage at the same time, but backoff if
24+
// they fail in reengaging the lock, contention
25+
// is reduced. However, this does not provide
26+
// first-come-first-served fairness. But, as
27+
// it only uses a single atomic value per lock
28+
// it is suitable for medium-contention
29+
// memory-limited architectures.
30+
31+
class BackoffLock implements Lock {
32+
AtomicBoolean locked;
33+
static final long MIN_WAIT = 1;
34+
static final long MAX_WAIT = 100;
35+
// queue: indicates who has the token
36+
// size: max allowed threads (size of queue)
37+
// tail: points to end of queue
38+
// slot: points where each thread stands in queue
39+
40+
public BackoffLock() {
41+
locked = new AtomicBoolean(false);
42+
}
43+
44+
// 1. When thread wants to access critical
45+
// section, it checks to see if lock is already
46+
// engaged, and if so, waits (spins).
47+
// 2. Once lock is disengaged it tries to reengage
48+
// it. So do all other threads wanting to enter
49+
// CS. Its a race between threads. So this
50+
// thread checks to see it was the one who was
51+
// successful, and if so its done.
52+
// 3. If not, then it backsoff (sleeps) for a
53+
// random interval, and then retries again.
54+
// Each retry the range of backoff interval
55+
// is increased so reduce high-contention.
56+
@Override
57+
public void lock() {
58+
long W = MIN_WAIT;
59+
while(true) { // 1
60+
while(locked.get()) Thread.yield(); // 1
61+
if(!locked.getAndSet(true)) return; // 2
62+
long w = (long) (Math.random() * // 3
63+
(MAX_WAIT-MIN_WAIT) + MIN_WAIT); // 3
64+
W = Math.min(2*W, MAX_WAIT); // 3
65+
try { Thread.sleep(w); } // 3
66+
catch(InterruptedException e) {} // 3
67+
}
68+
}
69+
70+
// 1. When a thread is done with its critical
71+
// section, it simply sets the "locked" state
72+
// to false.
73+
@Override
74+
public void unlock() {
75+
locked.set(false); // 1
76+
}
77+
78+
@Override
79+
public void lockInterruptibly() throws InterruptedException {
80+
lock();
81+
}
82+
83+
@Override
84+
public boolean tryLock() {
85+
lock();
86+
return true;
87+
}
88+
89+
@Override
90+
public boolean tryLock(long arg0, TimeUnit arg1) throws InterruptedException {
91+
lock();
92+
return true;
93+
}
94+
95+
@Override
96+
public Condition newCondition() {
97+
return null;
98+
}
99+
}

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 Subhajit Sahu
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Main.java

+79-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,82 @@
11
class Main {
2+
static BackoffLock lock;
3+
static double[] sharedData;
4+
static int SD = 100, CS = 100, TH = 10;
5+
// SD: shared data srray size
6+
// CS: critical section executed per thread
7+
// TH: number of threads
8+
9+
// Critical section updated shared data
10+
// with a random value. If all values
11+
// dont match then it was not executed
12+
// atomically!
13+
static void criticalSection() {
14+
try {
15+
double v = Math.random();
16+
for(int i=0; i<SD; i++) {
17+
if(i % (SD/4)==0) Thread.sleep(1);
18+
sharedData[i] = v + v*sharedData[i];
19+
}
20+
}
21+
catch(InterruptedException e) {}
22+
}
23+
24+
// Checks to see if all values match. If not,
25+
// then critical section was not executed
26+
// atomically.
27+
static boolean criticalSectionWasAtomic() {
28+
double v = sharedData[0];
29+
for(int i=0; i<SD; i++)
30+
if(sharedData[i]!=v) return false;
31+
return true;
32+
}
33+
34+
// Unsafe thread executes CS N times, without
35+
// holding a lock. This can cause CS to be
36+
// executed non-atomically which can be detected.
37+
//
38+
// Safe thread executes CS N times, while
39+
// holding a lock. This allows CS to always be
40+
// executed atomically which can be verified.
41+
static Thread thread(int n, boolean safe) {
42+
Thread t = new Thread(() -> {
43+
for(int i=0; i<CS; i++) {
44+
if(safe) lock.lock();
45+
criticalSection();
46+
if(safe) lock.unlock();
47+
}
48+
log(n+": done");
49+
});
50+
t.start();
51+
return t;
52+
}
53+
54+
// Tests to see if threads execute critical
55+
// section atomically.
56+
static void testThreads(boolean safe) {
57+
String type = safe? "safe" : "unsafe";
58+
log("Starting "+TH+" "+type+" threads ...");
59+
Thread[] threads = new Thread[TH];
60+
for(int i=0; i<TH; i++)
61+
threads[i] = thread(i, safe);
62+
try {
63+
for(int i=0; i<TH; i++)
64+
threads[i].join();
65+
}
66+
catch(InterruptedException e) {}
67+
boolean atomic = criticalSectionWasAtomic();
68+
log("Critical Section was atomic? "+atomic);
69+
log("");
70+
}
71+
272
public static void main(String[] args) {
3-
System.out.println("Hello world!");
73+
lock = new BackoffLock(TH);
74+
sharedData = new double[SD];
75+
testThreads(false);
76+
testThreads(true);
77+
}
78+
79+
static void log(String x) {
80+
System.out.println(x);
481
}
5-
}
82+
}

README.md

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
Exponential Backoff Lock uses an atomic value for
2+
indicating that some thread has engaged the lock
3+
and is executing its critical section (CS).
4+
5+
Each thread that wants to enter CS waits until
6+
the atomic lock is disengaged. Then it tries
7+
to reengage it, but as several threads might
8+
be trying the same, it may or may not succeed.
9+
So it checks if it succeeded (with the same
10+
atomic operation), and if so it done. If not
11+
it backsoff for a random interval which is
12+
doubled every retry (hence exponential), and
13+
retries it all over again.
14+
15+
Once the thread is done with CS, it simply
16+
disengages the lock.
17+
18+
As all thread wait (spin) for the lock to
19+
disengage at the same time, but backoff if
20+
they fail in reengaging the lock, contention
21+
is reduced. However, this does not provide
22+
first-come-first-served fairness. But, as
23+
it only uses a single atomic value per lock
24+
it is suitable for medium-contention
25+
memory-limited architectures.
26+
27+
Exponential back off is a well-known technique
28+
used in Ethernet routing, presented in the
29+
context of multiprocessor mutual exclusion by
30+
[Anant Agarwal] and [Mathews Cherian].
31+
32+
[Anant Agarwal]: https://scholar.google.com/citations?hl=en&user=E6XXUFcAAAAJ
33+
[Mathews Cherian]: https://dl.acm.org/profile/81100089786
34+
35+
```java
36+
1. When thread wants to access critical
37+
section, it checks to see if lock is already
38+
engaged, and if so, waits (spins).
39+
2. Once lock is disengaged it tries to reengage
40+
it. So do all other threads wanting to enter
41+
CS. Its a race between threads. So this
42+
thread checks to see it was the one who was
43+
successful, and if so its done.
44+
3. If not, then it backsoff (sleeps) for a
45+
random interval, and then retries again.
46+
Each retry the range of backoff interval
47+
is increased so reduce high-contention.
48+
```
49+
50+
```java
51+
1. When a thread is done with its critical
52+
section, it simply sets the "locked" state
53+
to false.
54+
```
55+
56+
See [BackoffLock.java] for code, [Main.java] for test, and [repl.it] for output.
57+
58+
[BackoffLock.java]: https://repl.it/@wolfram77/backoff-lock#BackoffLock.java
59+
[Main.java]: https://repl.it/@wolfram77/backoff-lock#Main.java
60+
[repl.it]: https://backoff-lock.wolfram77.repl.run
61+
62+
63+
### references
64+
65+
- [The Art of Multiprocessor Programming :: Maurice Herlihy, Nir Shavit](https://dl.acm.org/doi/book/10.5555/2385452)
66+
- [Adaptive backoff synchronization techniques :: Anant Agarwal, Mathews Cherian](https://ieeexplore.ieee.org/document/714578)

0 commit comments

Comments
 (0)