blob: c46694138ebbd831dece0eae2f5ab656911a845f [file] [log] [blame]
Madan Jampani12390c12014-11-12 00:35:56 -08001package org.onlab.onos.store.service.impl;
2
3import java.util.UUID;
4import java.util.concurrent.CompletableFuture;
5import java.util.concurrent.ExecutionException;
6import java.util.concurrent.TimeUnit;
7import java.util.concurrent.TimeoutException;
8import java.util.concurrent.atomic.AtomicBoolean;
9
10import org.joda.time.DateTime;
11import org.onlab.onos.cluster.ClusterService;
12import org.onlab.onos.store.service.DatabaseService;
13import org.onlab.onos.store.service.Lock;
Madan Jampani12390c12014-11-12 00:35:56 -080014
15/**
16 * A distributed lock implementation.
17 */
18public class DistributedLock implements Lock {
19
20 private final DistributedLockManager lockManager;
21 private final DatabaseService databaseService;
22 private final String path;
23 private DateTime lockExpirationTime;
24 private AtomicBoolean isLocked = new AtomicBoolean(false);
25 private byte[] lockId;
26
27 public DistributedLock(
28 String path,
29 DatabaseService databaseService,
30 ClusterService clusterService,
31 DistributedLockManager lockManager) {
32
33 this.path = path;
34 this.databaseService = databaseService;
35 this.lockManager = lockManager;
36 this.lockId =
37 (UUID.randomUUID().toString() + "::" + clusterService.getLocalNode().id().toString()).getBytes();
38 }
39
40 @Override
41 public String path() {
42 return path;
43 }
44
45 @Override
46 public void lock(int leaseDurationMillis) {
47
48 if (isLocked() && lockExpirationTime.isAfter(DateTime.now().plusMillis(leaseDurationMillis))) {
49 // Nothing to do.
50 // Current expiration time is beyond what is requested.
51 return;
52 } else {
53 tryLock(Long.MAX_VALUE, leaseDurationMillis);
54 }
55 }
56
57 @Override
58 public boolean tryLock(int leaseDurationMillis) {
Madan Jampanic22123d2014-11-12 02:12:19 -080059 return databaseService.putIfAbsent(
60 DistributedLockManager.ONOS_LOCK_TABLE_NAME,
61 path,
62 lockId);
Madan Jampani12390c12014-11-12 00:35:56 -080063 }
64
65 @Override
66 public boolean tryLock(
67 long waitTimeMillis,
68 int leaseDurationMillis) {
Madan Jampani44e6a542014-11-12 01:06:51 -080069 if (!tryLock(leaseDurationMillis)) {
Madan Jampani12390c12014-11-12 00:35:56 -080070 CompletableFuture<Void> future =
71 lockManager.lockIfAvailable(this, waitTimeMillis, leaseDurationMillis);
72 try {
73 future.get(waitTimeMillis, TimeUnit.MILLISECONDS);
74 } catch (ExecutionException | InterruptedException e) {
75 // TODO: ExecutionException could indicate something
76 // wrong with the backing database.
77 // Throw an exception?
78 return false;
79 } catch (TimeoutException e) {
80 return false;
81 }
82 }
83 lockExpirationTime = DateTime.now().plusMillis(leaseDurationMillis);
84 return true;
85 }
86
87 @Override
88 public boolean isLocked() {
89 if (isLocked.get()) {
90 // We rely on local information to check
91 // if the expired.
92 // This should should make this call
93 // light weight, which still retaining the same
94 // safety guarantees.
95 if (DateTime.now().isAfter(lockExpirationTime)) {
96 isLocked.set(false);
97 return false;
98 }
99 }
100 return true;
101 }
102
103 @Override
104 public void unlock() {
105 if (!isLocked()) {
106 return;
107 } else {
108 databaseService.removeIfValueMatches(DistributedLockManager.ONOS_LOCK_TABLE_NAME, path, lockId);
109 }
110 }
111
112 @Override
113 public boolean extendExpiration(int leaseDurationMillis) {
114 if (isLocked() && lockExpirationTime.isAfter(DateTime.now().plusMillis(leaseDurationMillis))) {
115 return true;
116 } else {
117 return tryLock(leaseDurationMillis);
118 }
119 }
120}