You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
6.1 KiB
207 lines
6.1 KiB
#if FAT |
|
|
|
using System; |
|
using System.Threading; |
|
using LinqInternal.Collections.ThreadSafe; |
|
|
|
namespace LinqInternal.Threading.Needles |
|
{ |
|
internal sealed partial class Transact |
|
{ |
|
private static readonly LockContext<Thread> _context = new LockContext<Thread>(512); |
|
|
|
[ThreadStatic] |
|
private static Transact _currentTransaction; |
|
|
|
private readonly Transact _parentTransaction; |
|
private readonly SafeDictionary<IResource, object> _readLog; |
|
private readonly Thread _thread; |
|
private readonly SafeDictionary<IResource, object> _writeLog; |
|
private LockSlot<Thread> _lockSlot; |
|
|
|
public Transact() |
|
{ |
|
_writeLog = new SafeDictionary<IResource, object>(); |
|
_readLog = new SafeDictionary<IResource, object>(); |
|
_parentTransaction = _currentTransaction; |
|
_currentTransaction = this; |
|
_thread = Thread.CurrentThread; |
|
} |
|
|
|
public static Transact CurrentTransaction |
|
{ |
|
get { return _currentTransaction; } |
|
} |
|
|
|
public bool IsRoot |
|
{ |
|
get { return _parentTransaction == null; } |
|
} |
|
|
|
private static LockContext<Thread> Context |
|
{ |
|
get { return _context; } |
|
} |
|
|
|
public static Needle<T> CreateNeedle<T>(T value) |
|
{ |
|
return new Needle<T>(value); |
|
} |
|
|
|
public bool Commit() |
|
{ |
|
if (ReferenceEquals(_currentTransaction, this)) |
|
{ |
|
ThreadingHelper.MemoryBarrier(); |
|
try |
|
{ |
|
if (!CheckValue()) |
|
{ |
|
//the resources has been modified by another thread |
|
return false; |
|
} |
|
try |
|
{ |
|
ThreadingHelper.SpinWaitUntil(() => _context.ClaimSlot(out _lockSlot)); |
|
_lockSlot.Value = Thread.CurrentThread; |
|
if (!Capture()) |
|
{ |
|
//Nothing to commit |
|
return true; |
|
} |
|
ThreadingHelper.MemoryBarrier(); |
|
if (!CheckCapture() || !CheckValue()) |
|
{ |
|
//the resources has been claimed by another thread |
|
return false; |
|
} |
|
var written = false; |
|
foreach (var resource in _writeLog) |
|
{ |
|
if (resource.Key.Commit()) |
|
{ |
|
written = true; |
|
} |
|
else |
|
{ |
|
//unexpected |
|
if (written) |
|
{ |
|
// TODO - the transaction was partially written, this should not be possible. |
|
System.Diagnostics.Debug.Fail("unexpected - partially commited transaction"); |
|
} |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
finally |
|
{ |
|
if (!ReferenceEquals(_lockSlot, null)) |
|
{ |
|
_lockSlot.Close(); |
|
_lockSlot = null; |
|
} |
|
} |
|
} |
|
finally |
|
{ |
|
Release(false); |
|
} |
|
} |
|
throw new InvalidOperationException("Cannot commit a non-current transaction."); |
|
} |
|
|
|
public void Rollback() |
|
{ |
|
if (ReferenceEquals(Thread.CurrentThread, _thread)) |
|
{ |
|
Release(false); |
|
} |
|
else |
|
{ |
|
throw new InvalidOperationException("Unable to rollback a transaction that belongs to another thread."); |
|
} |
|
} |
|
|
|
private bool Capture() |
|
{ |
|
var result = false; |
|
foreach (var resource1 in _writeLog) |
|
{ |
|
resource1.Key.Capture(); |
|
result = true; |
|
} |
|
if (result) |
|
{ |
|
foreach (var resource2 in _readLog) |
|
{ |
|
resource2.Key.Capture(); |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
private bool CheckCapture() |
|
{ |
|
// Keep foreach loop |
|
foreach (var resource in _readLog) |
|
{ |
|
if (!resource.Key.CheckCapture()) |
|
{ |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
private bool CheckValue() |
|
{ |
|
// Keep foreach loop |
|
foreach (var resource in _readLog) |
|
{ |
|
if (!resource.Key.CheckValue()) |
|
{ |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
private void Release(bool dispose) |
|
{ |
|
for (var currentTransaction = _currentTransaction; currentTransaction != null && currentTransaction != this; currentTransaction = currentTransaction._parentTransaction) |
|
{ |
|
if (dispose) |
|
{ |
|
currentTransaction.Dispose(); |
|
} |
|
else |
|
{ |
|
currentTransaction.Uncapture(); |
|
} |
|
} |
|
Uncapture(); |
|
if (dispose) |
|
{ |
|
_currentTransaction = _parentTransaction; |
|
} |
|
} |
|
|
|
private void Uncapture() |
|
{ |
|
foreach (var resource in _readLog) |
|
{ |
|
resource.Key.Release(); |
|
} |
|
foreach (var resource in _writeLog) |
|
{ |
|
resource.Key.Release(); |
|
} |
|
_readLog.Clear(); |
|
_writeLog.Clear(); |
|
} |
|
} |
|
} |
|
|
|
#endif |