Thread overview
Obedient threads
Oct 02, 2014
Chris
Oct 02, 2014
thedeemon
Oct 02, 2014
Chris
Oct 02, 2014
ketmar
Oct 02, 2014
Chris
Oct 02, 2014
ketmar
Oct 02, 2014
Ali Çehreli
Oct 02, 2014
Chris
Oct 03, 2014
ketmar
Oct 03, 2014
Chris
October 02, 2014
What is the best way to kill a thread when it pleases the owner (main thread)? The background is playing audio files. The playback happens in a separate thread so the owner can keep on listening to events triggered by the user (like stop, pause). I have to queue audio files, wait until one has finished, then play the next etc. Communicating between threads does not work with this technique:

Owner:
// create thread ...
...
auto isFinished = receiveOnly!bool();

Thread:
// play file ...
if (finished) {
  ownerTid.send(true);
}

Of course, now Owner waits until it receives the message and is deaf to any user input until Thread has finished, thus there is no way to interrupt the playback. Is there a simple and elegant D way to solve this? Slots are thread local so the observer doesn't know what's going on in another thread, does it?
October 02, 2014
Just use non-blocking receives in main thread's event loop. When you get a message from child thread that it's finished playing and you decide you don't need that thread anymore, send a message to child "you're dismissed". The child should also have some loop to check for incoming messages non-blockingly. Upon receiving such message it should exit.
October 02, 2014
On Thursday, 2 October 2014 at 10:33:02 UTC, thedeemon wrote:
> Just use non-blocking receives in main thread's event loop. When you get a message from child thread that it's finished playing and you decide you don't need that thread anymore, send a message to child "you're dismissed". The child should also have some loop to check for incoming messages non-blockingly. Upon receiving such message it should exit.

Thanks. I was thinking of something like that, only I haven't found a way to set up non-blocking receives. What am I missing. I'm sure it's something trivial.
October 02, 2014
On Thu, 02 Oct 2014 11:36:06 +0000
Chris via Digitalmars-d-learn <digitalmars-d-learn@puremagic.com> wrote:

> Thanks. I was thinking of something like that, only I haven't found a way to set up non-blocking receives. What am I missing. I'm sure it's something trivial.
you can use receiveTimeout! to check if there is some message available.


October 02, 2014
On Thursday, 2 October 2014 at 13:05:00 UTC, ketmar via Digitalmars-d-learn wrote:
> On Thu, 02 Oct 2014 11:36:06 +0000
> Chris via Digitalmars-d-learn <digitalmars-d-learn@puremagic.com> wrote:
>
> you can use receiveTimeout! to check if there is some message available.

That won't do. It blocks the main thread too (for the duration of timeout), and it might abandon the thread too early. If you do it like in Ali's example[1], the main thread is blocked in the sense that it does not listen to input.

[1] http://ddili.org/ders/d.en/concurrency.html
October 02, 2014
On Thu, 02 Oct 2014 13:49:33 +0000
Chris via Digitalmars-d-learn <digitalmars-d-learn@puremagic.com> wrote:

> That won't do. It blocks the main thread too (for the duration of timeout)
don't do hour-long timeouts. 1ms timeout is actually "poll-and-receive". can't see any problems with it.


October 02, 2014
On 10/02/2014 06:49 AM, Chris wrote:
> On Thursday, 2 October 2014 at 13:05:00 UTC, ketmar via
> Digitalmars-d-learn wrote:
>> On Thu, 02 Oct 2014 11:36:06 +0000
>> Chris via Digitalmars-d-learn <digitalmars-d-learn@puremagic.com> wrote:
>>
>> you can use receiveTimeout! to check if there is some message available.
>
> That won't do. It blocks the main thread too (for the duration of
> timeout), and it might abandon the thread too early. If you do it like
> in Ali's example[1], the main thread is blocked in the sense that it
> does not listen to input.
>
> [1] http://ddili.org/ders/d.en/concurrency.html

To add to what ketmar said, even 0.msecs works.

In such a case moving the other tasks out of the main thread may be a better option. main can safely block on the message queue while another thread interacts with the outside world. It would be a cleaner event loop that way.

I've just improved that example to interact with the user. The worker produces a random message periodically. The user can ask for the most recent message by entering "yes". (I made it so that reading the message also clears it.)

import std.stdio;
import std.concurrency;
import core.thread;
import std.string;
import std.random;
import std.array;

void workerFunc(size_t count, Duration duration)
{
    writefln("There will be %s messages every %s.", count, duration);

    foreach (i; 0 .. count) {
        Thread.sleep(duration);
        ownerTid.send(format("message %s: %s", i, uniform(0, 100)));
    }

    writeln("workerFunc exiting");
}

struct ResultPlease
{}

void interactor()
{
    bool done = false;

    while (!done) {
        write("Would you like to see the result? ");
        string response = readln.chomp;

        if (response == "yes") {
            ownerTid.send(ResultPlease(), thisTid);
            const result = receiveOnly!string();

            if (result.empty) {
                writeln("Sorry, no result yet.");

            } else {
                writefln(`The result is "%s"`, result);
            }

        } else {
            writeln("Ok, no more interaction. Bye.");
            done = true;
        }
    }
}

void main()
{
    spawnLinked(&workerFunc, 4, 5.seconds);
    spawnLinked(&interactor);

    string result;
    Tid[] completed;

    while (completed.length < 2) {
        receive(
            (string message) {
                result = message;
            },

            (ResultPlease request, Tid requestor) {
                requestor.send(result);
                result = "";
            },

            (LinkTerminated e) {
                completed ~= e.tid;
            }
        );
    }
}

Ali

October 02, 2014
On Thursday, 2 October 2014 at 18:08:40 UTC, Ali Çehreli wrote:
> On 10/02/2014 06:49 AM, Chris wrote:
>> On Thursday, 2 October 2014 at 13:05:00 UTC, ketmar via
>> Digitalmars-d-learn wrote:
>>> On Thu, 02 Oct 2014 11:36:06 +0000
>>> Chris via Digitalmars-d-learn <digitalmars-d-learn@puremagic.com> wrote:
>>>
>>> you can use receiveTimeout! to check if there is some message available.
>>
>> That won't do. It blocks the main thread too (for the duration of
>> timeout), and it might abandon the thread too early. If you do it like
>> in Ali's example[1], the main thread is blocked in the sense that it
>> does not listen to input.
>>
>> [1] http://ddili.org/ders/d.en/concurrency.html
>
> To add to what ketmar said, even 0.msecs works.
>
> In such a case moving the other tasks out of the main thread may be a better option. main can safely block on the message queue while another thread interacts with the outside world. It would be a cleaner event loop that way.
>
> I've just improved that example to interact with the user. The worker produces a random message periodically. The user can ask for the most recent message by entering "yes". (I made it so that reading the message also clears it.)
>
> import std.stdio;
> import std.concurrency;
> import core.thread;
> import std.string;
> import std.random;
> import std.array;
>
> void workerFunc(size_t count, Duration duration)
> {
>     writefln("There will be %s messages every %s.", count, duration);
>
>     foreach (i; 0 .. count) {
>         Thread.sleep(duration);
>         ownerTid.send(format("message %s: %s", i, uniform(0, 100)));
>     }
>
>     writeln("workerFunc exiting");
> }
>
> struct ResultPlease
> {}
>
> void interactor()
> {
>     bool done = false;
>
>     while (!done) {
>         write("Would you like to see the result? ");
>         string response = readln.chomp;
>
>         if (response == "yes") {
>             ownerTid.send(ResultPlease(), thisTid);
>             const result = receiveOnly!string();
>
>             if (result.empty) {
>                 writeln("Sorry, no result yet.");
>
>             } else {
>                 writefln(`The result is "%s"`, result);
>             }
>
>         } else {
>             writeln("Ok, no more interaction. Bye.");
>             done = true;
>         }
>     }
> }
>
> void main()
> {
>     spawnLinked(&workerFunc, 4, 5.seconds);
>     spawnLinked(&interactor);
>
>     string result;
>     Tid[] completed;
>
>     while (completed.length < 2) {
>         receive(
>             (string message) {
>                 result = message;
>             },
>
>             (ResultPlease request, Tid requestor) {
>                 requestor.send(result);
>                 result = "";
>             },
>
>             (LinkTerminated e) {
>                 completed ~= e.tid;
>             }
>         );
>     }
> }
>
> Ali

Thanks Ali, you're a legend! I was actually thinking of creating a separate thread as an event listener instead of using main. I'll try that now. Somehow the 1.msecs solution doesn't seem clean enough.
October 03, 2014
On Thu, 02 Oct 2014 20:42:49 +0000
Chris via Digitalmars-d-learn <digitalmars-d-learn@puremagic.com> wrote:

> I'll try that now. Somehow the 1.msecs solution doesn't seem clean enough.
it seems that you want thread messaging to be integrated in your event loop. sorry, there is no easy way to do it now. maybe if libasync will become std.async we will have such feature in phobos out-of-the-box.


October 03, 2014
On Friday, 3 October 2014 at 04:39:38 UTC, ketmar via Digitalmars-d-learn wrote:
> On Thu, 02 Oct 2014 20:42:49 +0000
> Chris via Digitalmars-d-learn <digitalmars-d-learn@puremagic.com> wrote:
>
>> I'll try that now. Somehow the 1.msecs solution doesn't seem clean enough.
> it seems that you want thread messaging to be integrated in your event
> loop. sorry, there is no easy way to do it now. maybe if libasync will
> become std.async we will have such feature in phobos out-of-the-box.

Exactly. But Ali's solution above might work for my program, if I can create a listener thread. The main thread will take care of queuing the audio files and the listener thread checks, if the user has chosen to interrupt the whole thing. The other way around of what I had first in mind.