Skip to content

Use TimeSpan everywhere we use an int for seconds, milliseconds, and timeouts #14336

Closed

Description

@briangru

In a large software project, it's easy to mess up units for time. IE, is that Int32 seconds or milliseconds? Or maybe minutes? I've been fixing my team's source code to use the unit-agnostic TimeSpan class wherever possible. However the .NET Framework is not complete in its adoption of TimeSpan. Specifically, we don't have a Process.WaitForExit overload that takes a TimeSpan, only an Int32 for the timeout.

I suggest someone look through all .NET Framework API's for Int32 parameters containing "second", "millisecond", "ms", "timeout" (and perhaps "time") and see if there is a parallel TimeSpan-based overload. If not, please fix that.

API Proposal

(Proposed by @reflectronic)

namespace System {
    public static class GC {
+       public static GCNotificationStatus WaitForFullGCApproach(TimeSpan timeout);
+       public static GCNotificationStatus WaitForFullGCComplete(TimeSpan timeout);
    }
}

namespace System.ComponentModel.DataAnnotations {
    public class RegularExpressionAttribute {
!       This one might not end up being that useful, since people generally don't ever manipulate an instance of this attribute.
+       public TimeSpan MatchTimeout { get; }
    }
}

namespace System.Diagnostics {
    public class Process {
+       public bool WaitForExit(TimeSpan timeout);
+       public bool WaitForInputIdle(TimeSpan timeout);
    }
}

namespace System.IO {
    public class FileSystemWatcher {
+       public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, TimeSpan timeout);
    }

    public sealed class NamedPipeClientStream : PipeStream {
+       public void Connect(TimeSpan timeout);
+       public Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken = default);
    }
}

namespace System.Net.NetworkInformation {
    public class Ping {
+       public PingReply Send(IPAddress address, TimeSpan timeout, byte[]? buffer = null, PingOptions? options = null);
+       public PingReply Send(string hostNameOrAddress, TimeSpan timeout, byte[]? buffer = null, PingOptions? options = null);

!       Skipped EAP based methods; if they are desired, they can be added back in

!       I added CancellationToken because it is probably worth it. If you don't want it you can remove it.
+       public Task<PingReply> SendPingAsync(IPAddress address, TimeSpan timeout, byte[]? buffer = null, PingOptions? options = null, CancellationToken cancellationToken = default);
+       public Task<PingReply> SendPingAsync(string hostNameOrAddress, TimeSpan timeout, byte[]? buffer = null, PingOptions? options = null, CancellationToken cancellationToken = default);
    }
}

namespace System.Net.Sockets {
    public class NetworkStream : Stream {
+       public void Close(TimeSpan timeout);
    }

    public class Socket {
+       public bool Poll(TimeSpan timeout, SelectMode mode);
+       public static void Select(IList checkRead, IList checkWrite, IList checkError, TimeSpan timeout);
    }
}

namespace System.ServiceProcess {
    public class ServiceBase {
+       public void RequestAdditionalTime(TimeSpan time);
    }
}

namespace System.Threading.Tasks {
    public class Task {
+       public bool Wait(TimeSpan timeout, CancellationToken cancellationToken); 
    } 
} 

namespace System.Timers {
    public class Timer {
+       public Timer(TimeSpan interval);
    }
}

APIs that I don't know how to make better:

namespace System.IO {
    public abstract class Stream {
+       public TimeSpan ReadTimeoutTimeSpan { get; set; }
+       public TimeSpan WriteTimeoutTimeSpan { get; set; }
    }
}

namespace System.IO.Ports {
    public class SerialPort {
+       public TimeSpan ReadTimeoutTimeSpan { get; set; }
+       public TimeSpan WriteTimeoutTimeSpan { get; set; }
    }
}

namespace System.Media {
    public class SoundPlayer {
+       public TimeSpan LoadTimeoutTimeSpan { get; set; } 
    }
}

namespace System.Net.NetworkInformation {
+   public static class NetworkInformationTimeSpanExtensions {
+       public static TimeSpan GetPacketReassemblyTimeout(this IPGlobalStatistics statistics);
+       public static TimeSpan GetMaximumTransmissionTimeout(this TcpStatistics statistics);
+       public static TimeSpan GetMinimumTransmissionTimeout(this TcpStatistics statistics);
+   }
}

namespace System.Net.Sockets {
    public class Socket {
+       public TimeSpan ReceiveTimeoutTimeSpan { get; set; }
+       public TimeSpan ReceiveTimeoutTimeSpan { get; set; }
    }

