fix: Optimizes EventScheduler by using PriorityQueue (#2167)
This commit is contained in:
parent
21a4092dd8
commit
415c7a6bfd
2 changed files with 30 additions and 61 deletions
|
|
@ -86,7 +86,7 @@ public class EventSchedulerTests
|
|||
Assert.True(called);
|
||||
Assert.Equal(Core._now, evt.NextOccurrence);
|
||||
|
||||
EventScheduler.Shared.StopEvent(evt);
|
||||
evt.Cancel();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -131,9 +131,9 @@ public class EventSchedulerTests
|
|||
// Verify they executed in time order, not addition order
|
||||
Assert.Equal([1, 2, 3], executionOrder);
|
||||
|
||||
EventScheduler.Shared.StopEvent(evt1);
|
||||
EventScheduler.Shared.StopEvent(evt2);
|
||||
EventScheduler.Shared.StopEvent(evt3);
|
||||
evt1.Cancel();
|
||||
evt2.Cancel();
|
||||
evt3.Cancel();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -169,7 +169,7 @@ public class EventSchedulerTests
|
|||
Assert.Equal(3, callCount);
|
||||
Assert.Equal(3, evt.CallCount);
|
||||
|
||||
EventScheduler.Shared.StopEvent(evt);
|
||||
evt.Cancel();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -211,8 +211,8 @@ public class EventSchedulerTests
|
|||
Assert.True(failedEventCalled);
|
||||
Assert.True(laterEventCalled);
|
||||
|
||||
EventScheduler.Shared.StopEvent(failedEvt);
|
||||
EventScheduler.Shared.StopEvent(laterEvt);
|
||||
failedEvt.Cancel();
|
||||
laterEvt.Cancel();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -235,7 +235,7 @@ public class EventSchedulerTests
|
|||
);
|
||||
|
||||
// Remove before execution
|
||||
EventScheduler.Shared.StopEvent(evt);
|
||||
evt.Cancel();
|
||||
|
||||
// Advance time
|
||||
Core._now = Core._now.AddSeconds(20);
|
||||
|
|
|
|||
|
|
@ -33,15 +33,12 @@ public enum DaysOfWeek : byte
|
|||
|
||||
public abstract class ScheduledEvent
|
||||
{
|
||||
private static Serial _nextSerial = (Serial)1;
|
||||
|
||||
// Tie-breaker for sorted set
|
||||
public Serial Serial { get; }
|
||||
public IRecurrencePattern Recurrence { get; }
|
||||
public TimeZoneInfo TimeZone { get; }
|
||||
public TimeOnly Time { get; }
|
||||
public DateTime EndDate { get; }
|
||||
public DateTime NextOccurrence { get; private set; }
|
||||
public bool Cancelled { get; private set; }
|
||||
|
||||
public ScheduledEvent(DateTime startOn, TimeZoneInfo timeZone = null)
|
||||
: this(startOn, startOn, TimeOnly.FromDateTime(startOn), null, timeZone)
|
||||
|
|
@ -61,7 +58,6 @@ public abstract class ScheduledEvent
|
|||
TimeZoneInfo timeZone = null
|
||||
)
|
||||
{
|
||||
Serial = _nextSerial++;
|
||||
Time = time;
|
||||
Recurrence = recurrence;
|
||||
TimeZone = timeZone ?? TimeZoneInfo.Utc;
|
||||
|
|
@ -71,18 +67,19 @@ public abstract class ScheduledEvent
|
|||
EndDate = endOn == DateTime.MaxValue || endOn.Kind == DateTimeKind.Utc ? endOn : endOn.LocalToUtc(TimeZone);
|
||||
}
|
||||
|
||||
public bool Advance()
|
||||
public void Cancel() => Cancelled = true;
|
||||
|
||||
public DateTime Advance()
|
||||
{
|
||||
OnEvent();
|
||||
|
||||
var next = Recurrence?.GetNextOccurrence(NextOccurrence, Time, TimeZone) ?? DateTime.MaxValue;
|
||||
if (next == DateTime.MaxValue || next > EndDate)
|
||||
{
|
||||
return false;
|
||||
return DateTime.MaxValue;
|
||||
}
|
||||
|
||||
NextOccurrence = next;
|
||||
return true;
|
||||
return NextOccurrence = next;
|
||||
}
|
||||
|
||||
public abstract void OnEvent();
|
||||
|
|
@ -92,7 +89,7 @@ public class EventScheduler : Timer
|
|||
{
|
||||
private static readonly ILogger logger = LogFactory.GetLogger(typeof(EventScheduler));
|
||||
|
||||
private readonly SortedSet<ScheduledEvent> _schedule = new(ScheduledEventComparer.Default);
|
||||
private readonly PriorityQueue<ScheduledEvent, DateTime> _schedule = new();
|
||||
|
||||
public static EventScheduler Shared { get; private set; }
|
||||
|
||||
|
|
@ -182,15 +179,7 @@ public class EventScheduler : Timer
|
|||
{
|
||||
if (entry != null)
|
||||
{
|
||||
_schedule.Add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
public void StopEvent(ScheduledEvent entry)
|
||||
{
|
||||
if (entry != null)
|
||||
{
|
||||
_schedule.Remove(entry);
|
||||
_schedule.Enqueue(entry, entry.NextOccurrence);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -200,55 +189,35 @@ public class EventScheduler : Timer
|
|||
|
||||
while (_schedule.Count > 0)
|
||||
{
|
||||
var entry = _schedule.Min!;
|
||||
if (entry.NextOccurrence > now)
|
||||
var entry = _schedule.Peek();
|
||||
var cancelled = entry.Cancelled;
|
||||
if (!cancelled && entry.NextOccurrence > now)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
_schedule.Remove(entry);
|
||||
_schedule.Dequeue();
|
||||
|
||||
bool advance;
|
||||
if (cancelled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DateTime nextOccurrence;
|
||||
try
|
||||
{
|
||||
advance = entry.Advance();
|
||||
nextOccurrence = entry.Advance();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error(e, "Error while executing scheduled event.");
|
||||
advance = false;
|
||||
nextOccurrence = DateTime.MaxValue;
|
||||
}
|
||||
|
||||
if (advance && entry.NextOccurrence < DateTime.MaxValue)
|
||||
if (nextOccurrence < DateTime.MaxValue)
|
||||
{
|
||||
_schedule.Add(entry);
|
||||
_schedule.Enqueue(entry, nextOccurrence);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ScheduledEventComparer : IComparer<ScheduledEvent>
|
||||
{
|
||||
public static readonly ScheduledEventComparer Default = new();
|
||||
|
||||
public int Compare(ScheduledEvent x, ScheduledEvent y)
|
||||
{
|
||||
if (x == null && y == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (x == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var next = x.NextOccurrence.CompareTo(y.NextOccurrence);
|
||||
return next != 0 ? next : x.Serial.CompareTo(y.Serial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue