Skip to content

Commit

Permalink
Non-reentrant timers
Browse files Browse the repository at this point in the history
  • Loading branch information
ReubenBond committed May 16, 2024
1 parent 64cd675 commit 75ac054
Show file tree
Hide file tree
Showing 35 changed files with 2,473 additions and 1,929 deletions.
30 changes: 25 additions & 5 deletions src/Orleans.Core.Abstractions/Core/Grain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Orleans.Core;
using Orleans.Runtime;
using Orleans.Serialization.TypeSystem;
using Orleans.Timers;

namespace Orleans
{
Expand Down Expand Up @@ -89,19 +90,38 @@ protected Grain(IGrainContext grainContext, IGrainRuntime? grainRuntime = null)
/// will be logged, but will not prevent the next timer tick from being queued.
/// </para>
/// </remarks>
/// <param name="asyncCallback">Callback function to be invoked when timer ticks.</param>
/// <param name="callback">Callback function to be invoked when timer ticks.</param>
/// <param name="state">State object that will be passed as argument when calling the asyncCallback.</param>
/// <param name="dueTime">Due time for first timer tick.</param>
/// <param name="period">Period of subsequent timer ticks.</param>
/// <returns>Handle for this Timer.</returns>
/// <seealso cref="IDisposable"/>
protected IGrainTimer RegisterTimer(Func<object?, Task> asyncCallback, object? state, TimeSpan dueTime, TimeSpan period)
[Obsolete("Use 'RegisterGrainTimer(callback, state, new() { DueTime = dueTime, Period = period, Interleave = true })' instead.")]
protected IGrainTimer RegisterTimer(Func<object?, Task> callback, object? state, TimeSpan dueTime, TimeSpan period)
{
if (asyncCallback == null)
throw new ArgumentNullException(nameof(asyncCallback));
ArgumentNullException.ThrowIfNull(callback);

EnsureRuntime();
return Runtime.TimerRegistry.RegisterTimer(GrainContext ?? RuntimeContext.Current, asyncCallback, state, dueTime, period);
return Runtime.TimerRegistry.RegisterTimer(GrainContext ?? RuntimeContext.Current, callback, state, dueTime, period);
}

/// <summary>
/// Creates a grain timer.
/// </summary>
/// <param name="callback">The timer callback, which will fire whenever the timer becomes due.</param>
/// <param name="state">The state object passed to the callback.</param>
/// <param name="options">
/// The options for creating the timer.
/// </param>
/// <returns>
/// An <see cref="IGrainTimer"/> instance which represents the timer.
/// </returns>
protected IGrainTimer RegisterGrainTimer<T>(Func<T, Task> callback, T state, GrainTimerCreationOptions options)
{
ArgumentNullException.ThrowIfNull(callback);

EnsureRuntime();
return Runtime.TimerRegistry.RegisterGrainTimer(GrainContext ?? RuntimeContext.Current, callback, state, options);
}

/// <summary>
Expand Down
27 changes: 3 additions & 24 deletions src/Orleans.Core.Abstractions/Core/IGrainContext.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Threading;
Expand All @@ -24,7 +25,7 @@ public interface IGrainContext : ITargetHolder, IEquatable<IGrainContext>
/// <summary>
/// Gets the grain instance, or <see langword="null"/> if the grain instance has not been set yet.
/// </summary>
object GrainInstance { get; }
object? GrainInstance { get; }

/// <summary>
/// Gets the activation id.
Expand Down Expand Up @@ -61,7 +62,7 @@ public interface IGrainContext : ITargetHolder, IEquatable<IGrainContext>
/// </summary>
/// <typeparam name="TComponent">The type used to lookup this component.</typeparam>
/// <param name="value">The component instance.</param>
void SetComponent<TComponent>(TComponent value) where TComponent : class;
void SetComponent<TComponent>(TComponent? value) where TComponent : class;

/// <summary>
/// Submits an incoming message to this instance.
Expand Down Expand Up @@ -179,28 +180,6 @@ internal interface ICollectibleGrainContext : IGrainContext
void DelayDeactivation(TimeSpan timeSpan);
}

/// <summary>
/// Provides functionality to record the creation and deletion of grain timers.
/// </summary>
internal interface IGrainTimerRegistry
{
/// <summary>
/// Signals to the registry that a timer was created.
/// </summary>
/// <param name="timer">
/// The timer.
/// </param>
void OnTimerCreated(IGrainTimer timer);

/// <summary>
/// Signals to the registry that a timer was disposed.
/// </summary>
/// <param name="timer">
/// The timer.
/// </param>
void OnTimerDisposed(IGrainTimer timer);
}

/// <summary>
/// Functionality to schedule tasks on a grain.
/// </summary>
Expand Down
110 changes: 57 additions & 53 deletions src/Orleans.Core.Abstractions/Runtime/IGrainRuntime.cs
Original file line number Diff line number Diff line change
@@ -1,60 +1,64 @@
#nullable enable
using System;
using Orleans.Core;
using Orleans.Timers;

namespace Orleans.Runtime
namespace Orleans.Runtime;

/// <summary>
/// The gateway of the <see cref="Grain"/> to the Orleans runtime. The <see cref="Grain"/> should only interact with the runtime through this interface.
/// </summary>
public interface IGrainRuntime
{
/// <summary>
/// The gateway of the <see cref="Grain"/> to the Orleans runtime. The <see cref="Grain"/> should only interact with the runtime through this interface.
/// </summary>
public interface IGrainRuntime
{
/// <summary>
/// Gets a unique identifier for the current silo.
/// There is no semantic content to this string, but it may be useful for logging.
/// </summary>
string SiloIdentity { get; }

/// <summary>
/// Gets the silo address associated with this instance.
/// </summary>
SiloAddress SiloAddress { get; }

/// <summary>
/// Gets the grain factory.
/// </summary>
IGrainFactory GrainFactory { get; }

/// <summary>
/// Gets the timer registry.
/// </summary>
ITimerRegistry TimerRegistry { get; }


/// <summary>
/// Gets the service provider.
/// </summary>
IServiceProvider ServiceProvider { get; }

/// <summary>
/// Deactivates the provided grain when it becomes idle.
/// </summary>
/// <param name="grainContext">The grain context.</param>
void DeactivateOnIdle(IGrainContext grainContext);

/// <summary>
/// Delays idle activation collection of the provided grain due to inactivity until at least the specified time has elapsed.
/// </summary>
/// <param name="grainContext">The grain context.</param>
/// <param name="timeSpan">The time to delay idle activation collection for.</param>
void DelayDeactivation(IGrainContext grainContext, TimeSpan timeSpan);

/// <summary>
/// Gets grain storage for the provided grain.
/// </summary>
/// <typeparam name="TGrainState">The grain state type.</typeparam>
/// <param name="grainContext">The grain context.</param>
/// <returns>The grain storage for the provided grain.</returns>
IStorage<TGrainState> GetStorage<TGrainState>(IGrainContext grainContext);
}
/// Gets a unique identifier for the current silo.
/// There is no semantic content to this string, but it may be useful for logging.
/// </summary>
string SiloIdentity { get; }

/// <summary>
/// Gets the silo address associated with this instance.
/// </summary>
SiloAddress SiloAddress { get; }

/// <summary>
/// Gets the grain factory.
/// </summary>
IGrainFactory GrainFactory { get; }

/// <summary>
/// Gets the timer registry.
/// </summary>
ITimerRegistry TimerRegistry { get; }

/// <summary>
/// Gets the service provider.
/// </summary>
IServiceProvider ServiceProvider { get; }

/// <summary>
/// Gets the time provider.
/// </summary>
TimeProvider TimeProvider => TimeProvider.System;

/// <summary>
/// Deactivates the provided grain when it becomes idle.
/// </summary>
/// <param name="grainContext">The grain context.</param>
void DeactivateOnIdle(IGrainContext grainContext);

/// <summary>
/// Delays idle activation collection of the provided grain due to inactivity until at least the specified time has elapsed.
/// </summary>
/// <param name="grainContext">The grain context.</param>
/// <param name="timeSpan">The time to delay idle activation collection for.</param>
void DelayDeactivation(IGrainContext grainContext, TimeSpan timeSpan);

/// <summary>
/// Gets grain storage for the provided grain.
/// </summary>
/// <typeparam name="TGrainState">The grain state type.</typeparam>
/// <param name="grainContext">The grain context.</param>
/// <returns>The grain storage for the provided grain.</returns>
IStorage<TGrainState> GetStorage<TGrainState>(IGrainContext grainContext);
}
1 change: 1 addition & 0 deletions src/Orleans.Core.Abstractions/Runtime/IGrainTimer.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Orleans.Runtime;

