Skip to content

Commit

Permalink
Removed locks for old Unity runtime.
Browse files Browse the repository at this point in the history
  • Loading branch information
timcassell committed Feb 28, 2022
1 parent 8a6ff11 commit a179685
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 137 deletions.
Expand Up @@ -600,13 +600,9 @@ internal override void Handle(ref PromiseRef handler, out HandleablePromiseBase
bool isComplete = ExchangeCurrentRunner(previousRunner) == null;
if (isComplete)
{
#if !CSHARP_7_3_OR_NEWER // Interlocked.Exchange doesn't seem to work properly in Unity's old runtime. I'm not sure why, but we need a lock here to pass multi-threaded tests.
lock (this)
#endif
{
Thread.MemoryBarrier(); // Make sure previous writes are done before swapping _waiter.
nextHandler = Interlocked.Exchange(ref _waiter, null);
}
Thread.MemoryBarrier(); // Make sure previous writes are done before swapping _waiter.
nextHandler = Interlocked.Exchange(ref _waiter, null);

HandleProgressListener(handler.State, 0, ref executionScheduler);
WaitWhileProgressFlags(PromiseFlags.Subscribing);
handler.MaybeDispose();
Expand Down Expand Up @@ -669,13 +665,9 @@ internal override void Handle(ref PromiseRef handler, out HandleablePromiseBase
bool isComplete = ExchangeCurrentRunner(previousRunner) == null;
if (isComplete)
{
#if !CSHARP_7_3_OR_NEWER // Interlocked.Exchange doesn't seem to work properly in Unity's old runtime. I'm not sure why, but we need a lock here to pass multi-threaded tests.
lock (this)
#endif
{
Thread.MemoryBarrier(); // Make sure previous writes are done before swapping _waiter.
nextHandler = Interlocked.Exchange(ref _waiter, null);
}
Thread.MemoryBarrier(); // Make sure previous writes are done before swapping _waiter.
nextHandler = Interlocked.Exchange(ref _waiter, null);

HandleProgressListener(handler.State, 0, ref executionScheduler);
WaitWhileProgressFlags(PromiseFlags.Subscribing);
handler.MaybeDispose();
Expand Down
Expand Up @@ -87,14 +87,10 @@ internal void HandleFromCancelation()
{
var executionScheduler = new ExecutionScheduler(true);
HandleablePromiseBase nextHandler;
#if !CSHARP_7_3_OR_NEWER // Interlocked.Exchange doesn't seem to work properly in Unity's old runtime. I'm not sure why, but we need a lock here to pass multi-threaded tests.
lock (this)
#endif
{
SetResult(CancelContainerVoid.GetOrCreate(), Promise.State.Canceled);
Thread.MemoryBarrier(); // Make sure previous writes are done before swapping _waiter.
nextHandler = Interlocked.Exchange(ref _waiter, null);
}
SetResult(CancelContainerVoid.GetOrCreate(), Promise.State.Canceled);
Thread.MemoryBarrier(); // Make sure previous writes are done before swapping _waiter.
nextHandler = Interlocked.Exchange(ref _waiter, null);

HandleProgressListener(Promise.State.Canceled, Depth, ref executionScheduler);
MaybeHandleNext(nextHandler, ref executionScheduler);
executionScheduler.Execute();
Expand Down
Expand Up @@ -29,14 +29,9 @@ protected void Handle(ref int _waitCount, ref ExecutionScheduler executionSchedu
var valueContainer = (ValueContainer) _valueOrPrevious;
var state = valueContainer.GetState();
State = state;
HandleablePromiseBase nextHandler;
#if !CSHARP_7_3_OR_NEWER // Interlocked.Exchange doesn't seem to work properly in Unity's old runtime. I'm not sure why, but we need a lock here to pass multi-threaded tests.
lock (this)
#endif
{
Thread.MemoryBarrier(); // Make sure previous writes are done before swapping _waiter.
nextHandler = Interlocked.Exchange(ref _waiter, null);
}
Thread.MemoryBarrier(); // Make sure previous writes are done before swapping _waiter.
var nextHandler = Interlocked.Exchange(ref _waiter, null);

HandleProgressListener(state, Depth, ref executionScheduler);
InterlockedRetainDisregardId(); // Retain since Handle will release indiscriminately.
if (InterlockedAddWithOverflowCheck(ref _waitCount, -1, 0) == 0)
Expand Down
Expand Up @@ -915,13 +915,9 @@ private void Invoke1(Promise.State state, out HandleablePromiseBase nextHandler,

State = state; // Set state after callback is executed to make sure it completes before the next waiter begins execution (in another thread).

#if !CSHARP_7_3_OR_NEWER // Interlocked.Exchange doesn't seem to work properly in Unity's old runtime. I'm not sure why, but we need a lock here to pass multi-threaded tests.
lock (this)
#endif
{
Thread.MemoryBarrier(); // Make sure previous writes are done before swapping _waiter.
nextHandler = Interlocked.Exchange(ref _waiter, null);
}
Thread.MemoryBarrier(); // Make sure previous writes are done before swapping _waiter.
nextHandler = Interlocked.Exchange(ref _waiter, null);

HandleProgressListener(state, Depth, ref executionScheduler);
#if PROTO_PROMISE_NO_STACK_UNWIND
MaybeHandleNext(nextHandler, ref executionScheduler);
Expand Down Expand Up @@ -958,38 +954,33 @@ internal override void AddWaiter(HandleablePromiseBase waiter, ref ExecutionSche

internal override void AddWaiter(HandleablePromiseBase waiter, out HandleablePromiseBase nextHandler, ref ExecutionScheduler executionScheduler)
{
#if !CSHARP_7_3_OR_NEWER // Interlocked.Exchange doesn't seem to work properly in Unity's old runtime. I'm not sure why, but we need a lock here to pass multi-threaded tests.
lock (this)
#endif
ThrowIfInPool(this);
// When this is completed, State is set then _waiter is swapped, so we must reverse that process here.
Thread.MemoryBarrier();
SetWaiter(waiter);
Thread.MemoryBarrier(); // Make sure State is read after _waiter is written.
if (State != Promise.State.Pending)
{
ThrowIfInPool(this);
// When this is completed, State is set then _waiter is swapped, so we must reverse that process here.
Thread.MemoryBarrier();
SetWaiter(waiter);
Thread.MemoryBarrier(); // Make sure State is read after _waiter is written.
if (State != Promise.State.Pending)
if (_smallProgressFields._isSynchronous)
{
if (_smallProgressFields._isSynchronous)
{
nextHandler = Interlocked.Exchange(ref _waiter, null);
return;
}
nextHandler = Interlocked.Exchange(ref _waiter, null);
return;
}

// If this was configured to execute progress on a SynchronizationContext or the ThreadPool, force the waiter to execute on the same context for consistency.
// Retain since this will be released higher in the call stack.
InterlockedRetainDisregardId();
if (_synchronizationContext == null)
{
// If there is no context, send it to the ThreadPool.
ThreadPool.QueueUserWorkItem(_threadPoolCallback, this);
}
else
{
_synchronizationContext.Post(_synchronizationContextCallback, this);
}
// If this was configured to execute progress on a SynchronizationContext or the ThreadPool, force the waiter to execute on the same context for consistency.
// Retain since this will be released higher in the call stack.
InterlockedRetainDisregardId();
if (_synchronizationContext == null)
{
// If there is no context, send it to the ThreadPool.
ThreadPool.QueueUserWorkItem(_threadPoolCallback, this);
}
else
{
_synchronizationContext.Post(_synchronizationContextCallback, this);
}
nextHandler = null;
}
nextHandler = null;
}

private static void ExecuteFromContext(object state)
Expand Down
Expand Up @@ -315,47 +315,38 @@ protected void SetWaiter(HandleablePromiseBase waiter)

internal override void AddWaiter(HandleablePromiseBase waiter, ref ExecutionScheduler executionScheduler)
{
#if !CSHARP_7_3_OR_NEWER // Interlocked.Exchange doesn't seem to work properly in Unity's old runtime. I'm not sure why, but we need a lock here to pass multi-threaded tests.
lock (this)
#endif
ThrowIfInPool(this);
// When this is completed, State is set then _waiter is swapped, so we must reverse that process here.
Thread.MemoryBarrier();
SetWaiter(waiter);
Thread.MemoryBarrier(); // Make sure State is read after _waiter is written.
if (State != Promise.State.Pending)
{
ThrowIfInPool(this);
// When this is completed, State is set then _waiter is swapped, so we must reverse that process here.
Thread.MemoryBarrier();
SetWaiter(waiter);
Thread.MemoryBarrier(); // Make sure State is read after _waiter is written.
if (State != Promise.State.Pending)
{
// Interlocked.Exchange to handle race condition with Handle on another thread.
MaybeHandleNext(Interlocked.Exchange(ref _waiter, null), ref executionScheduler);
}
else
{
MaybeDispose();
}
// Interlocked.Exchange to handle race condition with Handle on another thread.
var nextHandler = Interlocked.Exchange(ref _waiter, null);
MaybeHandleNext(nextHandler, ref executionScheduler);
}
else
{
MaybeDispose();
}
}

internal override void AddWaiter(HandleablePromiseBase waiter, out HandleablePromiseBase nextHandler, ref ExecutionScheduler executionScheduler)
{
#if !CSHARP_7_3_OR_NEWER // Interlocked.Exchange doesn't seem to work properly in Unity's old runtime. I'm not sure why, but we need a lock here to pass multi-threaded tests.
lock (this)
#endif
ThrowIfInPool(this);
// When this is completed, State is set then _waiter is swapped, so we must reverse that process here.
Thread.MemoryBarrier();
SetWaiter(waiter);
Thread.MemoryBarrier(); // Make sure State is read after _waiter is written.
if (State != Promise.State.Pending)
{
ThrowIfInPool(this);
// When this is completed, State is set then _waiter is swapped, so we must reverse that process here.
Thread.MemoryBarrier();
SetWaiter(waiter);
Thread.MemoryBarrier(); // Make sure State is read after _waiter is written.
if (State != Promise.State.Pending)
{
// Interlocked.Exchange to handle race condition with Handle on another thread.
nextHandler = Interlocked.Exchange(ref _waiter, null);
}
else
{
nextHandler = null;
}
// Interlocked.Exchange to handle race condition with Handle on another thread.
nextHandler = Interlocked.Exchange(ref _waiter, null);
}
else
{
nextHandler = null;
}
}

Expand Down Expand Up @@ -462,13 +453,8 @@ protected virtual void Execute(ref PromiseRef handler, out HandleablePromiseBase
internal void SetResultAndMaybeHandle(ValueContainer valueContainer, Promise.State state, out HandleablePromiseBase nextHandler, ref ExecutionScheduler executionScheduler)
{
SetResult(valueContainer, state);
#if !CSHARP_7_3_OR_NEWER // Interlocked.Exchange doesn't seem to work properly in Unity's old runtime. I'm not sure why, but we need a lock here to pass multi-threaded tests.
lock (this)
#endif
{
Thread.MemoryBarrier(); // Make sure previous writes are done before swapping _waiter.
nextHandler = Interlocked.Exchange(ref _waiter, null);
}
Thread.MemoryBarrier(); // Make sure previous writes are done before swapping _waiter.
nextHandler = Interlocked.Exchange(ref _waiter, null);
#if PROTO_PROMISE_NO_STACK_UNWIND
MaybeHandleNext(nextHandler, ref executionScheduler);
nextHandler = null;
Expand All @@ -489,13 +475,9 @@ internal void HandleSelf(ref PromiseRef handler, out HandleablePromiseBase nextH
internal void SetResultAndMaybeHandleFromCatch(ValueContainer valueContainer, Promise.State state, out HandleablePromiseBase nextHandler, ref ExecutionScheduler executionScheduler)
{
SetResult(valueContainer, state);
#if !CSHARP_7_3_OR_NEWER // Interlocked.Exchange doesn't seem to work properly in Unity's old runtime. I'm not sure why, but we need a lock here to pass multi-threaded tests.
lock (this)
#endif
{
Thread.MemoryBarrier(); // Make sure previous writes are done before swapping _waiter.
nextHandler = Interlocked.Exchange(ref _waiter, null);
}
Thread.MemoryBarrier(); // Make sure previous writes are done before swapping _waiter.
nextHandler = Interlocked.Exchange(ref _waiter, null);

HandleProgressListener(state, Depth, ref executionScheduler);
#if PROTO_PROMISE_NO_STACK_UNWIND
MaybeHandleNext(nextHandler, ref executionScheduler);
Expand All @@ -515,13 +497,9 @@ internal abstract partial class PromiseSingleAwaitWithProgress : PromiseSingleAw
new internal void SetResultAndMaybeHandle(ValueContainer valueContainer, Promise.State state, out HandleablePromiseBase nextHandler, ref ExecutionScheduler executionScheduler)
{
SetResult(valueContainer, state);
#if !CSHARP_7_3_OR_NEWER // Interlocked.Exchange doesn't seem to work properly in Unity's old runtime. I'm not sure why, but we need a lock here to pass multi-threaded tests.
lock (this)
#endif
{
Thread.MemoryBarrier(); // Make sure previous writes are done before swapping _waiter.
nextHandler = Interlocked.Exchange(ref _waiter, null);
}
Thread.MemoryBarrier(); // Make sure previous writes are done before swapping _waiter.
nextHandler = Interlocked.Exchange(ref _waiter, null);

HandleProgressListener(state, Depth, ref executionScheduler);
#if PROTO_PROMISE_NO_STACK_UNWIND
MaybeHandleNext(nextHandler, ref executionScheduler);
Expand Down Expand Up @@ -774,18 +752,13 @@ internal override void Handle(ref ExecutionScheduler executionScheduler)

private void AddWaiterImpl(HandleablePromiseBase waiter, ref ExecutionScheduler executionScheduler)
{
#if !CSHARP_7_3_OR_NEWER // Interlocked.Exchange doesn't seem to work properly in Unity's old runtime. I'm not sure why, but we need a lock here to pass multi-threaded tests.
lock (this)
#endif
ThrowIfInPool(this);
Thread.MemoryBarrier();
SetWaiter(waiter);
ScheduleMethod previousScheduleType = (ScheduleMethod) Interlocked.Exchange(ref _mostRecentPotentialScheduleMethod, (int) ScheduleMethod.AddWaiter);
if (previousScheduleType == ScheduleMethod.Handle)
{
ThrowIfInPool(this);
Thread.MemoryBarrier();
SetWaiter(waiter);
ScheduleMethod previousScheduleType = (ScheduleMethod) Interlocked.Exchange(ref _mostRecentPotentialScheduleMethod, (int) ScheduleMethod.AddWaiter);
if (previousScheduleType == ScheduleMethod.Handle)
{
executionScheduler.ScheduleOnContext(_synchronizationContext, this);
}
executionScheduler.ScheduleOnContext(_synchronizationContext, this);
}
}

Expand Down Expand Up @@ -869,13 +842,9 @@ protected void HandleInternal(ValueContainer valueContainer, Promise.State state
{
SetResult(valueContainer, state);
HandleablePromiseBase nextHandler;
#if !CSHARP_7_3_OR_NEWER // Interlocked.Exchange doesn't seem to work properly in Unity's old runtime. I'm not sure why, but we need a lock here to pass multi-threaded tests.
lock (this)
#endif
{
Thread.MemoryBarrier(); // Make sure previous writes are done before swapping _waiter.
nextHandler = Interlocked.Exchange(ref _waiter, null);
}
Thread.MemoryBarrier(); // Make sure previous writes are done before swapping _waiter.
nextHandler = Interlocked.Exchange(ref _waiter, null);

var executionScheduler = new ExecutionScheduler(true);
HandleProgressListener(state, 0, ref executionScheduler);
MaybeHandleNext(nextHandler, ref executionScheduler);
Expand Down

0 comments on commit a179685

Please sign in to comment.