    public class TcpClient {
+       public TimeSpan ReceiveTimeoutTimeSpan { get; set; }
+       public TimeSpan SendTimeoutTimeSpan { get; set; }
    }
}

namespace System.Timers {
    public class Timer {
+       public TimeSpan IntervalTimeSpan { get; set; }
    }
}

APIs which use seconds:

These suffer from the same problem as the previous group, but also only deal with seconds, meaning that most TimeSpan values would need to be rounded out.

namespace System.Data {
!   This would need to be a DIM.
    public interface IDbCommand {
+       TimeSpan CommandTimeoutTimeSpan { get; set; }
    }

+   public static class DataTimeSpanExtensions {
+       public static TimeSpan GetConnectionTimeout(this IDbConnection connection);
+   }

}

namespace System.Data.Sql {
    public sealed class SqlNotificationRequest {
+       public TimeSpan TimeoutTimeSpan { get; set; }
    }
}

namespace System.Data.SqlClient {
    public sealed class SqlBulkCopy {
+       public TimeSpan BulkCopyTimeoutTimeSpan { get; set; }
    }

    public sealed class SqlConnectionStringBuilder {
+       public TimeSpan ConnectTimeoutTimeSpan { get; set; }
+       public TimeSpan LoadBalanceTimeoutTimeSpan { get; set; }
    }

    public sealed class SqlDependency {
+       public SqlDependency(SqlCommand command, string options, TimeSpan timeout);
    }
}

namespace System.Net.Sockets {
    public class LingerOption {
+       public LingerOption(bool enable, TimeSpan time);
+       public TimeSpan LingerTimeSpan { get; set; }
    }

    public class Socket {
+       public void Close(TimeSpan timeout);
    }
}

Activity

  1. Clockwork-Muse commented on Mar 11, 2015

    @Clockwork-Muse
    Contributor

    I personally am against doing this right now.
    Not because I think the idea has no merit (on the contrary, I'm all for using a specific type that means "duration without units"), but because I'd rather do this with a type from a rebuilt date/time library (and hit all date/time related entry points, which would be a larger undertaking).

  2. ellismg commented on Mar 11, 2015

    @ellismg
    Contributor

    We think this is a good idea, even using the current type. What would be helpful is if someone could look at all the types we are going to expose (see https://github.com/dotnet/corefx-progress for a complete list) and produce a speclet of the APIs we'd need to add. Then we can take it into API review and start doing the work.

  3. self-assigned this
    on Mar 11, 2015
  4. aevitas commented on Mar 18, 2015

    @aevitas

    @ellismg The request is a compiled list of all API members included in src-full that include an indication of time in minutes, seconds, milliseconds?

  5. sharwell commented on Mar 23, 2015

    @sharwell
    Contributor

    I am very much a fan of this, and believe TimeSpan is the correct type to use.

  6. mattjohnsonpint commented on Apr 30, 2015

    @mattjohnsonpint
    Contributor

    I'm also a fan of this, as long as it is only implemented in places where time is used as a duration. (TimeSpan should not be used when referring to a time-of-day).

  7. terrajobst commented on Sep 15, 2015

    @terrajobst
    Contributor

    There is a lot of trickery due to .NET Framework compat and how we type forward contracts.

    @briangru, it would be helpful if you could distill the list of APIs that we'd need to add. Then we can break it down more.

  8. karelz commented on Nov 7, 2016

    @karelz
    Member

    We need list of APIs that should be added. Then we need to review them.

  9. Gnbrkm41 commented on Mar 29, 2019

    @Gnbrkm41
    Contributor

    Time to shine some lights on this issue? :^)

    System.GC.WaitForFullGCApproach(int millisecondsTimeout);
    System.GC.WaitForFullGCComplete(int millisecondsTimeout);
    
    System.Net.Sockets.Socket.Close(int timeout);
    System.Net.Sockets.Socket.Poll(int microSeconds, SelectMode mode)
    System.Net.Sockets.Socket.Select(IList checkRead, IList checkWrite, IList checkError, int microSeconds)
    System.Net.Sockets.NetworkStream.Close(int timeout)
    
    System.Net.Sockets.LingerOption.ctor(bool enable, int seconds)
    System.Net.Sockets.LingerOption.LingerTime // int, in seconds
    System.Security.Cryptography.Pkcs.Rfc3161TimestampTokenInfo.ctor(
                Oid policyId,
                Oid hashAlgorithmId,
                ReadOnlyMemory<byte> messageHash,
                ReadOnlyMemory<byte> serialNumber,
                DateTimeOffset timestamp,
                long? accuracyInMicroseconds = null,
                bool isOrdering = false,
                ReadOnlyMemory<byte>? nonce = null,
                ReadOnlyMemory<byte>? tsaName = null,
                X509ExtensionCollection extensions = null)

    Perhaps could automate the searching by using Roslyn to look for method / constructor parameters and property name containing "time", "second" etc. (In fact, I tried it but failed miserably)

  10. Gnbrkm41 commented on Mar 29, 2019

    @Gnbrkm41
    Contributor

    Is there any plans regarding this issue? seems abandoned considering that it's been more than two years since the last update.

    /cc @joperezr

  11. karelz commented on Mar 29, 2019

    @karelz
    Member

    @Gnbrkm41 it is in Future milestone, we do not close valid issues when we do not have time to look at them. It may be opened for longer time than "just" 2 years.
    The APIs which are new in 3.0 (e.g. LingerOption) should be changed before we ship 3.0 ... having a separate issue for that would be good.
    For the rest, we can add overloads in 3.0.

    Are you interested in picking it up?

  12. Gnbrkm41 commented on Mar 29, 2019

    @Gnbrkm41
    Contributor

    @karelz Ah, right. I see.

    If you're talking about the LingerOption, sure, I can open an issue regarding that problem.

  13. Gnbrkm41 commented on Mar 29, 2019

    @Gnbrkm41
    Contributor

    @karelz looking at the .NET API catalog, it appears that LingerOption is present since .NET Framework 1.0. Are you sure it's new in Core 3.0?

  14. 40 remaining items

  15. ghost removed
    in-prThere is an active PR which will close this issue when it is merged
    on Feb 6, 2022
  16. danmoseley commented on Mar 9, 2022

    @danmoseley
    Contributor

    Assigning to @deeprobin

  17. danmoseley commented on Mar 9, 2022

    @danmoseley
    Contributor

    Hmm, @deeprobin I can't assign to you unless you're either an org member (which you aren't) or you've commented on the issue. If you comment here, I can assign it.

  18. deeprobin commented on Mar 9, 2022

    @deeprobin
    Contributor

    @danmoseley
    Oh thanks for the info Dan. Here is my comment.

    I did apply to the Foundation a while back, but I don't know at what intervals they look at their applications 😄.

  19. deeprobin commented on Mar 11, 2022

    @deeprobin
    Contributor

    @danmoseley
    I think we could mark the issue as api-suggestion and let the 2nd group review already.

    Currently only the 1st group is approved (See #14336 (comment)). Then I can take care of the other groups in a follow-up PR.

  20. danmoseley commented on Mar 11, 2022

    @danmoseley
    Contributor

    I did apply to the Foundation a while back, but I don't know at what intervals they look at their applications

    That's different -- I'm talking about a Github security group that merely gives you the right to be assigned without a comment. If this keeps coming up, we can add you, but most contributors aren't in it.

    I think we could mark the issue as api-suggestion and let the 2nd group review already.

    I suspect that would become confusing. I suggest to open a new api suggestion issue for that 2nd group and copy over the relevant info. Then your PR can close this one.

  21. bartonjs commented on Mar 18, 2022

    @bartonjs
    Member

    I suggest to open a new api suggestion issue for that 2nd group

    Yes, please 😄.

  22. deeprobin commented on Mar 25, 2022

    @deeprobin
    Contributor

    I suspect that would become confusing. I suggest to open a new api suggestion issue for that 2nd group and copy over the relevant info. Then your PR can close this one.

    @danmoseley

    I have created an issue for the 2nd group. Should I create one for the 3rd as well?

  23. danmoseley commented on Mar 26, 2022

    @danmoseley
    Contributor

    @deeprobin I suggest putting everything remaining into that one issue.

  24. ghost added
    in-prThere is an active PR which will close this issue when it is merged
    on Mar 28, 2022
  25. Repository owner moved this from Future to Done in Triage POD for Reflection, META, etc.on Mar 29, 2022
  26. ghost removed
    in-prThere is an active PR which will close this issue when it is merged
    on Mar 29, 2022
  27. ghost locked as resolved and limited conversation to collaborators on Apr 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

api-approvedAPI was approved in API review, it can be implementedarea-Meta

Type

No type

Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions