-3

I am working on a project that needs to block a running thread for a time span that can vary between one second and several months.

The approach I came up with was to use the EventWaitHandle.WaitOne method (or any of its siblings) with a timeout specified. The problem is that all these methods take an Int32 as a parameter which caps the maximum block time to roughly 25 days.

Does anybody know a solution to this? How can I block a thread for longer that Int32.MaxValue milliseconds?

thanks

UPDATE

Just for the records, here's the code snippet I finally came up with:

while(_doRun)
{
  // Determine the next trigger time
  var nextOccurence = DetermineNextOccurence();
  var sleepSpan = nextOccurence - DateTime.Now;

  // if the next occurence is more than Int32.MaxValue millisecs away,
  // loop to work around the limitations of EventWaitHandle.WaitOne()
  if (sleepSpan.TotalMilliseconds > Int32.MaxValue) 
  {
    var idleTime = GetReasonableIdleWaitTimeSpan();
    var iterationCount = Math.Truncate(sleepSpan.TotalMilliseconds / idleTime.TotalMilliseconds);
    for (var i = 0; i < iterationCount; i++)
    {
      // Wait for the idle timespan (or until a Set() is called).
      if(_ewh.WaitOne(idleTime)) { break; }
    }
  }
  else
  {
    // if the next occurence is in the past, trigger right away
    if (sleepSpan.TotalMilliseconds < 0) { sleepSpan = TimeSpan.FromMilliseconds(25); }

    // Wait for the sleep span (or until a Set() is called).
    if (!_ewh.WaitOne(sleepSpan))
    {
      // raise the trigger event
      RaiseTriggerEvent();
    }
  }
}

That snippet is the code executed by a dedicated thread. Note that EventWaitHandle.Set() is only called when the Application quits or whishes to cancel the scheduler.

Thanks to those willing to help.

JuiceOntour
  • 110
  • 4
  • You really think that your application can run that long? It will certainly crash on day 23 during testing – Thomas Weller Nov 16 '16 at 12:50
  • 11
    Sounds to me like something is terribly wrong with the design. – 404 Nov 16 '16 at 12:51
  • You should start your thread on some event. Or use a timer that will not be wasted to take the thread. – Alexander Petrov Nov 16 '16 at 13:03
  • You may want to take a look to a scheduler: http://www.quartz-scheduler.net/ – vtortola Nov 16 '16 at 13:21
  • 1
    Threads are workers and their unit of work is measured in CPU cycles. You wouldn't hire a worker and then pay them to sleep for a trillion seconds, so don't hire a thread and pay it to sleep for a trillion CPU cycles. Threads aren't designed to be long running transactions; use a different mechanism. – Eric Lippert Nov 16 '16 at 13:35
  • Threading.Timer has an overloaded constructor that takes Int64 and another that takes UInt32 to set up the timer. – Kevin Nov 16 '16 at 13:35
  • @ThomasWeller: Of course, no application will run for months without being stopped or rebooted. I am totally aware of this. Consider this: I must execute a given task on Christmas eve, 11 pm. Each time when starting the application, I can calculate the sleep span until the said date and time and wait for the time span. The application can reboot as many times as you like, it will still trigger the job on Christmas eve, 11 pm. – JuiceOntour Nov 16 '16 at 13:44
  • @vtortola thanks for the hint. I know of quartz.net with its superb features. In fact, I wanted something a bit less full-featured and that’s why I came up with my own implementation. – JuiceOntour Nov 16 '16 at 13:55
  • @eurotrash telling me that something is wrong with the design is to easy an answer if the use case behind it is not known… – JuiceOntour Nov 16 '16 at 13:58
  • 1
    @JuiceOntour True, but I feel quite confident in saying that any case that requires a thread to potentially be blocked for int.MaxValue or more can be redesigned to not require that. – 404 Nov 16 '16 at 14:18

1 Answers1

0

Try handle.WaitOne(System.Threading.Timeout.Infinite).

If you don't want it to run infinitely, trigger the wait handle externally from a different thread.

UPDATE:

If you don't want to use another thread, use a loop:

bool isTriggered = false;
while (!isTriggered) {
    isTriggered = handle.WaitOne(timeout);
    //Check if time is expired and if yes, break
}

You have to split your timeout timespan into multiple chunks that will fit into an Int32. The isTriggered variable will show you if the handle was triggered or if it timed out.

Sefe
  • 13,731
  • 5
  • 42
  • 55
  • @Sefe I didn't down-vote (not enough reputation points anyway). But the work-around you're proposing will require a different thread to know about the time passing by. – JuiceOntour Nov 16 '16 at 13:32
  • @JuiceOntour: Sorry for my rant. Anyway, are you sure TimeSpan doesn't work? Anyway, if you want to avoid a thread, wait in a loop. See my edit. – Sefe Nov 16 '16 at 13:34
  • @Sefe. Thanks. Here's roughly what the guys at Quartz.Net do (I just had a look at it): Inside the loop they check whether the time-to-wait is within acceptable bounds. If yes they block the thread for this exact time. If not, they block the thread for a semi-randomized time and check again. Of course that's explained with my words and only with a limited time spent studying the code. Sorry guys from Quartz.Net if I over simplified... – JuiceOntour Nov 16 '16 at 16:02