Thread overview
Timeout around function call
Sep 22, 2020
drathier
Sep 22, 2020
Ali Çehreli
Sep 22, 2020
Imperatorn
Sep 23, 2020
drathier
Sep 23, 2020
Imperatorn
Sep 23, 2020
Ali Çehreli
Sep 23, 2020
Imperatorn
Sep 23, 2020
Imperatorn
Sep 27, 2020
drathier
September 22, 2020
What's the obvious way to put a timeout around a function call? I'm thinking a 5 or 30 second timeout, and I'm expecting it to pretty much never time out.
September 22, 2020
On 9/22/20 2:32 AM, drathier wrote:> What's the obvious way to put a timeout around a function call? I'm
> thinking a 5 or 30 second timeout, and I'm expecting it to pretty much
> never time out.

I would start a thread and use receiveTimeout():

import std.concurrency;
import std.stdio;
import std.exception;
import core.thread;

// Uncomment to see what happens upon time out.
// version = doTimeout;

void compute(int i) {
  version (doTimeout) {
    writeln("The thread is napping.");
    Thread.sleep(2.seconds);
  }
  ownerTid.send(i + 1);
}

void main() {
  auto worker = spawn(&compute, 42);
  const received = receiveTimeout(
    1.seconds,

    (int result) {
      writefln!"Received the result: %s"(result);
    }
  );

  enforce(received, "Timed out.");
}

The thread need not be one-shot: It can continue waiting for more messages until told to stop:

import std.concurrency;
import std.stdio;
import std.exception;
import core.thread;

// Uncomment to see what happens upon time out.
// version = doTimeout;

struct Done {
}

void computer() {
  bool done = false;
  while (!done) {
    receive(
      (Done _) {
        done = true;
      },

      (int i) {
        version (doTimeout) {
          writeln("The thread is napping.");
          Thread.sleep(2.seconds);
        }
        ownerTid.send(i + 1);
      }
    );
  }
}

void main() {
  // This time we use spawnLinked() so that we will receive
  // a LinkTerminated message. And the name is different and
  // the argument will be passed later with send().
  auto worker = spawnLinked(&computer);

  foreach (i; 0 .. 10) {
    worker.send(i);
    const received = receiveTimeout(
      1.seconds,

      (int result) {
        writefln!"Received the result: %s"(result);
      }
    );

    enforce(received, "Timed out.");
  }

  // Tell worker to stop.
  worker.send(Done());

  // Wait for worker to terminate.
  receiveOnly!LinkTerminated();
}

Ali

September 22, 2020
On Tuesday, 22 September 2020 at 09:32:13 UTC, drathier wrote:
> What's the obvious way to put a timeout around a function call? I'm thinking a 5 or 30 second timeout, and I'm expecting it to pretty much never time out.

You have several options. Either you use the actor model (spawn[Linked]) and send a termination message after a specified time. Or you use a task and check for yourTask.done(). Or you could create a Thread and check isRunning.

(You didn't specify what you wanted to happen and if blocking was allowed or not)
September 23, 2020
On Tuesday, 22 September 2020 at 21:55:51 UTC, Imperatorn wrote:
> On Tuesday, 22 September 2020 at 09:32:13 UTC, drathier wrote:
>> What's the obvious way to put a timeout around a function call? I'm thinking a 5 or 30 second timeout, and I'm expecting it to pretty much never time out.
>
> You have several options. Either you use the actor model (spawn[Linked]) and send a termination message after a specified time. Or you use a task and check for yourTask.done(). Or you could create a Thread and check isRunning.
>
> (You didn't specify what you wanted to happen and if blocking was allowed or not)

Blocking is perfectly fine. I'm wondering if I need things to be shared now or something? Not used to programming with threads. Adding a shared modifier recursively onto every piece of data that needs it is a ton of work though.

I don't want to copy the data around since it's many gigabytes, but I'm sure there won't be any data races; main thread will start 2 threads, one for running the timer and one running the function call. The main thread doesn't access the data until both the timer and function call threads have stopped completely.
September 23, 2020
On Wednesday, 23 September 2020 at 17:33:50 UTC, drathier wrote:
> On Tuesday, 22 September 2020 at 21:55:51 UTC, Imperatorn wrote:
>> [...]
>
> Blocking is perfectly fine. I'm wondering if I need things to be shared now or something? Not used to programming with threads. Adding a shared modifier recursively onto every piece of data that needs it is a ton of work though.
>
> I don't want to copy the data around since it's many gigabytes, but I'm sure there won't be any data races; main thread will start 2 threads, one for running the timer and one running the function call. The main thread doesn't access the data until both the timer and function call threads have stopped completely.

No. You should not share anything. Personally I would just send a message to request termination or use the solution provided with timeout.
September 23, 2020
On 9/23/20 1:19 PM, Imperatorn wrote:

> No. You should not share anything. Personally I would just send a
> message to request termination or use the solution provided with timeout.

std.concurrency does not allow "mutable thread-local data"; so one needs to cast to shared (assuming copying is not desired e.g. because it's expensive). I am modifying my second program[1] with these changes:

The worker's receive() call mentions an element with shared ints:

    receive(
      // .. same as before

      // New message type:
      (shared(int)[] arr) {
        ownerTid.send(arr[0]);
      }
    );

The sender casts to shared and receives a shared(int):

  auto arr = new int[100];
  worker.send(cast(shared)arr);
  writefln!"Received array result: %s"(receiveOnly!(shared(int))());

It is unfortunate that receiveOnly!int does not work because the returned int is just a copy. Well, at least we have a way of distinguishing int from shared(int). Is it useful?

Ali

[1] https://forum.dlang.org/post/rkdrql$2uht$1@digitalmars.com
September 23, 2020
On Wednesday, 23 September 2020 at 20:44:51 UTC, Ali Çehreli wrote:
> On 9/23/20 1:19 PM, Imperatorn wrote:
>
> > [...]
> send a
> > [...]
> with timeout.
>
> [...]

Sorry, I can't see the problem. Could you be more specific about what you want to achieve?
September 23, 2020
On Wednesday, 23 September 2020 at 20:54:51 UTC, Imperatorn wrote:
> On Wednesday, 23 September 2020 at 20:44:51 UTC, Ali Çehreli wrote:
>> On 9/23/20 1:19 PM, Imperatorn wrote:
>>
>> > [...]
>> send a
>> > [...]
>> with timeout.
>>
>> [...]
>
> Sorry, I can't see the problem. Could you be more specific about what you want to achieve?

Oops, I meant to reply to drathier
September 27, 2020
On Wednesday, 23 September 2020 at 20:58:00 UTC, Imperatorn wrote:
> On Wednesday, 23 September 2020 at 20:54:51 UTC, Imperatorn wrote:
>> On Wednesday, 23 September 2020 at 20:44:51 UTC, Ali Çehreli wrote:
>>> On 9/23/20 1:19 PM, Imperatorn wrote:
>>>
>>> > [...]
>>> send a
>>> > [...]
>>> with timeout.
>>>
>>> [...]
>>
>> Sorry, I can't see the problem. Could you be more specific about what you want to achieve?
>
> Oops, I meant to reply to drathier

I need to:

- call a side-effect-free fn with a huge argument that I don't want to copy; this argument is then returned mostly unmodified wrapped in a new value
- stop executing it if it runs for more than x seconds
- get the return value from it if it finishes within x seconds (99.9% of time time)
- let the main thread know what happened
- the main thread should block until the fn call returns
- the fn call should for sure stop executing before the main thread carries on
- the size and complexity of the fn makes it pretty much impossible to add timeout checks everywhere to make it exit nicely; I need to kill it to be sure I didn't miss a case