Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API Proposal]: Introduce TimeProvider extension method to create a CTS to be cancelled after a specified time period. #85080

Closed
tarekgh opened this issue Apr 20, 2023 · 8 comments · Fixed by #85131
Assignees
Labels
api-approved API was approved in API review, it can be implemented area-System.DateTime
Milestone

Comments

@tarekgh
Copy link
Member

tarekgh commented Apr 20, 2023

Background and motivation

A new constructor called CancellationTokenSource(TimeSpan, TimeProvider) has been introduced for creating a CTS object that automatically gets cancelled after a specified period using the TimeProvider. However, this constructor is only available on .NET 8.0. For users who are using TimeProvider on down-level versions, they will need to write their own code to achieve the same functionality. An example of such code is given below.

        private static void CancelAfter(TimeProvider provider, CancellationTokenSource cts, TimeSpan delay)
        {
            if (provider == TimeProvider.System)
            {
                cts.CancelAfter(delay);
            }
            else
            {
                ITimer timer = provider.CreateTimer(s => ((CancellationTokenSource)s).Cancel(), cts, delay, Timeout.InfiniteTimeSpan);
                cts.Token.Register(t => ((ITimer)t).Dispose(), timer);
            }
        }

The experience would be unsatisfactory when users want to write a ns2.0 library code that works in all supported framework versions.

API Proposal

namespace System.Threading.Tasks
{
    public static class TimeProviderTaskExtensions
    {

        /// <summary>Initializes a new instance of the <see cref="CancellationTokenSource"/> class that will be canceled after the specified <see cref="TimeSpan"/>. </summary>
        /// <param name="timeProvider">The <see cref="TimeProvider"/> with which to interpret the <paramref name="delay"/>. </param>
        /// <param name="delay">The time interval to wait before canceling this <see cref="CancellationTokenSource"/>. </param>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="delay"/>'s <see cref="TimeSpan.TotalMilliseconds"/> is less than -1 or greater than <see cref="uint.MaxValue"/> - 1. </exception>
        /// <remarks>
        /// The countdown for the delay starts during the call to the constructor.  When the delay expires,
        /// the constructed <see cref="CancellationTokenSource"/> is canceled if it has
        /// not been canceled already. Subsequent calls to CancelAfter will reset the delay for the constructed
        /// <see cref="CancellationTokenSource"/> if it has not been canceled already.
        /// </remarks>
        public static CancellationTokenSource CreateCancellationTokenSource(this TimeProvider timeProvider, TimeSpan delay) ;
    }
}

API Usage

CancellationTokenSource  cts = TimeProvider.System.CreateCancellationTokenSource(TimeSpan.FromMilliseconds(100));

....

cts.Task.WaitAsync(CancellationToken.None);

Alternative Designs

No response

Risks

No response

@tarekgh tarekgh added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Apr 20, 2023
@ghost ghost added the untriaged New issue has not been triaged by the area owner label Apr 20, 2023
@ghost
Copy link

ghost commented Apr 20, 2023

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

Issue Details

Background and motivation

A new constructor called CancellationTokenSource(TimeSpan, TimeProvider) has been introduced for creating a CTS object that automatically gets cancelled after a specified period using the TimeProvider. However, this constructor is only available on .NET 8.0. For users who are using TimeProvider on down-level versions, they will need to write their own code to achieve the same functionality. An example of such code is given below.

        private static void CancelAfter(TimeProvider provider, CancellationTokenSource cts, TimeSpan delay)
        {
            if (provider == TimeProvider.System)
            {
                cts.CancelAfter(delay);
            }
            else
            {
                ITimer timer = provider.CreateTimer(s => ((CancellationTokenSource)s).Cancel(), cts, delay, Timeout.InfiniteTimeSpan);
                cts.Token.Register(t => ((ITimer)t).Dispose(), timer);
            }
        }

The experience would be unsatisfactory when users want to write a ns2.0 library code that works in all supported framework versions.

API Proposal

namespace System.Threading.Tasks
{
    public static class TimeProviderTaskExtensions
    {