Expand Down
49 changes: 49 additions & 0 deletions src/Orleans.Core.Abstractions/Timers/GrainTimerCreationOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#nullable enable
using System;
using System.Threading;
using Orleans.Concurrency;

namespace Orleans.Runtime;

/// <summary>
/// Options for creating grain timers.
/// </summary>
public readonly struct GrainTimerCreationOptions()
{
/// <summary>
/// The amount of time to delay before the timer callback is invoked.
/// Specify <see cref="Timeout.InfiniteTimeSpan"/> to prevent the timer from starting.
/// Specify <see cref="TimeSpan.Zero"/> to invoke the callback promptly.
/// </summary>
public required TimeSpan DueTime { get; init; }

/// <summary>
/// The time interval between invocations of callback.
/// Specify <see cref="Timeout.InfiniteTimeSpan"/> to disable periodic signaling.
/// </summary>
public TimeSpan Period { get; init; } = Timeout.InfiniteTimeSpan;

/// <summary>
/// Gets a value indicating whether callbacks scheduled by this timer are allowed to interleave execution with other timers and grain calls.
/// Defaults to <see langword="false"/>.
/// </summary>
/// <remarks>
/// If this value is <see langword="false"/>, the timer callback will be treated akin to a grain call. If the grain scheduling this timer is reentrant
/// (i.e., it has the <see cref="ReentrantAttribute"/> attributed applied to its implementation class), the timer callback will be allowed
/// to interleave with other grain calls and timers regardless of the value of this property.
/// If this value is <see langword="true"/>, the timer callback will be allowed to interleave with other timers and grain calls.
/// </remarks>
public bool Interleave { get; init; }

/// <summary>
/// Gets a value indicating whether callbacks scheduled by this timer should extend the lifetime of the grain activation.
/// Defaults to <see langword="false"/>.
/// </summary>
/// <remarks>
/// If this value is <see langword="false"/>, timer callbacks will not extend a grain activation's lifetime.
/// If a grain is only processing this timer's callbacks and no other messages, the grain will be collected after its idle collection period expires.
/// If this value is <see langword="true"/>, timer callback will extend a grain activation's lifetime.
/// If the timer period is shorter than the grain's idle collection period, the grain will not be collected due to idleness.
/// </remarks>
public bool KeepAlive { get; init; }
}
20 changes: 19 additions & 1 deletion src/Orleans.Core.Abstractions/Timers/ITimerRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,23 @@ public interface ITimerRegistry
/// <returns>
/// An <see cref="IGrainTimer"/> instance which represents the timer.
/// </returns>
[Obsolete("Use 'RegisterGrainTimer(grainContext, callback, state, new() { DueTime = dueTime, Period = period, Interleave = true })' instead.")]
IGrainTimer RegisterTimer(IGrainContext grainContext, Func<object?, Task> callback, object? state, TimeSpan dueTime, TimeSpan period);
}

/// <summary>
/// Creates a grain timer.
/// </summary>
/// <param name="grainContext">The grain which the timer is associated with.</param>
/// <param name="callback">The timer callback, which will fire whenever the timer becomes due.</param>
/// <param name="state">The state object passed to the callback.</param>
/// <param name="options">
/// The options for creating the timer.
/// </param>
/// <typeparam name="T">
/// The type of <paramref name="state"/>.
/// </typeparam>
/// <returns>
/// An <see cref="IGrainTimer"/> instance which represents the timer.
/// </returns>
IGrainTimer RegisterGrainTimer<T>(IGrainContext grainContext, Func<T, Task> callback, T state, GrainTimerCreationOptions options);
}
Loading

0 comments on commit 75ac054

Please sign in to comment.