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