Skip to content

Commit

Permalink
Make Swarm's direct access code to store atomic
Browse files Browse the repository at this point in the history
It's only temporary code in a transition code.

[changelog skip]
  • Loading branch information
dahlia committed Jul 22, 2019
1 parent c748865 commit ef6ed36
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 65 deletions.
8 changes: 7 additions & 1 deletion Libplanet/Blockchain/BlockChain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
Expand All @@ -19,7 +20,12 @@ namespace Libplanet.Blockchain
public class BlockChain<T> : IReadOnlyList<Block<T>>
where T : IAction, new()
{
private readonly ReaderWriterLockSlim _rwlock;
// FIXME: The _rwlock field should be private.
[SuppressMessage(
"StyleCop.CSharp.OrderingRules",
"SA1401:FieldsMustBePrivate",
Justification = "Temporary visibility.")]
internal readonly ReaderWriterLockSlim _rwlock;
private readonly object _txLock;

public BlockChain(IBlockPolicy<T> policy, IStore store)
Expand Down
168 changes: 105 additions & 63 deletions Libplanet/Net/Swarm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -619,33 +619,55 @@ await SyncBehindsBlocksFromPeersAsync(

if (!received)
{
// FIXME: Swam should not directly access to the IStore instance,
// but BlockChain<T> should have an indirect interface to its underlying store.
IStore store = _blockChain.Store;

foreach (Block<T> block in _blockChain)
ReaderWriterLockSlim rwlock = _blockChain._rwlock;
rwlock.EnterUpgradeableReadLock();
try
{
if (store.GetBlockStates(block.Hash) is null)
// FIXME: Swam should not directly access to the IStore instance,
// but BlockChain<T> should have an indirect interface to its underlying store.
IStore store = _blockChain.Store;

foreach (Block<T> block in _blockChain)
{
AccountStateGetter stateGetter;
if (block.PreviousHash is null)
{
stateGetter = _ => null;
}
else
if (store.GetBlockStates(block.Hash) is null)
{
stateGetter = a =>
_blockChain.GetStates(
new[] { a },
block.PreviousHash
).GetValueOrDefault(a);
}
AccountStateGetter stateGetter;
if (block.PreviousHash is null)
{
stateGetter = _ => null;
}
else
{
stateGetter = a =>
_blockChain.GetStates(
new[] { a },
block.PreviousHash
).GetValueOrDefault(a);
}

IReadOnlyList<ActionEvaluation<T>> evaluations =
block.Evaluate(DateTimeOffset.UtcNow, stateGetter).ToImmutableList();
_blockChain.SetStates(block, evaluations, buildStateReferences: true);
IReadOnlyList<ActionEvaluation<T>> evaluations =
block.Evaluate(DateTimeOffset.UtcNow, stateGetter)
.ToImmutableList();
rwlock.EnterWriteLock();
try
{
_blockChain.SetStates(
block,
evaluations,
buildStateReferences: true
);
}
finally
{
rwlock.ExitWriteLock();
}
}
}
}
finally
{
rwlock.ExitUpgradeableReadLock();
}
}
}

Expand Down Expand Up @@ -960,29 +982,40 @@ await socket.SendMultipartMessageAsync(
Message parsedMessage = Message.Parse(reply, reply: true);
if (parsedMessage is RecentStates recentStates && !recentStates.Missing)
{
// FIXME: Swarm should not directly access to the IStore instance,
// but BlockChain<T> should have an indirect interface to its underlying
// store.
IStore store = BlockChain.Store;
string ns = BlockChain.Id.ToString();

_logger.Debug("Starts to store state refs received from {0}.", peer);
foreach (var pair in recentStates.StateReferences)
ReaderWriterLockSlim rwlock = BlockChain._rwlock;
rwlock.EnterWriteLock();
try
{
IImmutableSet<Address> address = ImmutableHashSet.Create(pair.Key);
foreach (HashDigest<SHA256> bHash in pair.Value)
// FIXME: Swarm should not directly access to the IStore instance,
// but BlockChain<T> should have an indirect interface to its underlying
// store.
IStore store = BlockChain.Store;
string ns = BlockChain.Id.ToString();

_logger.Debug("Starts to store state refs received from {0}.", peer);
foreach (var pair in recentStates.StateReferences)
{
store.StoreStateReference(ns, address, store.GetBlock<T>(bHash));
IImmutableSet<Address> address = ImmutableHashSet.Create(pair.Key);
foreach (HashDigest<SHA256> bHash in pair.Value)
{
Block<T> block = store.GetBlock<T>(bHash);
store.StoreStateReference(ns, address, block);
}
}
}

_logger.Debug("Starts to store block states received from {0}.", peer);
foreach (var pair in recentStates.BlockStates)
_logger.Debug("Starts to store block states received from {0}.", peer);
foreach (var pair in recentStates.BlockStates)
{
store.SetBlockStates(pair.Key, new AddressStateMap(pair.Value));
}
}
finally
{
store.SetBlockStates(pair.Key, new AddressStateMap(pair.Value));
rwlock.ExitWriteLock();
}

_logger.Debug("Finished to store received state refs and block states.");
_logger.Debug(
"Finished to store received state refs and block states.");
return true;
}

Expand Down Expand Up @@ -1500,34 +1533,43 @@ private void TransferRecentStates(GetRecentStates getRecentStates)

if (_blockChain.Blocks.ContainsKey(blockHash))
{
// FIXME: Swarm should not directly access to the IStore instance,
// but BlockChain<T> should have an indirect interface to its underlying
// store.
IStore store = _blockChain.Store;
string ns = _blockChain.Id.ToString();
ReaderWriterLockSlim rwlock = _blockChain._rwlock;
rwlock.EnterReadLock();
try
{
// FIXME: Swarm should not directly access to the IStore instance,
// but BlockChain<T> should have an indirect interface to its underlying
// store.
IStore store = _blockChain.Store;
string ns = _blockChain.Id.ToString();

stateRefs = store.ListAddresses(ns).Select(address =>
stateRefs = store.ListAddresses(ns).Select(address =>
{
ImmutableList<HashDigest<SHA256>> refs =
store.IterateStateReferences(ns, address).Select(
p => p.Item1
).Reverse().ToImmutableList();
return
new KeyValuePair<Address, IImmutableList<HashDigest<SHA256>>>(
address, refs
);
}).ToImmutableDictionary();

blockStates = stateRefs.Values
.Select(refs => refs.Last())
.ToImmutableHashSet()
.Select(bh =>
new KeyValuePair<
HashDigest<SHA256>,
IImmutableDictionary<Address, object>
>(bh, store.GetBlockStates(bh))
)
.ToImmutableDictionary();
}
finally
{
ImmutableList<HashDigest<SHA256>> refs =
store.IterateStateReferences(ns, address).Select(
p => p.Item1
).Reverse().ToImmutableList();
return
new KeyValuePair<Address, IImmutableList<HashDigest<SHA256>>>(
address, refs
);
}).ToImmutableDictionary();

blockStates = stateRefs.Values
.Select(refs => refs.Last())
.ToImmutableHashSet()
.Select(bh =>
new KeyValuePair<
HashDigest<SHA256>,
IImmutableDictionary<Address, object>
>(bh, store.GetBlockStates(bh))
)
.ToImmutableDictionary();
rwlock.ExitReadLock();
}
}

var reply = new RecentStates(blockHash, blockStates, stateRefs)
Expand Down
2 changes: 1 addition & 1 deletion Menees.Analyzers.Settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
<Menees.Analyzers.Settings>
<MaxLineColumns>100</MaxLineColumns>
<MaxMethodLines>200</MaxMethodLines>
<MaxFileLines>2050</MaxFileLines>
<MaxFileLines>2100</MaxFileLines>
</Menees.Analyzers.Settings>

0 comments on commit ef6ed36

Please sign in to comment.