mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-11 18:29:05 +01:00
Improved encapsulation of thread synchronisation as follows
- replaced use of Object instance mutex with a private final Lock object - replaced Object signaling with specific condition signalling
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
build
|
build
|
||||||
.gradle
|
.gradle
|
||||||
.metadata
|
.metadata
|
||||||
|
*.tmp
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import static java.util.logging.Level.WARNING;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -64,6 +66,8 @@ Service, EventListener {
|
|||||||
private int nextRequestId = 0; // Locking: this
|
private int nextRequestId = 0; // Locking: this
|
||||||
|
|
||||||
private volatile Settings settings = new Settings();
|
private volatile Settings settings = new Settings();
|
||||||
|
|
||||||
|
private final Lock synchLock = new ReentrantLock();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public AndroidNotificationManagerImpl(DatabaseComponent db,
|
public AndroidNotificationManagerImpl(DatabaseComponent db,
|
||||||
@@ -103,19 +107,31 @@ Service, EventListener {
|
|||||||
if(e instanceof SettingsUpdatedEvent) loadSettings();
|
if(e instanceof SettingsUpdatedEvent) loadSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void showPrivateMessageNotification(ContactId c) {
|
public void showPrivateMessageNotification(ContactId c) {
|
||||||
Integer count = contactCounts.get(c);
|
synchLock.lock();
|
||||||
if(count == null) contactCounts.put(c, 1);
|
try{
|
||||||
else contactCounts.put(c, count + 1);
|
Integer count = contactCounts.get(c);
|
||||||
privateTotal++;
|
if(count == null) contactCounts.put(c, 1);
|
||||||
updatePrivateMessageNotification();
|
else contactCounts.put(c, count + 1);
|
||||||
|
privateTotal++;
|
||||||
|
updatePrivateMessageNotification();
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void clearPrivateMessageNotification(ContactId c) {
|
public void clearPrivateMessageNotification(ContactId c) {
|
||||||
Integer count = contactCounts.remove(c);
|
synchLock.lock();
|
||||||
if(count == null) return; // Already cleared
|
try{
|
||||||
privateTotal -= count;
|
Integer count = contactCounts.remove(c);
|
||||||
updatePrivateMessageNotification();
|
if(count == null) return; // Already cleared
|
||||||
|
privateTotal -= count;
|
||||||
|
updatePrivateMessageNotification();
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locking: this
|
// Locking: this
|
||||||
@@ -180,19 +196,31 @@ Service, EventListener {
|
|||||||
return defaults;
|
return defaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void showGroupPostNotification(GroupId g) {
|
public void showGroupPostNotification(GroupId g) {
|
||||||
Integer count = groupCounts.get(g);
|
synchLock.lock();
|
||||||
if(count == null) groupCounts.put(g, 1);
|
try{
|
||||||
else groupCounts.put(g, count + 1);
|
Integer count = groupCounts.get(g);
|
||||||
groupTotal++;
|
if(count == null) groupCounts.put(g, 1);
|
||||||
updateGroupPostNotification();
|
else groupCounts.put(g, count + 1);
|
||||||
|
groupTotal++;
|
||||||
|
updateGroupPostNotification();
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void clearGroupPostNotification(GroupId g) {
|
public void clearGroupPostNotification(GroupId g) {
|
||||||
|
synchLock.lock();
|
||||||
|
try{
|
||||||
Integer count = groupCounts.remove(g);
|
Integer count = groupCounts.remove(g);
|
||||||
if(count == null) return; // Already cleared
|
if(count == null) return; // Already cleared
|
||||||
groupTotal -= count;
|
groupTotal -= count;
|
||||||
updateGroupPostNotification();
|
updateGroupPostNotification();
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locking: this
|
// Locking: this
|
||||||
@@ -238,18 +266,23 @@ Service, EventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locking: this
|
|
||||||
private void clearGroupPostNotification() {
|
private void clearGroupPostNotification() {
|
||||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||||
NotificationManager nm = (NotificationManager) o;
|
NotificationManager nm = (NotificationManager) o;
|
||||||
nm.cancel(GROUP_POST_NOTIFICATION_ID);
|
nm.cancel(GROUP_POST_NOTIFICATION_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void clearNotifications() {
|
public void clearNotifications() {
|
||||||
contactCounts.clear();
|
synchLock.lock();
|
||||||
groupCounts.clear();
|
try{
|
||||||
privateTotal = groupTotal = 0;
|
contactCounts.clear();
|
||||||
clearPrivateMessageNotification();
|
groupCounts.clear();
|
||||||
clearGroupPostNotification();
|
privateTotal = groupTotal = 0;
|
||||||
|
clearPrivateMessageNotification();
|
||||||
|
clearGroupPostNotification();
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import static java.util.logging.Level.INFO;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.briarproject.api.android.ReferenceManager;
|
import org.briarproject.api.android.ReferenceManager;
|
||||||
@@ -13,49 +15,70 @@ class ReferenceManagerImpl implements ReferenceManager {
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(ReferenceManagerImpl.class.getName());
|
Logger.getLogger(ReferenceManagerImpl.class.getName());
|
||||||
|
|
||||||
// Locking: this
|
|
||||||
private final Map<Class<?>, Map<Long, Object>> outerMap =
|
private final Map<Class<?>, Map<Long, Object>> outerMap =
|
||||||
new HashMap<Class<?>, Map<Long, Object>>();
|
new HashMap<Class<?>, Map<Long, Object>>();
|
||||||
|
|
||||||
private long nextHandle = 0; // Locking: this
|
private long nextHandle = 0; // Locking: this
|
||||||
|
|
||||||
public synchronized <T> T getReference(long handle, Class<T> c) {
|
private final Lock synchLock = new ReentrantLock();
|
||||||
Map<Long, Object> innerMap = outerMap.get(c);
|
|
||||||
if(innerMap == null) {
|
public <T> T getReference(long handle, Class<T> c) {
|
||||||
|
synchLock.lock();
|
||||||
|
try{
|
||||||
|
Map<Long, Object> innerMap = outerMap.get(c);
|
||||||
|
if(innerMap == null) {
|
||||||
|
if(LOG.isLoggable(INFO))
|
||||||
|
LOG.info("0 handles for " + c.getName());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if(LOG.isLoggable(INFO))
|
if(LOG.isLoggable(INFO))
|
||||||
LOG.info("0 handles for " + c.getName());
|
LOG.info(innerMap.size() + " handles for " + c.getName());
|
||||||
return null;
|
Object o = innerMap.get(handle);
|
||||||
|
return c.cast(o);
|
||||||
}
|
}
|
||||||
if(LOG.isLoggable(INFO))
|
finally{
|
||||||
LOG.info(innerMap.size() + " handles for " + c.getName());
|
synchLock.unlock();
|
||||||
Object o = innerMap.get(handle);
|
}
|
||||||
return c.cast(o);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized <T> long putReference(T reference, Class<T> c) {
|
public <T> long putReference(T reference, Class<T> c) {
|
||||||
Map<Long, Object> innerMap = outerMap.get(c);
|
synchLock.lock();
|
||||||
if(innerMap == null) {
|
try{
|
||||||
innerMap = new HashMap<Long, Object>();
|
Map<Long, Object> innerMap = outerMap.get(c);
|
||||||
outerMap.put(c, innerMap);
|
if(innerMap == null) {
|
||||||
|
innerMap = new HashMap<Long, Object>();
|
||||||
|
outerMap.put(c, innerMap);
|
||||||
|
}
|
||||||
|
long handle = nextHandle++;
|
||||||
|
innerMap.put(handle, reference);
|
||||||
|
if(LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info(innerMap.size() + " handles for " + c.getName() +
|
||||||
|
" after put");
|
||||||
|
}
|
||||||
|
return handle;
|
||||||
}
|
}
|
||||||
long handle = nextHandle++;
|
finally{
|
||||||
innerMap.put(handle, reference);
|
synchLock.unlock();
|
||||||
if(LOG.isLoggable(INFO)) {
|
|
||||||
LOG.info(innerMap.size() + " handles for " + c.getName() +
|
|
||||||
" after put");
|
|
||||||
}
|
}
|
||||||
return handle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized <T> T removeReference(long handle, Class<T> c) {
|
public <T> T removeReference(long handle, Class<T> c) {
|
||||||
Map<Long, Object> innerMap = outerMap.get(c);
|
synchLock.lock();
|
||||||
if(innerMap == null) return null;
|
try{
|
||||||
Object o = innerMap.remove(handle);
|
Map<Long, Object> innerMap = outerMap.get(c);
|
||||||
if(innerMap.isEmpty()) outerMap.remove(c);
|
if(innerMap == null) return null;
|
||||||
if(LOG.isLoggable(INFO)) {
|
Object o = innerMap.remove(handle);
|
||||||
LOG.info(innerMap.size() + " handles for " + c.getName() +
|
if(innerMap.isEmpty()) outerMap.remove(c);
|
||||||
" after remove");
|
if(LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info(innerMap.size() + " handles for " + c.getName() +
|
||||||
|
" after remove");
|
||||||
|
}
|
||||||
|
return c.cast(o);
|
||||||
}
|
}
|
||||||
return c.cast(o);
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package org.briarproject.crypto;
|
package org.briarproject.crypto;
|
||||||
|
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import org.briarproject.api.crypto.MessageDigest;
|
import org.briarproject.api.crypto.MessageDigest;
|
||||||
import org.spongycastle.crypto.BlockCipher;
|
import org.spongycastle.crypto.BlockCipher;
|
||||||
import org.spongycastle.crypto.digests.SHA256Digest;
|
import org.spongycastle.crypto.digests.SHA256Digest;
|
||||||
@@ -23,61 +26,89 @@ class FortunaGenerator {
|
|||||||
private final byte[] counter = new byte[BLOCK_BYTES];
|
private final byte[] counter = new byte[BLOCK_BYTES];
|
||||||
private final byte[] buffer = new byte[BLOCK_BYTES];
|
private final byte[] buffer = new byte[BLOCK_BYTES];
|
||||||
private final byte[] newKey = new byte[KEY_BYTES];
|
private final byte[] newKey = new byte[KEY_BYTES];
|
||||||
|
|
||||||
|
private final Lock synchLock = new ReentrantLock();
|
||||||
|
|
||||||
FortunaGenerator(byte[] seed) {
|
FortunaGenerator(byte[] seed) {
|
||||||
reseed(seed);
|
reseed(seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void reseed(byte[] seed) {
|
void reseed(byte[] seed) {
|
||||||
digest.update(key);
|
synchLock.lock();
|
||||||
digest.update(seed);
|
try{
|
||||||
digest.digest(key, 0, KEY_BYTES);
|
digest.update(key);
|
||||||
incrementCounter();
|
digest.update(seed);
|
||||||
|
digest.digest(key, 0, KEY_BYTES);
|
||||||
|
incrementCounter();
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Package access for testing
|
// Package access for testing
|
||||||
synchronized void incrementCounter() {
|
void incrementCounter() {
|
||||||
counter[0]++;
|
synchLock.lock();
|
||||||
for(int i = 0; counter[i] == 0; i++) {
|
try{
|
||||||
if(i + 1 == BLOCK_BYTES)
|
counter[0]++;
|
||||||
throw new RuntimeException("Counter exhausted");
|
for(int i = 0; counter[i] == 0; i++) {
|
||||||
counter[i + 1]++;
|
if(i + 1 == BLOCK_BYTES)
|
||||||
|
throw new RuntimeException("Counter exhausted");
|
||||||
|
counter[i + 1]++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Package access for testing
|
// Package access for testing
|
||||||
synchronized byte[] getCounter() {
|
byte[] getCounter() {
|
||||||
return counter;
|
synchLock.lock();
|
||||||
|
try{
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized int nextBytes(byte[] dest, int off, int len) {
|
int nextBytes(byte[] dest, int off, int len) {
|
||||||
// Don't write more than the maximum number of bytes in one request
|
synchLock.lock();
|
||||||
if(len > MAX_BYTES_PER_REQUEST) len = MAX_BYTES_PER_REQUEST;
|
try{
|
||||||
cipher.init(true, new KeyParameter(key));
|
// Don't write more than the maximum number of bytes in one request
|
||||||
// Generate full blocks directly into the output buffer
|
if(len > MAX_BYTES_PER_REQUEST) len = MAX_BYTES_PER_REQUEST;
|
||||||
int fullBlocks = len / BLOCK_BYTES;
|
cipher.init(true, new KeyParameter(key));
|
||||||
for(int i = 0; i < fullBlocks; i++) {
|
// Generate full blocks directly into the output buffer
|
||||||
cipher.processBlock(counter, 0, dest, off + i * BLOCK_BYTES);
|
int fullBlocks = len / BLOCK_BYTES;
|
||||||
incrementCounter();
|
for(int i = 0; i < fullBlocks; i++) {
|
||||||
|
cipher.processBlock(counter, 0, dest, off + i * BLOCK_BYTES);
|
||||||
|
incrementCounter();
|
||||||
|
}
|
||||||
|
// Generate a partial block if needed
|
||||||
|
int done = fullBlocks * BLOCK_BYTES, remaining = len - done;
|
||||||
|
assert remaining < BLOCK_BYTES;
|
||||||
|
if(remaining > 0) {
|
||||||
|
cipher.processBlock(counter, 0, buffer, 0);
|
||||||
|
incrementCounter();
|
||||||
|
// Copy the partial block to the output buffer and erase our copy
|
||||||
|
System.arraycopy(buffer, 0, dest, off + done, remaining);
|
||||||
|
for(int i = 0; i < BLOCK_BYTES; i++) buffer[i] = 0;
|
||||||
|
}
|
||||||
|
// Generate a new key
|
||||||
|
for(int i = 0; i < KEY_BYTES / BLOCK_BYTES; i++) {
|
||||||
|
cipher.processBlock(counter, 0, newKey, i * BLOCK_BYTES);
|
||||||
|
incrementCounter();
|
||||||
|
}
|
||||||
|
System.arraycopy(newKey, 0, key, 0, KEY_BYTES);
|
||||||
|
for(int i = 0; i < KEY_BYTES; i++) newKey[i] = 0;
|
||||||
|
// Return the number of bytes written
|
||||||
|
return len;
|
||||||
}
|
}
|
||||||
// Generate a partial block if needed
|
finally{
|
||||||
int done = fullBlocks * BLOCK_BYTES, remaining = len - done;
|
synchLock.unlock();
|
||||||
assert remaining < BLOCK_BYTES;
|
|
||||||
if(remaining > 0) {
|
|
||||||
cipher.processBlock(counter, 0, buffer, 0);
|
|
||||||
incrementCounter();
|
|
||||||
// Copy the partial block to the output buffer and erase our copy
|
|
||||||
System.arraycopy(buffer, 0, dest, off + done, remaining);
|
|
||||||
for(int i = 0; i < BLOCK_BYTES; i++) buffer[i] = 0;
|
|
||||||
}
|
}
|
||||||
// Generate a new key
|
|
||||||
for(int i = 0; i < KEY_BYTES / BLOCK_BYTES; i++) {
|
|
||||||
cipher.processBlock(counter, 0, newKey, i * BLOCK_BYTES);
|
|
||||||
incrementCounter();
|
|
||||||
}
|
|
||||||
System.arraycopy(newKey, 0, key, 0, KEY_BYTES);
|
|
||||||
for(int i = 0; i < KEY_BYTES; i++) newKey[i] = 0;
|
|
||||||
// Return the number of bytes written
|
|
||||||
return len;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package org.briarproject.crypto;
|
package org.briarproject.crypto;
|
||||||
|
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import org.briarproject.api.crypto.MessageDigest;
|
import org.briarproject.api.crypto.MessageDigest;
|
||||||
import org.briarproject.api.crypto.PseudoRandom;
|
import org.briarproject.api.crypto.PseudoRandom;
|
||||||
import org.briarproject.util.ByteUtils;
|
import org.briarproject.util.ByteUtils;
|
||||||
@@ -10,6 +13,8 @@ class PseudoRandomImpl implements PseudoRandom {
|
|||||||
|
|
||||||
private byte[] state;
|
private byte[] state;
|
||||||
private int offset;
|
private int offset;
|
||||||
|
|
||||||
|
private final Lock synchLock = new ReentrantLock();
|
||||||
|
|
||||||
PseudoRandomImpl(MessageDigest messageDigest, int seed1, int seed2) {
|
PseudoRandomImpl(MessageDigest messageDigest, int seed1, int seed2) {
|
||||||
this.messageDigest = messageDigest;
|
this.messageDigest = messageDigest;
|
||||||
@@ -21,21 +26,27 @@ class PseudoRandomImpl implements PseudoRandom {
|
|||||||
offset = 0;
|
offset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized byte[] nextBytes(int bytes) {
|
public byte[] nextBytes(int bytes) {
|
||||||
byte[] b = new byte[bytes];
|
synchLock.lock();
|
||||||
int half = state.length / 2;
|
try{
|
||||||
int off = 0, len = b.length, available = half - offset;
|
byte[] b = new byte[bytes];
|
||||||
while(available < len) {
|
int half = state.length / 2;
|
||||||
System.arraycopy(state, offset, b, off, available);
|
int off = 0, len = b.length, available = half - offset;
|
||||||
off += available;
|
while(available < len) {
|
||||||
len -= available;
|
System.arraycopy(state, offset, b, off, available);
|
||||||
messageDigest.update(state, half, half);
|
off += available;
|
||||||
state = messageDigest.digest();
|
len -= available;
|
||||||
offset = 0;
|
messageDigest.update(state, half, half);
|
||||||
available = half;
|
state = messageDigest.digest();
|
||||||
|
offset = 0;
|
||||||
|
available = half;
|
||||||
|
}
|
||||||
|
System.arraycopy(state, offset, b, off, len);
|
||||||
|
offset += len;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
System.arraycopy(state, offset, b, off, len);
|
|
||||||
offset += len;
|
|
||||||
return b;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package org.briarproject.crypto;
|
package org.briarproject.crypto;
|
||||||
|
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import org.briarproject.api.crypto.SecretKey;
|
import org.briarproject.api.crypto.SecretKey;
|
||||||
import org.briarproject.util.ByteUtils;
|
import org.briarproject.util.ByteUtils;
|
||||||
|
|
||||||
@@ -8,23 +11,38 @@ class SecretKeyImpl implements SecretKey {
|
|||||||
private final byte[] key;
|
private final byte[] key;
|
||||||
|
|
||||||
private boolean erased = false; // Locking: this
|
private boolean erased = false; // Locking: this
|
||||||
|
|
||||||
|
private final Lock synchLock = new ReentrantLock();
|
||||||
|
|
||||||
SecretKeyImpl(byte[] key) {
|
SecretKeyImpl(byte[] key) {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized byte[] getEncoded() {
|
public byte[] getEncoded() {
|
||||||
if(erased) throw new IllegalStateException();
|
synchLock.lock();
|
||||||
return key;
|
try{
|
||||||
|
if(erased) throw new IllegalStateException();
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SecretKey copy() {
|
public SecretKey copy() {
|
||||||
return new SecretKeyImpl(key.clone());
|
return new SecretKeyImpl(key.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void erase() {
|
public void erase() {
|
||||||
if(erased) throw new IllegalStateException();
|
synchLock.lock();
|
||||||
ByteUtils.erase(key);
|
try{
|
||||||
erased = true;
|
if(erased) throw new IllegalStateException();
|
||||||
|
ByteUtils.erase(key);
|
||||||
|
erased = true;
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ import java.util.Map;
|
|||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.Condition;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.briarproject.api.Author;
|
import org.briarproject.api.Author;
|
||||||
@@ -322,6 +325,9 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
protected abstract Connection createConnection() throws SQLException;
|
protected abstract Connection createConnection() throws SQLException;
|
||||||
protected abstract void flushBuffersToDisk(Statement s) throws SQLException;
|
protected abstract void flushBuffersToDisk(Statement s) throws SQLException;
|
||||||
|
|
||||||
|
private final Lock connectionsLock = new ReentrantLock();
|
||||||
|
private final Condition connectionsChanged = connectionsLock.newCondition();
|
||||||
|
|
||||||
JdbcDatabase(String hashType, String binaryType, String counterType,
|
JdbcDatabase(String hashType, String binaryType, String counterType,
|
||||||
String secretType, Clock clock) {
|
String secretType, Clock clock) {
|
||||||
@@ -431,19 +437,28 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
public Connection startTransaction() throws DbException {
|
public Connection startTransaction() throws DbException {
|
||||||
Connection txn = null;
|
Connection txn = null;
|
||||||
synchronized(connections) {
|
connectionsLock.lock();
|
||||||
|
try {
|
||||||
if(closed) throw new DbClosedException();
|
if(closed) throw new DbClosedException();
|
||||||
txn = connections.poll();
|
txn = connections.poll();
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
connectionsLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(txn == null) {
|
if(txn == null) {
|
||||||
// Open a new connection
|
// Open a new connection
|
||||||
txn = createConnection();
|
txn = createConnection();
|
||||||
if(txn == null) throw new DbException();
|
if(txn == null) throw new DbException();
|
||||||
txn.setAutoCommit(false);
|
txn.setAutoCommit(false);
|
||||||
synchronized(connections) {
|
connectionsLock.lock();
|
||||||
|
try {
|
||||||
openConnections++;
|
openConnections++;
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
connectionsLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch(SQLException e) {
|
} catch(SQLException e) {
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
@@ -455,9 +470,13 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
public void abortTransaction(Connection txn) {
|
public void abortTransaction(Connection txn) {
|
||||||
try {
|
try {
|
||||||
txn.rollback();
|
txn.rollback();
|
||||||
synchronized(connections) {
|
connectionsLock.lock();
|
||||||
|
try {
|
||||||
connections.add(txn);
|
connections.add(txn);
|
||||||
connections.notifyAll();
|
connectionsChanged.signalAll();
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
connectionsLock.unlock();
|
||||||
}
|
}
|
||||||
} catch(SQLException e) {
|
} catch(SQLException e) {
|
||||||
// Try to close the connection
|
// Try to close the connection
|
||||||
@@ -468,11 +487,14 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e1.toString(), e1);
|
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e1.toString(), e1);
|
||||||
}
|
}
|
||||||
// Whatever happens, allow the database to close
|
// Whatever happens, allow the database to close
|
||||||
synchronized(connections) {
|
connectionsLock.lock();
|
||||||
|
try {
|
||||||
openConnections--;
|
openConnections--;
|
||||||
connections.notifyAll();
|
connectionsChanged.signalAll();
|
||||||
}
|
}
|
||||||
}
|
finally{
|
||||||
|
connectionsLock.unlock();
|
||||||
|
} }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void commitTransaction(Connection txn) throws DbException {
|
public void commitTransaction(Connection txn) throws DbException {
|
||||||
@@ -486,9 +508,13 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
tryToClose(s);
|
tryToClose(s);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
synchronized(connections) {
|
connectionsLock.lock();
|
||||||
|
try{
|
||||||
connections.add(txn);
|
connections.add(txn);
|
||||||
connections.notifyAll();
|
connectionsChanged.signalAll();
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
connectionsLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,14 +528,15 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
protected void closeAllConnections() throws SQLException {
|
protected void closeAllConnections() throws SQLException {
|
||||||
boolean interrupted = false;
|
boolean interrupted = false;
|
||||||
synchronized(connections) {
|
connectionsLock.lock();
|
||||||
|
try{
|
||||||
closed = true;
|
closed = true;
|
||||||
for(Connection c : connections) c.close();
|
for(Connection c : connections) c.close();
|
||||||
openConnections -= connections.size();
|
openConnections -= connections.size();
|
||||||
connections.clear();
|
connections.clear();
|
||||||
while(openConnections > 0) {
|
while(openConnections > 0) {
|
||||||
try {
|
try {
|
||||||
connections.wait();
|
connectionsChanged.await();
|
||||||
} catch(InterruptedException e) {
|
} catch(InterruptedException e) {
|
||||||
LOG.warning("Interrupted while closing connections");
|
LOG.warning("Interrupted while closing connections");
|
||||||
interrupted = true;
|
interrupted = true;
|
||||||
@@ -519,6 +546,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
connections.clear();
|
connections.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
connectionsLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
if(interrupted) Thread.currentThread().interrupt();
|
if(interrupted) Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import java.util.Map;
|
|||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.briarproject.api.Author;
|
import org.briarproject.api.Author;
|
||||||
@@ -60,6 +62,8 @@ class ConnectorGroup extends Thread implements InvitationTask {
|
|||||||
private final Collection<InvitationListener> listeners;
|
private final Collection<InvitationListener> listeners;
|
||||||
private final AtomicBoolean connected;
|
private final AtomicBoolean connected;
|
||||||
private final CountDownLatch localConfirmationLatch;
|
private final CountDownLatch localConfirmationLatch;
|
||||||
|
|
||||||
|
private final Lock synchLock = new ReentrantLock();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* All of the following require locking: this. We don't want to call the
|
* All of the following require locking: this. We don't want to call the
|
||||||
@@ -104,12 +108,18 @@ class ConnectorGroup extends Thread implements InvitationTask {
|
|||||||
localConfirmationLatch = new CountDownLatch(1);
|
localConfirmationLatch = new CountDownLatch(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized InvitationState addListener(InvitationListener l) {
|
public InvitationState addListener(InvitationListener l) {
|
||||||
listeners.add(l);
|
synchLock.lock();
|
||||||
return new InvitationState(localInvitationCode, remoteInvitationCode,
|
try{
|
||||||
localConfirmationCode, remoteConfirmationCode, connected.get(),
|
listeners.add(l);
|
||||||
connectionFailed, localCompared, remoteCompared, localMatched,
|
return new InvitationState(localInvitationCode, remoteInvitationCode,
|
||||||
remoteMatched, remoteName);
|
localConfirmationCode, remoteConfirmationCode, connected.get(),
|
||||||
|
connectionFailed, localCompared, remoteCompared, localMatched,
|
||||||
|
remoteMatched, remoteName);
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeListener(InvitationListener l) {
|
public void removeListener(InvitationListener l) {
|
||||||
@@ -130,9 +140,13 @@ class ConnectorGroup extends Thread implements InvitationTask {
|
|||||||
localProps = db.getLocalProperties();
|
localProps = db.getLocalProperties();
|
||||||
} catch(DbException e) {
|
} catch(DbException e) {
|
||||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
connectionFailed = true;
|
connectionFailed = true;
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
for(InvitationListener l : listeners) l.connectionFailed();
|
for(InvitationListener l : listeners) l.connectionFailed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -163,9 +177,13 @@ class ConnectorGroup extends Thread implements InvitationTask {
|
|||||||
}
|
}
|
||||||
// If none of the threads connected, inform the listeners
|
// If none of the threads connected, inform the listeners
|
||||||
if(!connected.get()) {
|
if(!connected.get()) {
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
connectionFailed = true;
|
connectionFailed = true;
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
for(InvitationListener l : listeners) l.connectionFailed();
|
for(InvitationListener l : listeners) l.connectionFailed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -193,18 +211,26 @@ class ConnectorGroup extends Thread implements InvitationTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void localConfirmationSucceeded() {
|
public void localConfirmationSucceeded() {
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
localCompared = true;
|
localCompared = true;
|
||||||
localMatched = true;
|
localMatched = true;
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
localConfirmationLatch.countDown();
|
localConfirmationLatch.countDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void localConfirmationFailed() {
|
public void localConfirmationFailed() {
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
localCompared = true;
|
localCompared = true;
|
||||||
localMatched = false;
|
localMatched = false;
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
localConfirmationLatch.countDown();
|
localConfirmationLatch.countDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,10 +242,14 @@ class ConnectorGroup extends Thread implements InvitationTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void keyAgreementSucceeded(int localCode, int remoteCode) {
|
void keyAgreementSucceeded(int localCode, int remoteCode) {
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
localConfirmationCode = localCode;
|
localConfirmationCode = localCode;
|
||||||
remoteConfirmationCode = remoteCode;
|
remoteConfirmationCode = remoteCode;
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
for(InvitationListener l : listeners)
|
for(InvitationListener l : listeners)
|
||||||
l.keyAgreementSucceeded(localCode, remoteCode);
|
l.keyAgreementSucceeded(localCode, remoteCode);
|
||||||
}
|
}
|
||||||
@@ -230,32 +260,48 @@ class ConnectorGroup extends Thread implements InvitationTask {
|
|||||||
|
|
||||||
boolean waitForLocalConfirmationResult() throws InterruptedException {
|
boolean waitForLocalConfirmationResult() throws InterruptedException {
|
||||||
localConfirmationLatch.await(CONFIRMATION_TIMEOUT, MILLISECONDS);
|
localConfirmationLatch.await(CONFIRMATION_TIMEOUT, MILLISECONDS);
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
return localMatched;
|
return localMatched;
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void remoteConfirmationSucceeded() {
|
void remoteConfirmationSucceeded() {
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
remoteCompared = true;
|
remoteCompared = true;
|
||||||
remoteMatched = true;
|
remoteMatched = true;
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
for(InvitationListener l : listeners) l.remoteConfirmationSucceeded();
|
for(InvitationListener l : listeners) l.remoteConfirmationSucceeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
void remoteConfirmationFailed() {
|
void remoteConfirmationFailed() {
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
remoteCompared = true;
|
remoteCompared = true;
|
||||||
remoteMatched = false;
|
remoteMatched = false;
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
for(InvitationListener l : listeners) l.remoteConfirmationFailed();
|
for(InvitationListener l : listeners) l.remoteConfirmationFailed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void pseudonymExchangeSucceeded(Author remoteAuthor) {
|
void pseudonymExchangeSucceeded(Author remoteAuthor) {
|
||||||
String name = remoteAuthor.getName();
|
String name = remoteAuthor.getName();
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
remoteName = name;
|
remoteName = name;
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
for(InvitationListener l : listeners)
|
for(InvitationListener l : listeners)
|
||||||
l.pseudonymExchangeSucceeded(name);
|
l.pseudonymExchangeSucceeded(name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package org.briarproject.lifecycle;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import org.briarproject.api.lifecycle.ShutdownManager;
|
import org.briarproject.api.lifecycle.ShutdownManager;
|
||||||
|
|
||||||
@@ -10,26 +12,42 @@ class ShutdownManagerImpl implements ShutdownManager {
|
|||||||
protected final Map<Integer, Thread> hooks; // Locking: this
|
protected final Map<Integer, Thread> hooks; // Locking: this
|
||||||
|
|
||||||
private int nextHandle = 0; // Locking: this
|
private int nextHandle = 0; // Locking: this
|
||||||
|
|
||||||
|
private final Lock synchLock = new ReentrantLock();
|
||||||
|
|
||||||
ShutdownManagerImpl() {
|
ShutdownManagerImpl() {
|
||||||
hooks = new HashMap<Integer, Thread>();
|
hooks = new HashMap<Integer, Thread>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized int addShutdownHook(Runnable r) {
|
public int addShutdownHook(Runnable r) {
|
||||||
int handle = nextHandle++;
|
synchLock.lock();
|
||||||
Thread hook = createThread(r);
|
try{
|
||||||
hooks.put(handle, hook);
|
int handle = nextHandle++;
|
||||||
Runtime.getRuntime().addShutdownHook(hook);
|
Thread hook = createThread(r);
|
||||||
return handle;
|
hooks.put(handle, hook);
|
||||||
|
Runtime.getRuntime().addShutdownHook(hook);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Thread createThread(Runnable r) {
|
protected Thread createThread(Runnable r) {
|
||||||
return new Thread(r, "ShutdownManager");
|
return new Thread(r, "ShutdownManager");
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean removeShutdownHook(int handle) {
|
public boolean removeShutdownHook(int handle) {
|
||||||
Thread hook = hooks.remove(handle);
|
synchLock.lock();
|
||||||
if(hook == null) return false;
|
try{
|
||||||
else return Runtime.getRuntime().removeShutdownHook(hook);
|
Thread hook = hooks.remove(handle);
|
||||||
|
if(hook == null) return false;
|
||||||
|
else return Runtime.getRuntime().removeShutdownHook(hook);
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import java.util.Collections;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.briarproject.api.ContactId;
|
import org.briarproject.api.ContactId;
|
||||||
@@ -29,6 +31,8 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
|
|||||||
private final Map<TransportId, Map<ContactId, Integer>> connections;
|
private final Map<TransportId, Map<ContactId, Integer>> connections;
|
||||||
// Locking: this
|
// Locking: this
|
||||||
private final Map<ContactId, Integer> contactCounts;
|
private final Map<ContactId, Integer> contactCounts;
|
||||||
|
|
||||||
|
private final Lock synchLock = new ReentrantLock();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ConnectionRegistryImpl(EventBus eventBus) {
|
ConnectionRegistryImpl(EventBus eventBus) {
|
||||||
@@ -40,7 +44,8 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
|
|||||||
public void registerConnection(ContactId c, TransportId t) {
|
public void registerConnection(ContactId c, TransportId t) {
|
||||||
LOG.info("Connection registered");
|
LOG.info("Connection registered");
|
||||||
boolean firstConnection = false;
|
boolean firstConnection = false;
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
Map<ContactId, Integer> m = connections.get(t);
|
Map<ContactId, Integer> m = connections.get(t);
|
||||||
if(m == null) {
|
if(m == null) {
|
||||||
m = new HashMap<ContactId, Integer>();
|
m = new HashMap<ContactId, Integer>();
|
||||||
@@ -57,6 +62,10 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
|
|||||||
contactCounts.put(c, count + 1);
|
contactCounts.put(c, count + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
if(firstConnection) {
|
if(firstConnection) {
|
||||||
LOG.info("Contact connected");
|
LOG.info("Contact connected");
|
||||||
eventBus.broadcast(new ContactConnectedEvent(c));
|
eventBus.broadcast(new ContactConnectedEvent(c));
|
||||||
@@ -66,7 +75,8 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
|
|||||||
public void unregisterConnection(ContactId c, TransportId t) {
|
public void unregisterConnection(ContactId c, TransportId t) {
|
||||||
LOG.info("Connection unregistered");
|
LOG.info("Connection unregistered");
|
||||||
boolean lastConnection = false;
|
boolean lastConnection = false;
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
Map<ContactId, Integer> m = connections.get(t);
|
Map<ContactId, Integer> m = connections.get(t);
|
||||||
if(m == null) throw new IllegalArgumentException();
|
if(m == null) throw new IllegalArgumentException();
|
||||||
Integer count = m.remove(c);
|
Integer count = m.remove(c);
|
||||||
@@ -85,22 +95,40 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
|
|||||||
contactCounts.put(c, count - 1);
|
contactCounts.put(c, count - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
if(lastConnection) {
|
if(lastConnection) {
|
||||||
LOG.info("Contact disconnected");
|
LOG.info("Contact disconnected");
|
||||||
eventBus.broadcast(new ContactDisconnectedEvent(c));
|
eventBus.broadcast(new ContactDisconnectedEvent(c));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized Collection<ContactId> getConnectedContacts(
|
public Collection<ContactId> getConnectedContacts(
|
||||||
TransportId t) {
|
TransportId t) {
|
||||||
Map<ContactId, Integer> m = connections.get(t);
|
synchLock.lock();
|
||||||
if(m == null) return Collections.emptyList();
|
try{
|
||||||
List<ContactId> ids = new ArrayList<ContactId>(m.keySet());
|
Map<ContactId, Integer> m = connections.get(t);
|
||||||
if(LOG.isLoggable(INFO)) LOG.info(ids.size() + " contacts connected");
|
if(m == null) return Collections.emptyList();
|
||||||
return Collections.unmodifiableList(ids);
|
List<ContactId> ids = new ArrayList<ContactId>(m.keySet());
|
||||||
|
if(LOG.isLoggable(INFO)) LOG.info(ids.size() + " contacts connected");
|
||||||
|
return Collections.unmodifiableList(ids);
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean isConnected(ContactId c) {
|
public boolean isConnected(ContactId c) {
|
||||||
return contactCounts.containsKey(c);
|
synchLock.lock();
|
||||||
|
try{
|
||||||
|
return contactCounts.containsKey(c);
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ import java.util.Comparator;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.locks.Condition;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import org.briarproject.api.reliability.ReadHandler;
|
import org.briarproject.api.reliability.ReadHandler;
|
||||||
import org.briarproject.api.system.Clock;
|
import org.briarproject.api.system.Clock;
|
||||||
@@ -23,6 +27,8 @@ class Receiver implements ReadHandler {
|
|||||||
private long nextSequenceNumber = 1;
|
private long nextSequenceNumber = 1;
|
||||||
|
|
||||||
private volatile boolean valid = true;
|
private volatile boolean valid = true;
|
||||||
|
private Lock synchLock = new ReentrantLock();
|
||||||
|
private Condition dataFrameAvailable = synchLock.newCondition();
|
||||||
|
|
||||||
Receiver(Clock clock, Sender sender) {
|
Receiver(Clock clock, Sender sender) {
|
||||||
this.sender = sender;
|
this.sender = sender;
|
||||||
@@ -30,36 +36,46 @@ class Receiver implements ReadHandler {
|
|||||||
dataFrames = new TreeSet<Data>(new SequenceNumberComparator());
|
dataFrames = new TreeSet<Data>(new SequenceNumberComparator());
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized Data read() throws IOException, InterruptedException {
|
Data read() throws IOException, InterruptedException {
|
||||||
long now = clock.currentTimeMillis(), end = now + READ_TIMEOUT;
|
synchLock.lock();
|
||||||
while(now < end && valid) {
|
try{
|
||||||
if(dataFrames.isEmpty()) {
|
long now = clock.currentTimeMillis(), end = now + READ_TIMEOUT;
|
||||||
// Wait for a data frame
|
while(now < end && valid) {
|
||||||
wait(end - now);
|
if(dataFrames.isEmpty()) {
|
||||||
} else {
|
// Wait for a data frame
|
||||||
Data d = dataFrames.first();
|
dataFrameAvailable.await(end - now, TimeUnit.MILLISECONDS);
|
||||||
if(d.getSequenceNumber() == nextSequenceNumber) {
|
|
||||||
dataFrames.remove(d);
|
|
||||||
// Update the window
|
|
||||||
windowSize += d.getPayloadLength();
|
|
||||||
sender.sendAck(0, windowSize);
|
|
||||||
nextSequenceNumber++;
|
|
||||||
return d;
|
|
||||||
} else {
|
} else {
|
||||||
// Wait for the next in-order data frame
|
Data d = dataFrames.first();
|
||||||
wait(end - now);
|
if(d.getSequenceNumber() == nextSequenceNumber) {
|
||||||
|
dataFrames.remove(d);
|
||||||
|
// Update the window
|
||||||
|
windowSize += d.getPayloadLength();
|
||||||
|
sender.sendAck(0, windowSize);
|
||||||
|
nextSequenceNumber++;
|
||||||
|
return d;
|
||||||
|
} else {
|
||||||
|
// Wait for the next in-order data frame
|
||||||
|
dataFrameAvailable.await(end - now, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
now = clock.currentTimeMillis();
|
||||||
}
|
}
|
||||||
now = clock.currentTimeMillis();
|
if(valid) throw new IOException("Read timed out");
|
||||||
|
throw new IOException("Connection closed");
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
if(valid) throw new IOException("Read timed out");
|
|
||||||
throw new IOException("Connection closed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void invalidate() {
|
void invalidate() {
|
||||||
valid = false;
|
valid = false;
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
notifyAll();
|
try {
|
||||||
|
dataFrameAvailable.signalAll();
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,43 +95,49 @@ class Receiver implements ReadHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void handleData(byte[] b) throws IOException {
|
private void handleData(byte[] b) throws IOException {
|
||||||
if(b.length < Data.MIN_LENGTH || b.length > Data.MAX_LENGTH) {
|
synchLock.lock();
|
||||||
// Ignore data frame with invalid length
|
try{
|
||||||
return;
|
if(b.length < Data.MIN_LENGTH || b.length > Data.MAX_LENGTH) {
|
||||||
}
|
// Ignore data frame with invalid length
|
||||||
Data d = new Data(b);
|
return;
|
||||||
int payloadLength = d.getPayloadLength();
|
|
||||||
if(payloadLength > windowSize) return; // No space in the window
|
|
||||||
if(d.getChecksum() != d.calculateChecksum()) {
|
|
||||||
// Ignore data frame with invalid checksum
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
long sequenceNumber = d.getSequenceNumber();
|
|
||||||
if(sequenceNumber == 0) {
|
|
||||||
// Window probe
|
|
||||||
} else if(sequenceNumber < nextSequenceNumber) {
|
|
||||||
// Duplicate data frame
|
|
||||||
} else if(d.isLastFrame()) {
|
|
||||||
finalSequenceNumber = sequenceNumber;
|
|
||||||
// Remove any data frames with higher sequence numbers
|
|
||||||
Iterator<Data> it = dataFrames.iterator();
|
|
||||||
while(it.hasNext()) {
|
|
||||||
Data d1 = it.next();
|
|
||||||
if(d1.getSequenceNumber() >= finalSequenceNumber) it.remove();
|
|
||||||
}
|
}
|
||||||
if(dataFrames.add(d)) {
|
Data d = new Data(b);
|
||||||
windowSize -= payloadLength;
|
int payloadLength = d.getPayloadLength();
|
||||||
notifyAll();
|
if(payloadLength > windowSize) return; // No space in the window
|
||||||
|
if(d.getChecksum() != d.calculateChecksum()) {
|
||||||
|
// Ignore data frame with invalid checksum
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else if(sequenceNumber < finalSequenceNumber) {
|
long sequenceNumber = d.getSequenceNumber();
|
||||||
if(dataFrames.add(d)) {
|
if(sequenceNumber == 0) {
|
||||||
windowSize -= payloadLength;
|
// Window probe
|
||||||
notifyAll();
|
} else if(sequenceNumber < nextSequenceNumber) {
|
||||||
|
// Duplicate data frame
|
||||||
|
} else if(d.isLastFrame()) {
|
||||||
|
finalSequenceNumber = sequenceNumber;
|
||||||
|
// Remove any data frames with higher sequence numbers
|
||||||
|
Iterator<Data> it = dataFrames.iterator();
|
||||||
|
while(it.hasNext()) {
|
||||||
|
Data d1 = it.next();
|
||||||
|
if(d1.getSequenceNumber() >= finalSequenceNumber) it.remove();
|
||||||
|
}
|
||||||
|
if(dataFrames.add(d)) {
|
||||||
|
windowSize -= payloadLength;
|
||||||
|
dataFrameAvailable.signalAll();
|
||||||
|
}
|
||||||
|
} else if(sequenceNumber < finalSequenceNumber) {
|
||||||
|
if(dataFrames.add(d)) {
|
||||||
|
windowSize -= payloadLength;
|
||||||
|
dataFrameAvailable.signalAll();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// Acknowledge the data frame even if it's a duplicate
|
||||||
|
sender.sendAck(sequenceNumber, windowSize);
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
// Acknowledge the data frame even if it's a duplicate
|
|
||||||
sender.sendAck(sequenceNumber, windowSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SequenceNumberComparator implements Comparator<Data> {
|
private static class SequenceNumberComparator implements Comparator<Data> {
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ import java.util.ArrayList;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.locks.Condition;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import org.briarproject.api.reliability.WriteHandler;
|
import org.briarproject.api.reliability.WriteHandler;
|
||||||
import org.briarproject.api.system.Clock;
|
import org.briarproject.api.system.Clock;
|
||||||
@@ -31,6 +35,9 @@ class Sender {
|
|||||||
private long lastWindowUpdateOrProbe = Long.MAX_VALUE;
|
private long lastWindowUpdateOrProbe = Long.MAX_VALUE;
|
||||||
private boolean dataWaiting = false;
|
private boolean dataWaiting = false;
|
||||||
|
|
||||||
|
private Lock synchLock = new ReentrantLock();
|
||||||
|
private Condition sendWindowAvailable = synchLock.newCondition();
|
||||||
|
|
||||||
Sender(Clock clock, WriteHandler writeHandler) {
|
Sender(Clock clock, WriteHandler writeHandler) {
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
this.writeHandler = writeHandler;
|
this.writeHandler = writeHandler;
|
||||||
@@ -58,7 +65,8 @@ class Sender {
|
|||||||
long sequenceNumber = a.getSequenceNumber();
|
long sequenceNumber = a.getSequenceNumber();
|
||||||
long now = clock.currentTimeMillis();
|
long now = clock.currentTimeMillis();
|
||||||
Outstanding fastRetransmit = null;
|
Outstanding fastRetransmit = null;
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
// Remove the acked data frame if it's outstanding
|
// Remove the acked data frame if it's outstanding
|
||||||
int foundIndex = -1;
|
int foundIndex = -1;
|
||||||
Iterator<Outstanding> it = outstanding.iterator();
|
Iterator<Outstanding> it = outstanding.iterator();
|
||||||
@@ -96,6 +104,9 @@ class Sender {
|
|||||||
// If space has become available, notify any waiting writers
|
// If space has become available, notify any waiting writers
|
||||||
if(windowSize > oldWindowSize || foundIndex != -1) notifyAll();
|
if(windowSize > oldWindowSize || foundIndex != -1) notifyAll();
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
// Fast retransmission
|
// Fast retransmission
|
||||||
if(fastRetransmit != null)
|
if(fastRetransmit != null)
|
||||||
writeHandler.handleWrite(fastRetransmit.data.getBuffer());
|
writeHandler.handleWrite(fastRetransmit.data.getBuffer());
|
||||||
@@ -105,7 +116,8 @@ class Sender {
|
|||||||
long now = clock.currentTimeMillis();
|
long now = clock.currentTimeMillis();
|
||||||
List<Outstanding> retransmit = null;
|
List<Outstanding> retransmit = null;
|
||||||
boolean sendProbe = false;
|
boolean sendProbe = false;
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
if(outstanding.isEmpty()) {
|
if(outstanding.isEmpty()) {
|
||||||
if(dataWaiting && now - lastWindowUpdateOrProbe > rto) {
|
if(dataWaiting && now - lastWindowUpdateOrProbe > rto) {
|
||||||
sendProbe = true;
|
sendProbe = true;
|
||||||
@@ -135,6 +147,9 @@ class Sender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
// Send a window probe if necessary
|
// Send a window probe if necessary
|
||||||
if(sendProbe) {
|
if(sendProbe) {
|
||||||
byte[] buf = new byte[Data.MIN_LENGTH];
|
byte[] buf = new byte[Data.MIN_LENGTH];
|
||||||
@@ -151,12 +166,13 @@ class Sender {
|
|||||||
|
|
||||||
void write(Data d) throws IOException, InterruptedException {
|
void write(Data d) throws IOException, InterruptedException {
|
||||||
int payloadLength = d.getPayloadLength();
|
int payloadLength = d.getPayloadLength();
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
// Wait for space in the window
|
// Wait for space in the window
|
||||||
long now = clock.currentTimeMillis(), end = now + WRITE_TIMEOUT;
|
long now = clock.currentTimeMillis(), end = now + WRITE_TIMEOUT;
|
||||||
while(now < end && outstandingBytes + payloadLength >= windowSize) {
|
while(now < end && outstandingBytes + payloadLength >= windowSize) {
|
||||||
dataWaiting = true;
|
dataWaiting = true;
|
||||||
wait(end - now);
|
sendWindowAvailable.await(end - now, TimeUnit.MILLISECONDS);
|
||||||
now = clock.currentTimeMillis();
|
now = clock.currentTimeMillis();
|
||||||
}
|
}
|
||||||
if(outstandingBytes + payloadLength >= windowSize)
|
if(outstandingBytes + payloadLength >= windowSize)
|
||||||
@@ -165,11 +181,20 @@ class Sender {
|
|||||||
outstandingBytes += payloadLength;
|
outstandingBytes += payloadLength;
|
||||||
dataWaiting = false;
|
dataWaiting = false;
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
writeHandler.handleWrite(d.getBuffer());
|
writeHandler.handleWrite(d.getBuffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void flush() throws IOException, InterruptedException {
|
void flush() throws IOException, InterruptedException {
|
||||||
while(dataWaiting || !outstanding.isEmpty()) wait();
|
synchLock.lock();
|
||||||
|
try{
|
||||||
|
while(dataWaiting || !outstanding.isEmpty()) sendWindowAvailable.await();
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Outstanding {
|
private static class Outstanding {
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import java.util.Iterator;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -56,6 +58,8 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
|
|||||||
private final Map<EndpointKey, TemporarySecret> currentSecrets;
|
private final Map<EndpointKey, TemporarySecret> currentSecrets;
|
||||||
private final Map<EndpointKey, TemporarySecret> newSecrets;
|
private final Map<EndpointKey, TemporarySecret> newSecrets;
|
||||||
|
|
||||||
|
private final Lock synchLock = new ReentrantLock();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
KeyManagerImpl(CryptoComponent crypto, DatabaseComponent db,
|
KeyManagerImpl(CryptoComponent crypto, DatabaseComponent db,
|
||||||
EventBus eventBus, TagRecogniser tagRecogniser, Clock clock,
|
EventBus eventBus, TagRecogniser tagRecogniser, Clock clock,
|
||||||
@@ -72,41 +76,47 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
|
|||||||
newSecrets = new HashMap<EndpointKey, TemporarySecret>();
|
newSecrets = new HashMap<EndpointKey, TemporarySecret>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean start() {
|
public boolean start() {
|
||||||
eventBus.addListener(this);
|
synchLock.lock();
|
||||||
// Load the temporary secrets and transport latencies from the database
|
|
||||||
Collection<TemporarySecret> secrets;
|
|
||||||
try {
|
try {
|
||||||
secrets = db.getSecrets();
|
eventBus.addListener(this);
|
||||||
maxLatencies.putAll(db.getTransportLatencies());
|
// Load the temporary secrets and transport latencies from the database
|
||||||
} catch(DbException e) {
|
Collection<TemporarySecret> secrets;
|
||||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Work out what phase of its lifecycle each secret is in
|
|
||||||
long now = clock.currentTimeMillis();
|
|
||||||
Collection<TemporarySecret> dead = assignSecretsToMaps(now, secrets);
|
|
||||||
// Replace any dead secrets
|
|
||||||
Collection<TemporarySecret> created = replaceDeadSecrets(now, dead);
|
|
||||||
if(!created.isEmpty()) {
|
|
||||||
// Store any secrets that have been created, removing any dead ones
|
|
||||||
try {
|
try {
|
||||||
db.addSecrets(created);
|
secrets = db.getSecrets();
|
||||||
|
maxLatencies.putAll(db.getTransportLatencies());
|
||||||
} catch(DbException e) {
|
} catch(DbException e) {
|
||||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Work out what phase of its lifecycle each secret is in
|
||||||
|
long now = clock.currentTimeMillis();
|
||||||
|
Collection<TemporarySecret> dead = assignSecretsToMaps(now, secrets);
|
||||||
|
// Replace any dead secrets
|
||||||
|
Collection<TemporarySecret> created = replaceDeadSecrets(now, dead);
|
||||||
|
if(!created.isEmpty()) {
|
||||||
|
// Store any secrets that have been created, removing any dead ones
|
||||||
|
try {
|
||||||
|
db.addSecrets(created);
|
||||||
|
} catch(DbException e) {
|
||||||
|
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Pass the old, current and new secrets to the recogniser
|
||||||
|
for(TemporarySecret s : oldSecrets.values())
|
||||||
|
tagRecogniser.addSecret(s);
|
||||||
|
for(TemporarySecret s : currentSecrets.values())
|
||||||
|
tagRecogniser.addSecret(s);
|
||||||
|
for(TemporarySecret s : newSecrets.values())
|
||||||
|
tagRecogniser.addSecret(s);
|
||||||
|
// Schedule periodic key rotation
|
||||||
|
timer.scheduleAtFixedRate(this, MS_BETWEEN_CHECKS, MS_BETWEEN_CHECKS);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
// Pass the old, current and new secrets to the recogniser
|
|
||||||
for(TemporarySecret s : oldSecrets.values())
|
|
||||||
tagRecogniser.addSecret(s);
|
|
||||||
for(TemporarySecret s : currentSecrets.values())
|
|
||||||
tagRecogniser.addSecret(s);
|
|
||||||
for(TemporarySecret s : newSecrets.values())
|
|
||||||
tagRecogniser.addSecret(s);
|
|
||||||
// Schedule periodic key rotation
|
|
||||||
timer.scheduleAtFixedRate(this, MS_BETWEEN_CHECKS, MS_BETWEEN_CHECKS);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assigns secrets to the appropriate maps and returns any dead secrets
|
// Assigns secrets to the appropriate maps and returns any dead secrets
|
||||||
@@ -215,15 +225,21 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
|
|||||||
return created;
|
return created;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean stop() {
|
public boolean stop() {
|
||||||
eventBus.removeListener(this);
|
synchLock.lock();
|
||||||
timer.cancel();
|
try{
|
||||||
tagRecogniser.removeSecrets();
|
eventBus.removeListener(this);
|
||||||
maxLatencies.clear();
|
timer.cancel();
|
||||||
removeAndEraseSecrets(oldSecrets);
|
tagRecogniser.removeSecrets();
|
||||||
removeAndEraseSecrets(currentSecrets);
|
maxLatencies.clear();
|
||||||
removeAndEraseSecrets(newSecrets);
|
removeAndEraseSecrets(oldSecrets);
|
||||||
return true;
|
removeAndEraseSecrets(currentSecrets);
|
||||||
|
removeAndEraseSecrets(newSecrets);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locking: this
|
// Locking: this
|
||||||
@@ -232,98 +248,116 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
|
|||||||
m.clear();
|
m.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized StreamContext getStreamContext(ContactId c,
|
public StreamContext getStreamContext(ContactId c,
|
||||||
TransportId t) {
|
TransportId t) {
|
||||||
TemporarySecret s = currentSecrets.get(new EndpointKey(c, t));
|
synchLock.lock();
|
||||||
if(s == null) {
|
try{
|
||||||
LOG.info("No secret for endpoint");
|
TemporarySecret s = currentSecrets.get(new EndpointKey(c, t));
|
||||||
return null;
|
if(s == null) {
|
||||||
}
|
LOG.info("No secret for endpoint");
|
||||||
long streamNumber;
|
|
||||||
try {
|
|
||||||
streamNumber = db.incrementStreamCounter(c, t, s.getPeriod());
|
|
||||||
if(streamNumber == -1) {
|
|
||||||
LOG.info("No counter for period");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} catch(DbException e) {
|
long streamNumber;
|
||||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
try {
|
||||||
return null;
|
streamNumber = db.incrementStreamCounter(c, t, s.getPeriod());
|
||||||
|
if(streamNumber == -1) {
|
||||||
|
LOG.info("No counter for period");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch(DbException e) {
|
||||||
|
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Clone the secret - the original will be erased
|
||||||
|
byte[] secret = s.getSecret().clone();
|
||||||
|
return new StreamContext(c, t, secret, streamNumber, s.getAlice());
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
// Clone the secret - the original will be erased
|
|
||||||
byte[] secret = s.getSecret().clone();
|
|
||||||
return new StreamContext(c, t, secret, streamNumber, s.getAlice());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void endpointAdded(Endpoint ep, long maxLatency,
|
public void endpointAdded(Endpoint ep, long maxLatency,
|
||||||
byte[] initialSecret) {
|
byte[] initialSecret) {
|
||||||
maxLatencies.put(ep.getTransportId(), maxLatency);
|
synchLock.lock();
|
||||||
// Work out which rotation period we're in
|
try{
|
||||||
long elapsed = clock.currentTimeMillis() - ep.getEpoch();
|
maxLatencies.put(ep.getTransportId(), maxLatency);
|
||||||
long rotation = maxLatency + MAX_CLOCK_DIFFERENCE;
|
// Work out which rotation period we're in
|
||||||
long period = (elapsed / rotation) + 1;
|
long elapsed = clock.currentTimeMillis() - ep.getEpoch();
|
||||||
if(period < 1) throw new IllegalStateException();
|
long rotation = maxLatency + MAX_CLOCK_DIFFERENCE;
|
||||||
// Derive the old, current and new secrets
|
long period = (elapsed / rotation) + 1;
|
||||||
byte[] b1 = initialSecret;
|
if(period < 1) throw new IllegalStateException();
|
||||||
for(long p = 0; p < period; p++) {
|
// Derive the old, current and new secrets
|
||||||
byte[] temp = crypto.deriveNextSecret(b1, p);
|
byte[] b1 = initialSecret;
|
||||||
ByteUtils.erase(b1);
|
for(long p = 0; p < period; p++) {
|
||||||
b1 = temp;
|
byte[] temp = crypto.deriveNextSecret(b1, p);
|
||||||
|
ByteUtils.erase(b1);
|
||||||
|
b1 = temp;
|
||||||
|
}
|
||||||
|
byte[] b2 = crypto.deriveNextSecret(b1, period);
|
||||||
|
byte[] b3 = crypto.deriveNextSecret(b2, period + 1);
|
||||||
|
TemporarySecret s1 = new TemporarySecret(ep, period - 1, b1);
|
||||||
|
TemporarySecret s2 = new TemporarySecret(ep, period, b2);
|
||||||
|
TemporarySecret s3 = new TemporarySecret(ep, period + 1, b3);
|
||||||
|
// Add the incoming secrets to their respective maps
|
||||||
|
EndpointKey k = new EndpointKey(ep);
|
||||||
|
oldSecrets.put(k, s1);
|
||||||
|
currentSecrets.put(k, s2);
|
||||||
|
newSecrets.put(k, s3);
|
||||||
|
// Store the new secrets
|
||||||
|
try {
|
||||||
|
db.addSecrets(Arrays.asList(s1, s2, s3));
|
||||||
|
} catch(DbException e) {
|
||||||
|
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Pass the new secrets to the recogniser
|
||||||
|
tagRecogniser.addSecret(s1);
|
||||||
|
tagRecogniser.addSecret(s2);
|
||||||
|
tagRecogniser.addSecret(s3);
|
||||||
}
|
}
|
||||||
byte[] b2 = crypto.deriveNextSecret(b1, period);
|
finally{
|
||||||
byte[] b3 = crypto.deriveNextSecret(b2, period + 1);
|
synchLock.unlock();
|
||||||
TemporarySecret s1 = new TemporarySecret(ep, period - 1, b1);
|
|
||||||
TemporarySecret s2 = new TemporarySecret(ep, period, b2);
|
|
||||||
TemporarySecret s3 = new TemporarySecret(ep, period + 1, b3);
|
|
||||||
// Add the incoming secrets to their respective maps
|
|
||||||
EndpointKey k = new EndpointKey(ep);
|
|
||||||
oldSecrets.put(k, s1);
|
|
||||||
currentSecrets.put(k, s2);
|
|
||||||
newSecrets.put(k, s3);
|
|
||||||
// Store the new secrets
|
|
||||||
try {
|
|
||||||
db.addSecrets(Arrays.asList(s1, s2, s3));
|
|
||||||
} catch(DbException e) {
|
|
||||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// Pass the new secrets to the recogniser
|
|
||||||
tagRecogniser.addSecret(s1);
|
|
||||||
tagRecogniser.addSecret(s2);
|
|
||||||
tagRecogniser.addSecret(s3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void run() {
|
public void run() {
|
||||||
|
synchLock.lock();
|
||||||
|
try{
|
||||||
// Rebuild the maps because we may be running a whole period late
|
// Rebuild the maps because we may be running a whole period late
|
||||||
Collection<TemporarySecret> secrets = new ArrayList<TemporarySecret>();
|
Collection<TemporarySecret> secrets = new ArrayList<TemporarySecret>();
|
||||||
secrets.addAll(oldSecrets.values());
|
secrets.addAll(oldSecrets.values());
|
||||||
secrets.addAll(currentSecrets.values());
|
secrets.addAll(currentSecrets.values());
|
||||||
secrets.addAll(newSecrets.values());
|
secrets.addAll(newSecrets.values());
|
||||||
oldSecrets.clear();
|
oldSecrets.clear();
|
||||||
currentSecrets.clear();
|
currentSecrets.clear();
|
||||||
newSecrets.clear();
|
newSecrets.clear();
|
||||||
// Work out what phase of its lifecycle each secret is in
|
// Work out what phase of its lifecycle each secret is in
|
||||||
long now = clock.currentTimeMillis();
|
long now = clock.currentTimeMillis();
|
||||||
Collection<TemporarySecret> dead = assignSecretsToMaps(now, secrets);
|
Collection<TemporarySecret> dead = assignSecretsToMaps(now, secrets);
|
||||||
// Remove any dead secrets from the recogniser
|
// Remove any dead secrets from the recogniser
|
||||||
for(TemporarySecret s : dead) {
|
for(TemporarySecret s : dead) {
|
||||||
ContactId c = s.getContactId();
|
ContactId c = s.getContactId();
|
||||||
TransportId t = s.getTransportId();
|
TransportId t = s.getTransportId();
|
||||||
long period = s.getPeriod();
|
long period = s.getPeriod();
|
||||||
tagRecogniser.removeSecret(c, t, period);
|
tagRecogniser.removeSecret(c, t, period);
|
||||||
}
|
|
||||||
// Replace any dead secrets
|
|
||||||
Collection<TemporarySecret> created = replaceDeadSecrets(now, dead);
|
|
||||||
if(!created.isEmpty()) {
|
|
||||||
// Store any secrets that have been created
|
|
||||||
try {
|
|
||||||
db.addSecrets(created);
|
|
||||||
} catch(DbException e) {
|
|
||||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
}
|
}
|
||||||
// Pass any secrets that have been created to the recogniser
|
// Replace any dead secrets
|
||||||
for(TemporarySecret s : created) tagRecogniser.addSecret(s);
|
Collection<TemporarySecret> created = replaceDeadSecrets(now, dead);
|
||||||
|
if(!created.isEmpty()) {
|
||||||
|
// Store any secrets that have been created
|
||||||
|
try {
|
||||||
|
db.addSecrets(created);
|
||||||
|
} catch(DbException e) {
|
||||||
|
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
|
}
|
||||||
|
// Pass any secrets that have been created to the recogniser
|
||||||
|
for(TemporarySecret s : created) tagRecogniser.addSecret(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,11 +441,15 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
|
|||||||
public void run() {
|
public void run() {
|
||||||
ContactId c = event.getContactId();
|
ContactId c = event.getContactId();
|
||||||
tagRecogniser.removeSecrets(c);
|
tagRecogniser.removeSecrets(c);
|
||||||
synchronized(KeyManagerImpl.this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
removeAndEraseSecrets(c, oldSecrets);
|
removeAndEraseSecrets(c, oldSecrets);
|
||||||
removeAndEraseSecrets(c, currentSecrets);
|
removeAndEraseSecrets(c, currentSecrets);
|
||||||
removeAndEraseSecrets(c, newSecrets);
|
removeAndEraseSecrets(c, newSecrets);
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,9 +463,13 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
synchronized(KeyManagerImpl.this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
maxLatencies.put(event.getTransportId(), event.getMaxLatency());
|
maxLatencies.put(event.getTransportId(), event.getMaxLatency());
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,12 +485,16 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
|
|||||||
public void run() {
|
public void run() {
|
||||||
TransportId t = event.getTransportId();
|
TransportId t = event.getTransportId();
|
||||||
tagRecogniser.removeSecrets(t);
|
tagRecogniser.removeSecrets(t);
|
||||||
synchronized(KeyManagerImpl.this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
maxLatencies.remove(t);
|
maxLatencies.remove(t);
|
||||||
removeAndEraseSecrets(t, oldSecrets);
|
removeAndEraseSecrets(t, oldSecrets);
|
||||||
removeAndEraseSecrets(t, currentSecrets);
|
removeAndEraseSecrets(t, currentSecrets);
|
||||||
removeAndEraseSecrets(t, newSecrets);
|
removeAndEraseSecrets(t, newSecrets);
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package org.briarproject.transport;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@@ -20,6 +22,9 @@ class TagRecogniserImpl implements TagRecogniser {
|
|||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
// Locking: this
|
// Locking: this
|
||||||
private final Map<TransportId, TransportTagRecogniser> recognisers;
|
private final Map<TransportId, TransportTagRecogniser> recognisers;
|
||||||
|
|
||||||
|
private final Lock synchLock = new ReentrantLock();
|
||||||
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
TagRecogniserImpl(CryptoComponent crypto, DatabaseComponent db) {
|
TagRecogniserImpl(CryptoComponent crypto, DatabaseComponent db) {
|
||||||
@@ -31,9 +36,13 @@ class TagRecogniserImpl implements TagRecogniser {
|
|||||||
public StreamContext recogniseTag(TransportId t, byte[] tag)
|
public StreamContext recogniseTag(TransportId t, byte[] tag)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
TransportTagRecogniser r;
|
TransportTagRecogniser r;
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
r = recognisers.get(t);
|
r = recognisers.get(t);
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
if(r == null) return null;
|
if(r == null) return null;
|
||||||
return r.recogniseTag(tag);
|
return r.recogniseTag(tag);
|
||||||
}
|
}
|
||||||
@@ -41,35 +50,63 @@ class TagRecogniserImpl implements TagRecogniser {
|
|||||||
public void addSecret(TemporarySecret s) {
|
public void addSecret(TemporarySecret s) {
|
||||||
TransportId t = s.getTransportId();
|
TransportId t = s.getTransportId();
|
||||||
TransportTagRecogniser r;
|
TransportTagRecogniser r;
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
r = recognisers.get(t);
|
r = recognisers.get(t);
|
||||||
if(r == null) {
|
if(r == null) {
|
||||||
r = new TransportTagRecogniser(crypto, db, t);
|
r = new TransportTagRecogniser(crypto, db, t);
|
||||||
recognisers.put(t, r);
|
recognisers.put(t, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
r.addSecret(s);
|
r.addSecret(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeSecret(ContactId c, TransportId t, long period) {
|
public void removeSecret(ContactId c, TransportId t, long period) {
|
||||||
TransportTagRecogniser r;
|
TransportTagRecogniser r;
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
r = recognisers.get(t);
|
r = recognisers.get(t);
|
||||||
}
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
if(r != null) r.removeSecret(c, period);
|
if(r != null) r.removeSecret(c, period);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void removeSecrets(ContactId c) {
|
public void removeSecrets(ContactId c) {
|
||||||
for(TransportTagRecogniser r : recognisers.values())
|
synchLock.lock();
|
||||||
r.removeSecrets(c);
|
try{
|
||||||
|
for(TransportTagRecogniser r : recognisers.values())
|
||||||
|
r.removeSecrets(c);
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void removeSecrets(TransportId t) {
|
public void removeSecrets(TransportId t) {
|
||||||
recognisers.remove(t);
|
synchLock.lock();
|
||||||
|
try{
|
||||||
|
recognisers.remove(t);
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void removeSecrets() {
|
public void removeSecrets() {
|
||||||
for(TransportTagRecogniser r : recognisers.values())
|
synchLock.lock();
|
||||||
r.removeSecrets();
|
try{
|
||||||
|
for(TransportTagRecogniser r : recognisers.values())
|
||||||
|
r.removeSecrets();
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import org.briarproject.api.Bytes;
|
import org.briarproject.api.Bytes;
|
||||||
import org.briarproject.api.ContactId;
|
import org.briarproject.api.ContactId;
|
||||||
@@ -30,6 +32,8 @@ class TransportTagRecogniser {
|
|||||||
private final Map<Bytes, TagContext> tagMap; // Locking: this
|
private final Map<Bytes, TagContext> tagMap; // Locking: this
|
||||||
private final Map<RemovalKey, RemovalContext> removalMap; // Locking: this
|
private final Map<RemovalKey, RemovalContext> removalMap; // Locking: this
|
||||||
|
|
||||||
|
private final Lock synchLock = new ReentrantLock();
|
||||||
|
|
||||||
TransportTagRecogniser(CryptoComponent crypto, DatabaseComponent db,
|
TransportTagRecogniser(CryptoComponent crypto, DatabaseComponent db,
|
||||||
TransportId transportId) {
|
TransportId transportId) {
|
||||||
this.crypto = crypto;
|
this.crypto = crypto;
|
||||||
@@ -39,62 +43,80 @@ class TransportTagRecogniser {
|
|||||||
removalMap = new HashMap<RemovalKey, RemovalContext>();
|
removalMap = new HashMap<RemovalKey, RemovalContext>();
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized StreamContext recogniseTag(byte[] tag) throws DbException {
|
StreamContext recogniseTag(byte[] tag) throws DbException {
|
||||||
TagContext t = tagMap.remove(new Bytes(tag));
|
synchLock.lock();
|
||||||
if(t == null) return null; // The tag was not expected
|
try{
|
||||||
// Update the reordering window and the expected tags
|
TagContext t = tagMap.remove(new Bytes(tag));
|
||||||
SecretKey key = crypto.deriveTagKey(t.secret, !t.alice);
|
if(t == null) return null; // The tag was not expected
|
||||||
for(long streamNumber : t.window.setSeen(t.streamNumber)) {
|
// Update the reordering window and the expected tags
|
||||||
byte[] tag1 = new byte[TAG_LENGTH];
|
SecretKey key = crypto.deriveTagKey(t.secret, !t.alice);
|
||||||
crypto.encodeTag(tag1, key, streamNumber);
|
for(long streamNumber : t.window.setSeen(t.streamNumber)) {
|
||||||
if(streamNumber < t.streamNumber) {
|
byte[] tag1 = new byte[TAG_LENGTH];
|
||||||
TagContext removed = tagMap.remove(new Bytes(tag1));
|
crypto.encodeTag(tag1, key, streamNumber);
|
||||||
assert removed != null;
|
if(streamNumber < t.streamNumber) {
|
||||||
} else {
|
TagContext removed = tagMap.remove(new Bytes(tag1));
|
||||||
TagContext added = new TagContext(t, streamNumber);
|
assert removed != null;
|
||||||
TagContext duplicate = tagMap.put(new Bytes(tag1), added);
|
} else {
|
||||||
|
TagContext added = new TagContext(t, streamNumber);
|
||||||
|
TagContext duplicate = tagMap.put(new Bytes(tag1), added);
|
||||||
|
assert duplicate == null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
key.erase();
|
||||||
|
// Store the updated reordering window in the DB
|
||||||
|
db.setReorderingWindow(t.contactId, transportId, t.period,
|
||||||
|
t.window.getCentre(), t.window.getBitmap());
|
||||||
|
// Clone the secret - the key manager will erase the original
|
||||||
|
byte[] secret = t.secret.clone();
|
||||||
|
return new StreamContext(t.contactId, transportId, secret,
|
||||||
|
t.streamNumber, t.alice);
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addSecret(TemporarySecret s) {
|
||||||
|
synchLock.lock();
|
||||||
|
try{
|
||||||
|
ContactId contactId = s.getContactId();
|
||||||
|
boolean alice = s.getAlice();
|
||||||
|
long period = s.getPeriod();
|
||||||
|
byte[] secret = s.getSecret();
|
||||||
|
long centre = s.getWindowCentre();
|
||||||
|
byte[] bitmap = s.getWindowBitmap();
|
||||||
|
// Create the reordering window and the expected tags
|
||||||
|
SecretKey key = crypto.deriveTagKey(secret, !alice);
|
||||||
|
ReorderingWindow window = new ReorderingWindow(centre, bitmap);
|
||||||
|
for(long streamNumber : window.getUnseen()) {
|
||||||
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
|
crypto.encodeTag(tag, key, streamNumber);
|
||||||
|
TagContext added = new TagContext(contactId, alice, period,
|
||||||
|
secret, window, streamNumber);
|
||||||
|
TagContext duplicate = tagMap.put(new Bytes(tag), added);
|
||||||
assert duplicate == null;
|
assert duplicate == null;
|
||||||
}
|
}
|
||||||
|
key.erase();
|
||||||
|
// Create a removal context to remove the window and the tags later
|
||||||
|
RemovalContext r = new RemovalContext(window, secret, alice);
|
||||||
|
removalMap.put(new RemovalKey(contactId, period), r);
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
key.erase();
|
|
||||||
// Store the updated reordering window in the DB
|
|
||||||
db.setReorderingWindow(t.contactId, transportId, t.period,
|
|
||||||
t.window.getCentre(), t.window.getBitmap());
|
|
||||||
// Clone the secret - the key manager will erase the original
|
|
||||||
byte[] secret = t.secret.clone();
|
|
||||||
return new StreamContext(t.contactId, transportId, secret,
|
|
||||||
t.streamNumber, t.alice);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void addSecret(TemporarySecret s) {
|
void removeSecret(ContactId contactId, long period) {
|
||||||
ContactId contactId = s.getContactId();
|
synchLock.lock();
|
||||||
boolean alice = s.getAlice();
|
try{
|
||||||
long period = s.getPeriod();
|
RemovalKey k = new RemovalKey(contactId, period);
|
||||||
byte[] secret = s.getSecret();
|
RemovalContext removed = removalMap.remove(k);
|
||||||
long centre = s.getWindowCentre();
|
if(removed == null) throw new IllegalArgumentException();
|
||||||
byte[] bitmap = s.getWindowBitmap();
|
removeSecret(removed);
|
||||||
// Create the reordering window and the expected tags
|
}
|
||||||
SecretKey key = crypto.deriveTagKey(secret, !alice);
|
finally{
|
||||||
ReorderingWindow window = new ReorderingWindow(centre, bitmap);
|
synchLock.unlock();
|
||||||
for(long streamNumber : window.getUnseen()) {
|
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
|
||||||
crypto.encodeTag(tag, key, streamNumber);
|
|
||||||
TagContext added = new TagContext(contactId, alice, period,
|
|
||||||
secret, window, streamNumber);
|
|
||||||
TagContext duplicate = tagMap.put(new Bytes(tag), added);
|
|
||||||
assert duplicate == null;
|
|
||||||
}
|
}
|
||||||
key.erase();
|
|
||||||
// Create a removal context to remove the window and the tags later
|
|
||||||
RemovalContext r = new RemovalContext(window, secret, alice);
|
|
||||||
removalMap.put(new RemovalKey(contactId, period), r);
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized void removeSecret(ContactId contactId, long period) {
|
|
||||||
RemovalKey k = new RemovalKey(contactId, period);
|
|
||||||
RemovalContext removed = removalMap.remove(k);
|
|
||||||
if(removed == null) throw new IllegalArgumentException();
|
|
||||||
removeSecret(removed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locking: this
|
// Locking: this
|
||||||
@@ -110,17 +132,29 @@ class TransportTagRecogniser {
|
|||||||
key.erase();
|
key.erase();
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void removeSecrets(ContactId c) {
|
void removeSecrets(ContactId c) {
|
||||||
Collection<RemovalKey> keysToRemove = new ArrayList<RemovalKey>();
|
synchLock.lock();
|
||||||
for(RemovalKey k : removalMap.keySet())
|
try{
|
||||||
if(k.contactId.equals(c)) keysToRemove.add(k);
|
Collection<RemovalKey> keysToRemove = new ArrayList<RemovalKey>();
|
||||||
for(RemovalKey k : keysToRemove) removeSecret(k.contactId, k.period);
|
for(RemovalKey k : removalMap.keySet())
|
||||||
|
if(k.contactId.equals(c)) keysToRemove.add(k);
|
||||||
|
for(RemovalKey k : keysToRemove) removeSecret(k.contactId, k.period);
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void removeSecrets() {
|
void removeSecrets() {
|
||||||
for(RemovalContext r : removalMap.values()) removeSecret(r);
|
synchLock.lock();
|
||||||
assert tagMap.isEmpty();
|
try{
|
||||||
removalMap.clear();
|
for(RemovalContext r : removalMap.values()) removeSecret(r);
|
||||||
|
assert tagMap.isEmpty();
|
||||||
|
removalMap.clear();
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class TagContext {
|
private static class TagContext {
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import java.util.Collections;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.briarproject.util.OsUtils;
|
import org.briarproject.util.OsUtils;
|
||||||
@@ -38,6 +40,9 @@ class WindowsShutdownManagerImpl extends ShutdownManagerImpl {
|
|||||||
private final Map<String, Object> options;
|
private final Map<String, Object> options;
|
||||||
|
|
||||||
private boolean initialised = false; // Locking: this
|
private boolean initialised = false; // Locking: this
|
||||||
|
|
||||||
|
private final Lock synchLock = new ReentrantLock();
|
||||||
|
|
||||||
|
|
||||||
WindowsShutdownManagerImpl() {
|
WindowsShutdownManagerImpl() {
|
||||||
// Use the Unicode versions of Win32 API calls
|
// Use the Unicode versions of Win32 API calls
|
||||||
@@ -48,9 +53,15 @@ class WindowsShutdownManagerImpl extends ShutdownManagerImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized int addShutdownHook(Runnable r) {
|
public int addShutdownHook(Runnable r) {
|
||||||
if(!initialised) initialise();
|
synchLock.lock();
|
||||||
return super.addShutdownHook(r);
|
try {
|
||||||
|
if(!initialised) initialise();
|
||||||
|
return super.addShutdownHook(r);
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -69,20 +80,26 @@ class WindowsShutdownManagerImpl extends ShutdownManagerImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Package access for testing
|
// Package access for testing
|
||||||
synchronized void runShutdownHooks() {
|
void runShutdownHooks() {
|
||||||
boolean interrupted = false;
|
synchLock.lock();
|
||||||
// Start each hook in its own thread
|
try {
|
||||||
for(Thread hook : hooks.values()) hook.start();
|
boolean interrupted = false;
|
||||||
// Wait for all the hooks to finish
|
// Start each hook in its own thread
|
||||||
for(Thread hook : hooks.values()) {
|
for(Thread hook : hooks.values()) hook.start();
|
||||||
try {
|
// Wait for all the hooks to finish
|
||||||
hook.join();
|
for(Thread hook : hooks.values()) {
|
||||||
} catch(InterruptedException e) {
|
try {
|
||||||
LOG.warning("Interrupted while running shutdown hooks");
|
hook.join();
|
||||||
interrupted = true;
|
} catch(InterruptedException e) {
|
||||||
|
LOG.warning("Interrupted while running shutdown hooks");
|
||||||
|
interrupted = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if(interrupted) Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
if(interrupted) Thread.currentThread().interrupt();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class EventLoop extends Thread {
|
private class EventLoop extends Thread {
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.locks.Condition;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable {
|
class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable {
|
||||||
@@ -14,11 +18,14 @@ class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable {
|
|||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
private final RemovableDriveFinder finder;
|
private final RemovableDriveFinder finder;
|
||||||
private final long pollingInterval;
|
private final long pollingInterval;
|
||||||
private final Object pollingLock = new Object();
|
|
||||||
|
|
||||||
private volatile boolean running = false;
|
private volatile boolean running = false;
|
||||||
private volatile Callback callback = null;
|
private volatile Callback callback = null;
|
||||||
|
|
||||||
|
private final Lock synchLock = new ReentrantLock();
|
||||||
|
private final Condition stopPolling = synchLock.newCondition();
|
||||||
|
|
||||||
|
|
||||||
public PollingRemovableDriveMonitor(Executor ioExecutor,
|
public PollingRemovableDriveMonitor(Executor ioExecutor,
|
||||||
RemovableDriveFinder finder, long pollingInterval) {
|
RemovableDriveFinder finder, long pollingInterval) {
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
@@ -34,8 +41,12 @@ class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable {
|
|||||||
|
|
||||||
public void stop() throws IOException {
|
public void stop() throws IOException {
|
||||||
running = false;
|
running = false;
|
||||||
synchronized(pollingLock) {
|
synchLock.lock();
|
||||||
pollingLock.notifyAll();
|
try {
|
||||||
|
stopPolling.signalAll();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,8 +54,12 @@ class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable {
|
|||||||
try {
|
try {
|
||||||
Collection<File> drives = finder.findRemovableDrives();
|
Collection<File> drives = finder.findRemovableDrives();
|
||||||
while(running) {
|
while(running) {
|
||||||
synchronized(pollingLock) {
|
synchLock.lock();
|
||||||
pollingLock.wait(pollingInterval);
|
try {
|
||||||
|
stopPolling.await(pollingInterval, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
if(!running) return;
|
if(!running) return;
|
||||||
Collection<File> newDrives = finder.findRemovableDrives();
|
Collection<File> newDrives = finder.findRemovableDrives();
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.locks.Condition;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import net.contentobjects.jnotify.JNotify;
|
import net.contentobjects.jnotify.JNotify;
|
||||||
import net.contentobjects.jnotify.JNotifyListener;
|
import net.contentobjects.jnotify.JNotifyListener;
|
||||||
@@ -21,7 +24,11 @@ JNotifyListener {
|
|||||||
private Callback callback = null; // Locking: this
|
private Callback callback = null; // Locking: this
|
||||||
|
|
||||||
protected abstract String[] getPathsToWatch();
|
protected abstract String[] getPathsToWatch();
|
||||||
|
|
||||||
|
//TODO: rationalise this in a further refactor
|
||||||
|
private final Lock synchLock = new ReentrantLock();
|
||||||
|
private static final Lock staticSynchLock = new ReentrantLock();
|
||||||
|
|
||||||
private static Throwable tryLoad() {
|
private static Throwable tryLoad() {
|
||||||
try {
|
try {
|
||||||
Class.forName("net.contentobjects.jnotify.JNotify");
|
Class.forName("net.contentobjects.jnotify.JNotify");
|
||||||
@@ -33,12 +40,18 @@ JNotifyListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized void checkEnabled() throws IOException {
|
public static void checkEnabled() throws IOException {
|
||||||
if(!triedLoad) {
|
staticSynchLock.lock();
|
||||||
loadError = tryLoad();
|
try {
|
||||||
triedLoad = true;
|
if(!triedLoad) {
|
||||||
|
loadError = tryLoad();
|
||||||
|
triedLoad = true;
|
||||||
|
}
|
||||||
|
if(loadError != null) throw new IOException(loadError.toString());
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
staticSynchLock.unlock();
|
||||||
}
|
}
|
||||||
if(loadError != null) throw new IOException(loadError.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start(Callback callback) throws IOException {
|
public void start(Callback callback) throws IOException {
|
||||||
@@ -49,34 +62,46 @@ JNotifyListener {
|
|||||||
if(new File(path).exists())
|
if(new File(path).exists())
|
||||||
watches.add(JNotify.addWatch(path, mask, false, this));
|
watches.add(JNotify.addWatch(path, mask, false, this));
|
||||||
}
|
}
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
assert !started;
|
try {
|
||||||
assert this.callback == null;
|
assert !started;
|
||||||
started = true;
|
assert this.callback == null;
|
||||||
this.callback = callback;
|
started = true;
|
||||||
this.watches.addAll(watches);
|
this.callback = callback;
|
||||||
}
|
this.watches.addAll(watches);
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stop() throws IOException {
|
public void stop() throws IOException {
|
||||||
checkEnabled();
|
checkEnabled();
|
||||||
List<Integer> watches;
|
List<Integer> watches;
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
assert started;
|
try {
|
||||||
assert callback != null;
|
assert started;
|
||||||
started = false;
|
assert callback != null;
|
||||||
callback = null;
|
started = false;
|
||||||
watches = new ArrayList<Integer>(this.watches);
|
callback = null;
|
||||||
this.watches.clear();
|
watches = new ArrayList<Integer>(this.watches);
|
||||||
}
|
this.watches.clear();
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
for(Integer w : watches) JNotify.removeWatch(w);
|
for(Integer w : watches) JNotify.removeWatch(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void fileCreated(int wd, String rootPath, String name) {
|
public void fileCreated(int wd, String rootPath, String name) {
|
||||||
Callback callback;
|
Callback callback;
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
callback = this.callback;
|
try {
|
||||||
}
|
callback = this.callback;
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
|
}
|
||||||
if(callback != null)
|
if(callback != null)
|
||||||
callback.driveInserted(new File(rootPath + "/" + name));
|
callback.driveInserted(new File(rootPath + "/" + name));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ import java.io.InputStream;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.locks.Condition;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import jssc.SerialPortEvent;
|
import jssc.SerialPortEvent;
|
||||||
@@ -44,6 +48,11 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
|||||||
|
|
||||||
private ReliabilityLayer reliability = null; // Locking: this
|
private ReliabilityLayer reliability = null; // Locking: this
|
||||||
private boolean initialised = false, connected = false; // Locking: this
|
private boolean initialised = false, connected = false; // Locking: this
|
||||||
|
|
||||||
|
private final Lock synchLock = new ReentrantLock();
|
||||||
|
private final Condition connectedStateChanged = synchLock.newCondition();
|
||||||
|
private final Condition initialisedStateChanged = synchLock.newCondition();
|
||||||
|
|
||||||
|
|
||||||
ModemImpl(Executor executor, ReliabilityLayerFactory reliabilityFactory,
|
ModemImpl(Executor executor, ReliabilityLayerFactory reliabilityFactory,
|
||||||
Clock clock, Callback callback, SerialPort port) {
|
Clock clock, Callback callback, SerialPort port) {
|
||||||
@@ -91,14 +100,18 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
|||||||
// Wait for the event thread to receive "OK"
|
// Wait for the event thread to receive "OK"
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
long now = clock.currentTimeMillis();
|
long now = clock.currentTimeMillis();
|
||||||
long end = now + OK_TIMEOUT;
|
long end = now + OK_TIMEOUT;
|
||||||
while(now < end && !initialised) {
|
while(now < end && !initialised) {
|
||||||
wait(end - now);
|
initialisedStateChanged.await(end - now, TimeUnit.MILLISECONDS);
|
||||||
now = clock.currentTimeMillis();
|
now = clock.currentTimeMillis();
|
||||||
}
|
}
|
||||||
success = initialised;
|
success = initialised;
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
} catch(InterruptedException e) {
|
} catch(InterruptedException e) {
|
||||||
tryToClose(port);
|
tryToClose(port);
|
||||||
@@ -123,11 +136,16 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
|||||||
|
|
||||||
public void stop() throws IOException {
|
public void stop() throws IOException {
|
||||||
LOG.info("Stopping");
|
LOG.info("Stopping");
|
||||||
// Wake any threads that are waiting to connect
|
synchLock.lock();
|
||||||
synchronized(this) {
|
try {
|
||||||
|
// Wake any threads that are waiting to connect
|
||||||
initialised = false;
|
initialised = false;
|
||||||
connected = false;
|
connected = false;
|
||||||
notifyAll();
|
initialisedStateChanged.signalAll();
|
||||||
|
connectedStateChanged.signalAll();
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
// Hang up if necessary and close the port
|
// Hang up if necessary and close the port
|
||||||
try {
|
try {
|
||||||
@@ -148,7 +166,8 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
|||||||
// Locking: stateChange
|
// Locking: stateChange
|
||||||
private void hangUpInner() throws IOException {
|
private void hangUpInner() throws IOException {
|
||||||
ReliabilityLayer reliability;
|
ReliabilityLayer reliability;
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
if(this.reliability == null) {
|
if(this.reliability == null) {
|
||||||
LOG.info("Not hanging up - already on the hook");
|
LOG.info("Not hanging up - already on the hook");
|
||||||
return;
|
return;
|
||||||
@@ -156,6 +175,9 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
|||||||
reliability = this.reliability;
|
reliability = this.reliability;
|
||||||
this.reliability = null;
|
this.reliability = null;
|
||||||
connected = false;
|
connected = false;
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
reliability.stop();
|
reliability.stop();
|
||||||
LOG.info("Hanging up");
|
LOG.info("Hanging up");
|
||||||
@@ -182,7 +204,8 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
|||||||
try {
|
try {
|
||||||
ReliabilityLayer reliability =
|
ReliabilityLayer reliability =
|
||||||
reliabilityFactory.createReliabilityLayer(this);
|
reliabilityFactory.createReliabilityLayer(this);
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
if(!initialised) {
|
if(!initialised) {
|
||||||
LOG.info("Not dialling - modem not initialised");
|
LOG.info("Not dialling - modem not initialised");
|
||||||
return false;
|
return false;
|
||||||
@@ -192,6 +215,9 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this.reliability = reliability;
|
this.reliability = reliability;
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
reliability.start();
|
reliability.start();
|
||||||
LOG.info("Dialling");
|
LOG.info("Dialling");
|
||||||
@@ -204,14 +230,18 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
|||||||
}
|
}
|
||||||
// Wait for the event thread to receive "CONNECT"
|
// Wait for the event thread to receive "CONNECT"
|
||||||
try {
|
try {
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
long now = clock.currentTimeMillis();
|
long now = clock.currentTimeMillis();
|
||||||
long end = now + CONNECT_TIMEOUT;
|
long end = now + CONNECT_TIMEOUT;
|
||||||
while(now < end && initialised && !connected) {
|
while(now < end && initialised && !connected) {
|
||||||
wait(end - now);
|
connectedStateChanged.await(end - now, TimeUnit.MILLISECONDS);
|
||||||
now = clock.currentTimeMillis();
|
now = clock.currentTimeMillis();
|
||||||
}
|
}
|
||||||
if(connected) return true;
|
if(connected) return true;
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
} catch(InterruptedException e) {
|
} catch(InterruptedException e) {
|
||||||
tryToClose(port);
|
tryToClose(port);
|
||||||
@@ -227,8 +257,12 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
|||||||
|
|
||||||
public InputStream getInputStream() throws IOException {
|
public InputStream getInputStream() throws IOException {
|
||||||
ReliabilityLayer reliability;
|
ReliabilityLayer reliability;
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
reliability = this.reliability;
|
reliability = this.reliability;
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
if(reliability == null) throw new IOException("Not connected");
|
if(reliability == null) throw new IOException("Not connected");
|
||||||
return reliability.getInputStream();
|
return reliability.getInputStream();
|
||||||
@@ -236,8 +270,12 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
|||||||
|
|
||||||
public OutputStream getOutputStream() throws IOException {
|
public OutputStream getOutputStream() throws IOException {
|
||||||
ReliabilityLayer reliability;
|
ReliabilityLayer reliability;
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
reliability = this.reliability;
|
reliability = this.reliability;
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
if(reliability == null) throw new IOException("Not connected");
|
if(reliability == null) throw new IOException("Not connected");
|
||||||
return reliability.getOutputStream();
|
return reliability.getOutputStream();
|
||||||
@@ -288,8 +326,12 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
|||||||
|
|
||||||
private boolean handleData(byte[] b) throws IOException {
|
private boolean handleData(byte[] b) throws IOException {
|
||||||
ReliabilityLayer reliability;
|
ReliabilityLayer reliability;
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
reliability = this.reliability;
|
reliability = this.reliability;
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
if(reliability == null) return false;
|
if(reliability == null) return false;
|
||||||
reliability.handleRead(b);
|
reliability.handleRead(b);
|
||||||
@@ -309,9 +351,13 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
|||||||
lineLen = 0;
|
lineLen = 0;
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Modem status: " + s);
|
if(LOG.isLoggable(INFO)) LOG.info("Modem status: " + s);
|
||||||
if(s.startsWith("CONNECT")) {
|
if(s.startsWith("CONNECT")) {
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
connected = true;
|
connected = true;
|
||||||
notifyAll();
|
connectedStateChanged.signalAll();
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
// There might be data in the buffer as well as text
|
// There might be data in the buffer as well as text
|
||||||
int off = i + 1;
|
int off = i + 1;
|
||||||
@@ -323,14 +369,22 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
|||||||
return;
|
return;
|
||||||
} else if(s.equals("BUSY") || s.equals("NO DIALTONE")
|
} else if(s.equals("BUSY") || s.equals("NO DIALTONE")
|
||||||
|| s.equals("NO CARRIER")) {
|
|| s.equals("NO CARRIER")) {
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
connected = false;
|
connected = false;
|
||||||
notifyAll();
|
connectedStateChanged.signalAll();
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
} else if(s.equals("OK")) {
|
} else if(s.equals("OK")) {
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
initialised = true;
|
initialised = true;
|
||||||
notifyAll();
|
initialisedStateChanged.signalAll();
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
} else if(s.equals("RING")) {
|
} else if(s.equals("RING")) {
|
||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@@ -358,7 +412,8 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
|||||||
try {
|
try {
|
||||||
ReliabilityLayer reliability =
|
ReliabilityLayer reliability =
|
||||||
reliabilityFactory.createReliabilityLayer(this);
|
reliabilityFactory.createReliabilityLayer(this);
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
if(!initialised) {
|
if(!initialised) {
|
||||||
LOG.info("Not answering - modem not initialised");
|
LOG.info("Not answering - modem not initialised");
|
||||||
return;
|
return;
|
||||||
@@ -368,6 +423,9 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.reliability = reliability;
|
this.reliability = reliability;
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
reliability.start();
|
reliability.start();
|
||||||
LOG.info("Answering");
|
LOG.info("Answering");
|
||||||
@@ -380,14 +438,18 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
|||||||
// Wait for the event thread to receive "CONNECT"
|
// Wait for the event thread to receive "CONNECT"
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
synchronized(this) {
|
synchLock.lock();
|
||||||
|
try {
|
||||||
long now = clock.currentTimeMillis();
|
long now = clock.currentTimeMillis();
|
||||||
long end = now + CONNECT_TIMEOUT;
|
long end = now + CONNECT_TIMEOUT;
|
||||||
while(now < end && initialised && !connected) {
|
while(now < end && initialised && !connected) {
|
||||||
wait(end - now);
|
connectedStateChanged.await(end - now, TimeUnit.MILLISECONDS);
|
||||||
now = clock.currentTimeMillis();
|
now = clock.currentTimeMillis();
|
||||||
}
|
}
|
||||||
success = connected;
|
success = connected;
|
||||||
|
}
|
||||||
|
finally{
|
||||||
|
synchLock.unlock();
|
||||||
}
|
}
|
||||||
} catch(InterruptedException e) {
|
} catch(InterruptedException e) {
|
||||||
tryToClose(port);
|
tryToClose(port);
|
||||||
|
|||||||
Reference in New Issue
Block a user