mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-07 14:54:15 +00:00
Fix another deadlock when storing channel in wallet
This commit is contained in:
parent
8fd8683461
commit
715e3596d2
@ -22,10 +22,13 @@ import java.util.Date;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import com.google.bitcoin.core.*;
|
import com.google.bitcoin.core.*;
|
||||||
|
import com.google.bitcoin.utils.Locks;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.HashMultimap;
|
import com.google.common.collect.HashMultimap;
|
||||||
|
import net.jcip.annotations.GuardedBy;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
@ -37,12 +40,14 @@ import static com.google.common.base.Preconditions.checkState;
|
|||||||
public class StoredPaymentChannelClientStates implements WalletExtension {
|
public class StoredPaymentChannelClientStates implements WalletExtension {
|
||||||
static final String EXTENSION_ID = StoredPaymentChannelClientStates.class.getName();
|
static final String EXTENSION_ID = StoredPaymentChannelClientStates.class.getName();
|
||||||
|
|
||||||
@VisibleForTesting final HashMultimap<Sha256Hash, StoredClientChannel> mapChannels = HashMultimap.create();
|
@GuardedBy("lock") @VisibleForTesting final HashMultimap<Sha256Hash, StoredClientChannel> mapChannels = HashMultimap.create();
|
||||||
@VisibleForTesting final Timer channelTimeoutHandler = new Timer();
|
@VisibleForTesting final Timer channelTimeoutHandler = new Timer();
|
||||||
|
|
||||||
private Wallet containingWallet;
|
private Wallet containingWallet;
|
||||||
private final TransactionBroadcaster announcePeerGroup;
|
private final TransactionBroadcaster announcePeerGroup;
|
||||||
|
|
||||||
|
protected final ReentrantLock lock = Locks.lock("StoredPaymentChannelClientStates");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new StoredPaymentChannelClientStates and associates it with the given {@link Wallet} and
|
* Creates a new StoredPaymentChannelClientStates and associates it with the given {@link Wallet} and
|
||||||
* {@link TransactionBroadcaster} which are used to complete and announce contract and refund
|
* {@link TransactionBroadcaster} which are used to complete and announce contract and refund
|
||||||
@ -56,15 +61,20 @@ public class StoredPaymentChannelClientStates implements WalletExtension {
|
|||||||
/**
|
/**
|
||||||
* Finds an inactive channel with the given id and returns it, or returns null.
|
* Finds an inactive channel with the given id and returns it, or returns null.
|
||||||
*/
|
*/
|
||||||
public synchronized StoredClientChannel getInactiveChannelById(Sha256Hash id) {
|
public StoredClientChannel getInactiveChannelById(Sha256Hash id) {
|
||||||
Set<StoredClientChannel> setChannels = mapChannels.get(id);
|
lock.lock();
|
||||||
for (StoredClientChannel channel : setChannels) {
|
try {
|
||||||
synchronized (channel) {
|
Set<StoredClientChannel> setChannels = mapChannels.get(id);
|
||||||
if (!channel.active) {
|
for (StoredClientChannel channel : setChannels) {
|
||||||
channel.active = true;
|
synchronized (channel) {
|
||||||
return channel;
|
if (!channel.active) {
|
||||||
|
channel.active = true;
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -72,13 +82,18 @@ public class StoredPaymentChannelClientStates implements WalletExtension {
|
|||||||
/**
|
/**
|
||||||
* Finds a channel with the given id and contract hash and returns it, or returns null.
|
* Finds a channel with the given id and contract hash and returns it, or returns null.
|
||||||
*/
|
*/
|
||||||
public synchronized StoredClientChannel getChannel(Sha256Hash id, Sha256Hash contractHash) {
|
public StoredClientChannel getChannel(Sha256Hash id, Sha256Hash contractHash) {
|
||||||
Set<StoredClientChannel> setChannels = mapChannels.get(id);
|
lock.lock();
|
||||||
for (StoredClientChannel channel : setChannels) {
|
try {
|
||||||
if (channel.contract.getHash().equals(contractHash))
|
Set<StoredClientChannel> setChannels = mapChannels.get(id);
|
||||||
return channel;
|
for (StoredClientChannel channel : setChannels) {
|
||||||
|
if (channel.contract.getHash().equals(contractHash))
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,17 +105,22 @@ public class StoredPaymentChannelClientStates implements WalletExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Adds this channel and optionally notifies the wallet of an update to this extension (used during deserialize)
|
// Adds this channel and optionally notifies the wallet of an update to this extension (used during deserialize)
|
||||||
private synchronized void putChannel(final StoredClientChannel channel, boolean updateWallet) {
|
private void putChannel(final StoredClientChannel channel, boolean updateWallet) {
|
||||||
mapChannels.put(channel.id, channel);
|
lock.lock();
|
||||||
channelTimeoutHandler.schedule(new TimerTask() {
|
try {
|
||||||
@Override
|
mapChannels.put(channel.id, channel);
|
||||||
public void run() {
|
channelTimeoutHandler.schedule(new TimerTask() {
|
||||||
removeChannel(channel);
|
@Override
|
||||||
announcePeerGroup.broadcastTransaction(channel.contract);
|
public void run() {
|
||||||
announcePeerGroup.broadcastTransaction(channel.refund);
|
removeChannel(channel);
|
||||||
}
|
announcePeerGroup.broadcastTransaction(channel.contract);
|
||||||
// Add the difference between real time and Utils.now() so that test-cases can use a mock clock.
|
announcePeerGroup.broadcastTransaction(channel.refund);
|
||||||
}, new Date((channel.refund.getLockTime() + 60 * 5) * 1000 + (System.currentTimeMillis() - Utils.now().getTime())));
|
}
|
||||||
|
// Add the difference between real time and Utils.now() so that test-cases can use a mock clock.
|
||||||
|
}, new Date((channel.refund.getLockTime() + 60 * 5) * 1000 + (System.currentTimeMillis() - Utils.now().getTime())));
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
if (updateWallet)
|
if (updateWallet)
|
||||||
containingWallet.addOrUpdateExtension(this);
|
containingWallet.addOrUpdateExtension(this);
|
||||||
}
|
}
|
||||||
@ -113,8 +133,13 @@ public class StoredPaymentChannelClientStates implements WalletExtension {
|
|||||||
* {@link TransactionBroadcaster} as long as this {@link StoredPaymentChannelClientStates} continues to
|
* {@link TransactionBroadcaster} as long as this {@link StoredPaymentChannelClientStates} continues to
|
||||||
* exist in memory.</p>
|
* exist in memory.</p>
|
||||||
*/
|
*/
|
||||||
public synchronized void removeChannel(StoredClientChannel channel) {
|
public void removeChannel(StoredClientChannel channel) {
|
||||||
mapChannels.remove(channel.id, channel);
|
lock.lock();
|
||||||
|
try {
|
||||||
|
mapChannels.remove(channel.id, channel);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
containingWallet.addOrUpdateExtension(this);
|
containingWallet.addOrUpdateExtension(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +154,8 @@ public class StoredPaymentChannelClientStates implements WalletExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized byte[] serializeWalletExtension() {
|
public byte[] serializeWalletExtension() {
|
||||||
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
ObjectOutputStream oos = new ObjectOutputStream(out);
|
ObjectOutputStream oos = new ObjectOutputStream(out);
|
||||||
@ -139,18 +165,25 @@ public class StoredPaymentChannelClientStates implements WalletExtension {
|
|||||||
return out.toByteArray();
|
return out.toByteArray();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void deserializeWalletExtension(Wallet containingWallet, byte[] data) throws Exception {
|
public void deserializeWalletExtension(Wallet containingWallet, byte[] data) throws Exception {
|
||||||
checkState(this.containingWallet == null || this.containingWallet == containingWallet);
|
lock.lock();
|
||||||
this.containingWallet = containingWallet;
|
try {
|
||||||
ByteArrayInputStream inStream = new ByteArrayInputStream(data);
|
checkState(this.containingWallet == null || this.containingWallet == containingWallet);
|
||||||
ObjectInputStream ois = new ObjectInputStream(inStream);
|
this.containingWallet = containingWallet;
|
||||||
while (inStream.available() > 0) {
|
ByteArrayInputStream inStream = new ByteArrayInputStream(data);
|
||||||
StoredClientChannel channel = (StoredClientChannel)ois.readObject();
|
ObjectInputStream ois = new ObjectInputStream(inStream);
|
||||||
putChannel(channel, false);
|
while (inStream.available() > 0) {
|
||||||
|
StoredClientChannel channel = (StoredClientChannel)ois.readObject();
|
||||||
|
putChannel(channel, false);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user