        /// <summary>Initializes a new instance of the <see cref="CancellationTokenSource"/> class that will be canceled after the specified <see cref="TimeSpan"/>. </summary>
        /// <param name="timeProvider">The <see cref="TimeProvider"/> with which to interpret the <paramref name="delay"/>. </param>
        /// <param name="delay">The time interval to wait before canceling this <see cref="CancellationTokenSource"/>. </param>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="delay"/>'s <see cref="TimeSpan.TotalMilliseconds"/> is less than -1 or greater than <see cref="uint.MaxValue"/> - 1. </exception>
        /// <remarks>
        /// The countdown for the delay starts during the call to the constructor.  When the delay expires,
        /// the constructed <see cref="CancellationTokenSource"/> is canceled if it has
        /// not been canceled already. Subsequent calls to CancelAfter will reset the delay for the constructed
        /// <see cref="CancellationTokenSource"/> if it has not been canceled already.
        /// </remarks>
        public static CancellationTokenSource CreateCancellationTokenSource(this TimeProvider timeProvider, TimeSpan delay) ;
    }
}

API Usage

CancellationTokenSource  cts = TimeProvider.System.CreateCancellationTokenSource(TimeSpan.FromMilliseconds(100));

....

cts.Task.WaitAsync(CancellationToken.None);

Alternative Designs

No response

Risks

No response

Author: tarekgh
Assignees: -
Labels:

api-suggestion, area-System.Threading

Milestone: -

@ghost
Copy link

ghost commented Apr 20, 2023

Tagging subscribers to this area: @dotnet/area-system-datetime
See info in area-owners.md if you want to be subscribed.

Issue Details

Background and motivation

A new constructor called CancellationTokenSource(TimeSpan, TimeProvider) has been introduced for creating a CTS object that automatically gets cancelled after a specified period using the TimeProvider. However, this constructor is only available on .NET 8.0. For users who are using TimeProvider on down-level versions, they will need to write their own code to achieve the same functionality. An example of such code is given below.

        private static void CancelAfter(TimeProvider provider, CancellationTokenSource cts, TimeSpan delay)
        {
            if (provider == TimeProvider.System)
            {
                cts.CancelAfter(delay);
            }
            else
            {
                ITimer timer = provider.CreateTimer(s => ((CancellationTokenSource)s).Cancel(), cts, delay, Timeout.InfiniteTimeSpan);
                cts.Token.Register(t => ((ITimer)t).Dispose(), timer);
            }
        }

The experience would be unsatisfactory when users want to write a ns2.0 library code that works in all supported framework versions.

API Proposal

namespace System.Threading.Tasks
{
    public static class TimeProviderTaskExtensions
    {

        /// <summary>Initializes a new instance of the <see cref="CancellationTokenSource"/> class that will be canceled after the specified <see cref="TimeSpan"/>. </summary>
        /// <param name="timeProvider">The <see cref="TimeProvider"/> with which to interpret the <paramref name="delay"/>. </param>
        /// <param name="delay">The time interval to wait before canceling this <see cref="CancellationTokenSource"/>. </param>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="delay"/>'s <see cref="TimeSpan.TotalMilliseconds"/> is less than -1 or greater than <see cref="uint.MaxValue"/> - 1. </exception>
        /// <remarks>
        /// The countdown for the delay starts during the call to the constructor.  When the delay expires,
        /// the constructed <see cref="CancellationTokenSource"/> is canceled if it has
        /// not been canceled already. Subsequent calls to CancelAfter will reset the delay for the constructed
        /// <see cref="CancellationTokenSource"/> if it has not been canceled already.
        /// </remarks>
        public static CancellationTokenSource CreateCancellationTokenSource(this TimeProvider timeProvider, TimeSpan delay) ;
    }
}

API Usage

CancellationTokenSource  cts = TimeProvider.System.CreateCancellationTokenSource(TimeSpan.FromMilliseconds(100));

....

cts.Task.WaitAsync(CancellationToken.None);

Alternative Designs

No response

Risks

No response

Author: tarekgh
Assignees: -
Labels:

api-suggestion, untriaged, area-System.DateTime

Milestone: -

@tarekgh tarekgh removed the untriaged New issue has not been triaged by the area owner label Apr 20, 2023
@tarekgh tarekgh added this to the 8.0.0 milestone Apr 20, 2023
@tarekgh tarekgh self-assigned this Apr 20, 2023
@tarekgh
Copy link
Member Author

tarekgh commented Apr 20, 2023

CC @stephentoub @sebastienros

@tarekgh tarekgh added api-ready-for-review API is ready for review, it is NOT ready for implementation blocking Marks issues that we want to fast track in order to unblock other important work and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation labels Apr 20, 2023
@tarekgh tarekgh changed the title [API Proposal]: [API Proposal]: Introduce TimeProvider extension method to create a CTS to be cancelled after a specified time period. Apr 20, 2023
@tarekgh
Copy link
Member Author

tarekgh commented Apr 20, 2023

#36617

@bartonjs
Copy link
Member

bartonjs commented Apr 20, 2023

Video

On the basis that this is only provided in the Microsoft.Bcl.TimeProvider compat-package, and that TimeProviderTaskExtensions already exists, this looks good as proposed.

namespace System.Threading.Tasks
{
    public static partial class TimeProviderTaskExtensions
    {

        /// <summary>Initializes a new instance of the <see cref="CancellationTokenSource"/> class that will be canceled after the specified <see cref="TimeSpan"/>. </summary>
        /// <param name="timeProvider">The <see cref="TimeProvider"/> with which to interpret the <paramref name="delay"/>. </param>
        /// <param name="delay">The time interval to wait before canceling this <see cref="CancellationTokenSource"/>. </param>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="delay"/>'s <see cref="TimeSpan.TotalMilliseconds"/> is less than -1 or greater than <see cref="uint.MaxValue"/> - 1. </exception>
        /// <remarks>
        /// The countdown for the delay starts during the call to the constructor.  When the delay expires,
        /// the constructed <see cref="CancellationTokenSource"/> is canceled if it has
        /// not been canceled already. Subsequent calls to CancelAfter will reset the delay for the constructed
        /// <see cref="CancellationTokenSource"/> if it has not been canceled already.
        /// </remarks>
        public static CancellationTokenSource CreateCancellationTokenSource(this TimeProvider timeProvider, TimeSpan delay) ;
    }
}

@bartonjs bartonjs added api-approved API was approved in API review, it can be implemented and removed blocking Marks issues that we want to fast track in order to unblock other important work api-ready-for-review API is ready for review, it is NOT ready for implementation labels Apr 20, 2023
@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Apr 20, 2023
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Apr 24, 2023
@egil
Copy link

egil commented Apr 26, 2023

@tarekgh I know this is closed, but wonder if you considered the following extension method as an alternative?

/// <summary>
/// Schedules a Cancel operation on the <paramref name="cancellationTokenSource"/>.
/// </summary>
/// <param name="cancellationTokenSource">
/// The <see cref="CancellationTokenSource"/> to cancel after the specified delay.
/// </param>
/// <param name="delay">The time span to wait before canceling the <paramref name="cancellationTokenSource"/>.
/// </param>
/// <param name="timeProvider">
/// The <see cref="TimeProvider"/> to use for scheduling the cancellation.
/// </param>
/// <exception cref="ObjectDisposedException">The exception thrown when the <paramref name="cancellationTokenSource"/>
/// has been disposed.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// The <paramref name="delay"/> is less than -1 or greater than maximum allowed timer duration.
/// </exception>
/// <exception cref="ArgumentNullException">The <paramref name="cancellationTokenSource"/> is null.</exception>
/// <remarks>
/// <para>
/// The countdown for the delay starts during this call. When the delay expires,
/// the <paramref name="cancellationTokenSource"/> is canceled, if it has
/// not been canceled already.
/// </para>
/// <para>
/// Subsequent calls to CancelAfter will reset the delay for the
/// <paramref name="cancellationTokenSource"/>, if it has not been canceled already.
/// </para>
/// </remarks>
public static void CancelAfter(this CancellationTokenSource cancellationTokenSource, TimeSpan delay, TimeProvider timeProvider);

@tarekgh
Copy link
Member Author

tarekgh commented Apr 26, 2023

@egil you may review the detailed comment #36617 (comment) why we are trying to avoid CancelAfter.

@egil
Copy link

egil commented Apr 26, 2023

Thanks for the link.

@ghost ghost locked as resolved and limited conversation to collaborators May 27, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved API was approved in API review, it can be implemented area-System.DateTime
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants