src/Microsoft.IdentityModel.Tokens/EventBasedLRUCache.cs
/// <summary>
/// This method is called after an item is added to the event queue. It will start the event queue task if one is not already running (_eventQueueTaskState != EventQueueTaskRunning).
/// Using CompareExchange to set the _eventQueueTaskState prevents multiple tasks from being started.
/// </summary>
This method is called after an item is added to the event queue. It will start the event queue task if one is not already running (_eventQueueTaskState != EventQueueTaskRunning).
Using CompareExchange to set the _eventQueueTaskState prevents multiple tasks from being started.
This method is called after an item is added to the event queue . It will start the event queue task if one is not already running ( _eventQueueTaskState ! = EventQueueTaskRunning ) . Using CompareExchange to set the _eventQueueTaskState prevents multiple tasks from being started .
private void StartEventQueueTaskIfNotRunning()
        {
            _eventQueueTaskStopTime = SetTaskEndTime(); 
             Without the EventQueueTaskDoNotStop state check below, steps (3), (4) and (5) above will not be applicable.
             After step (2) the event queue task is still running and the state is still EventQueueTaskRunning (even though the EventQueueTaskAction() method has already checked that the queue is empty
             and is about to stop the task). This method (StartEventQueueTaskIfNotRunning()) will return, the execution will switch over to EventQueueTaskAction(),
             and the task will terminate. This means no new task would be started to process the newly added event.
             This scenario is unlikely to happen, as it can only occur if the event queue task ALREADY checked the queue and it was empty, and the new event was added AFTER that check but BEFORE the
             event queue task set the _eventQueueTaskState to EventQueueTaskStopped.
            if (Interlocked.CompareExchange(ref _eventQueueTaskState, EventQueueTaskDoNotStop, EventQueueTaskRunning) == EventQueueTaskRunning)
            {
                return;
            }
             If the task is stopped, set _eventQueueTaskState = EventQueueTaskRunning and start a new task.
             Note: we need to call the Task.Run() to start a new task on the default TaskScheduler (TaskScheduler.Default) so it does not interfere with
             the caller's TaskScheduler (if there is one) as some custom TaskSchedulers might be single-threaded and its execution can be blocked.
            if (Interlocked.CompareExchange(ref _eventQueueTaskState, EventQueueTaskRunning, EventQueueTaskStopped) == EventQueueTaskStopped)
            {
                Task.Run(EventQueueTaskAction);
            }
        }
private void StartEventQueueTaskIfNotRunning ( ) { _eventQueueTaskStopTime = SetTaskEndTime ( ) ; if ( Interlocked . CompareExchange ( ref _eventQueueTaskState , EventQueueTaskDoNotStop , EventQueueTaskRunning ) == EventQueueTaskRunning ) { return ; } if ( Interlocked . CompareExchange ( ref _eventQueueTaskState , EventQueueTaskRunning , EventQueueTaskStopped ) == EventQueueTaskStopped ) { Task . Run ( EventQueueTaskAction ) ; } }
This method is called after an item is added to the event queue.
This method is called after an item is added to the event queue .
// set the time when the _eventQueueTask should end // Setting _eventQueueTaskState to EventQueueTaskDoNotStop here will force the event queue task in EventQueueTaskAction to continue even it has past the _eventQueueTaskEndTime. // It is mainly to prevent a rare (but theoretically possible) scenario caused by context switching // For example: //   1. the task execution in EventQueueTaskAction() checks event queue and it is empty (ready to exit) //   2. the execution is switched to this thread (before the event queue task calls the Interlocked.CompareExchange() to set the _eventQueueTaskState to EventQueueTaskStopped) //   3. now since the _eventQueueTaskState == EventQueueTaskRunning, it can be set to EventQueueTaskDoNotStop by the Interlocked.CompareExchange() below //   4. if _eventQueueTaskState is successfully set to EventQueueTaskDoNotStop, the Interlocked.CompareExchange() in the EventQueueTaskAction() will fail //      and the task will continue the while loop and the new event will keep the task running //   5. if _eventQueueTaskState is NOT set to EventQueueTaskDoNotStop because execution switches back to the EventQueueTaskAction() and the _eventQueueTaskState is //      set to EventQueueTaskStopped (task exits), then the second Interlocked.CompareExchange() below should set the _eventQueueTaskState to EventQueueTaskRunning //      and start a task again (though this scenario is unlikely to happen) // // Without the EventQueueTaskDoNotStop state check below, steps (3), (4) and (5) above will not be applicable. // After step (2) the event queue task is still running and the state is still EventQueueTaskRunning (even though the EventQueueTaskAction() method has already checked that the queue is empty // and is about to stop the task). This method (StartEventQueueTaskIfNotRunning()) will return, the execution will switch over to EventQueueTaskAction(), // and the task will terminate. This means no new task would be started to process the newly added event. // // This scenario is unlikely to happen, as it can only occur if the event queue task ALREADY checked the queue and it was empty, and the new event was added AFTER that check but BEFORE the // event queue task set the _eventQueueTaskState to EventQueueTaskStopped. // If the task is stopped, set _eventQueueTaskState = EventQueueTaskRunning and start a new task. // Note: we need to call the Task.Run() to start a new task on the default TaskScheduler (TaskScheduler.Default) so it does not interfere with // the caller's TaskScheduler (if there is one) as some custom TaskSchedulers might be single-threaded and its execution can be blocked.