public long AddParticipants(int participantCount)
{
// check dispose
ThrowIfDisposed();
if (participantCount < 1)
{
throw new ArgumentOutOfRangeException(nameof(participantCount), participantCount,
SR.Barrier_AddParticipants_NonPositive_ArgumentOutOfRange);
}
else if (participantCount > MAX_PARTICIPANTS) //overflow
{
throw new ArgumentOutOfRangeException(nameof(participantCount),
SR.Barrier_AddParticipants_Overflow_ArgumentOutOfRange);
}
// in case of this is called from the PHA
if (_actionCallerID != 0 && Environment.CurrentManagedThreadId == _actionCallerID)
{
throw new InvalidOperationException(SR.Barrier_InvalidOperation_CalledFromPHA);
}
SpinWait spinner = new SpinWait();
long newPhase = 0;
while (true)
{
int currentTotal = _currentTotalCount;
int total;
int current;
bool sense;
GetCurrentTotal(currentTotal, out current, out total, out sense);
if (participantCount + total > MAX_PARTICIPANTS) //overflow
{
throw new ArgumentOutOfRangeException(nameof(participantCount),
SR.Barrier_AddParticipants_Overflow_ArgumentOutOfRange);
}
if (SetCurrentTotal(currentTotal, current, total + participantCount, sense))
{
// Calculating the first phase for that participant, if the current phase already finished return the nextphase else return the current phase
// To know that the current phase is the sense doesn't match the
// phase odd even, so that means it didn't yet change the phase count, so currentPhase +1 is returned, otherwise currentPhase is returned
long currPhase = CurrentPhaseNumber;
newPhase = (sense != (currPhase % 2 == 0)) ? currPhase + 1 : currPhase;
// If this participant is going to join the next phase, which means the postPhaseAction is being running, this participants must wait until this done
// and its event is reset.
// Without that, if the postPhaseAction takes long time, this means the event ehich the current participant is goint to wait on is still set
// (FinishPPhase didn't reset it yet) so it should wait until it reset
if (newPhase != currPhase)
{
// Wait on the opposite event
if (sense)
{
_oddEvent.Wait();
}
else
{
_evenEvent.Wait();
}
}
//This else to fix the racing where the current phase has been finished, m_currentPhase has been updated but the events have not been set/reset yet
// otherwise when this participant calls SignalAndWait it will wait on a set event however all other participants have not arrived yet.
else
{
if (sense && _evenEvent.IsSet)
_evenEvent.Reset();
else if (!sense && _oddEvent.IsSet)
_oddEvent.Reset();
}
break;
}
spinner.SpinOnce();
}
return newPhase;
}