Thread overview
Can/should spawn work with functions that return?
Jan 21, 2011
Andrej Mitrovic
Jan 21, 2011
Jonathan M Davis
Jan 21, 2011
Andrej Mitrovic
Jan 22, 2011
Jonathan M Davis
January 21, 2011
import std.stdio;
import std.concurrency;

void foo(int var)
{
}

bool bar(int var)
{
    return true;
}

void barWrapper(int var)
{
    bar(var);
}

void main()
{
    spawn(&foo, 1);
    spawn(&barWrapper, 1);
    spawn(&bar, 1);
}

Errors:
testSpawn.d(24): Error: template std.concurrency.spawn(T...) does not match any function template declaration
testSpawn.d(24): Error: template std.concurrency.spawn(T...) cannot deduce template function from argument types !()(bool function(int var),int)

Of course, when my foreground thread spawns a background thread it doesn't wait for it to finish, so assigning a return value doesn't make much sense. I can see how that can be an error (in any case that error message above is not very informative).

But what if I want to spawn a thread with an existing function 'bar' that has side-effects, but I'm not interested in its return value even though it has one?

Right now I'm forced to either:
a) remove any returns from 'bar' and change it to a void function, which can be really complicated if other functions already depend on its return value, or
b) write a new void function that can be called with spawn(), which internally calls 'bar' but discards it's value (so basically it's a wrapper). This is what I've done in the example code.

So, is spawning threads on functions that return banned by design? I couldn't read about this anywhere on the D site or TDPL.
January 21, 2011
On Friday, January 21, 2011 14:12:18 Andrej Mitrovic wrote:
> import std.stdio;
> import std.concurrency;
> 
> void foo(int var)
> {
> }
> 
> bool bar(int var)
> {
>     return true;
> }
> 
> void barWrapper(int var)
> {
>     bar(var);
> }
> 
> void main()
> {
>     spawn(&foo, 1);
>     spawn(&barWrapper, 1);
>     spawn(&bar, 1);
> }
> 
> Errors:
> testSpawn.d(24): Error: template std.concurrency.spawn(T...) does not match
> any function template declaration testSpawn.d(24): Error: template
> std.concurrency.spawn(T...) cannot deduce template function from argument
> types !()(bool function(int var),int)
> 
> Of course, when my foreground thread spawns a background thread it doesn't wait for it to finish, so assigning a return value doesn't make much sense. I can see how that can be an error (in any case that error message above is not very informative).
> 
> But what if I want to spawn a thread with an existing function 'bar' that has side-effects, but I'm not interested in its return value even though it has one?
> 
> Right now I'm forced to either:
> a) remove any returns from 'bar' and change it to a void function, which
> can be really complicated if other functions already depend on its return
> value, or b) write a new void function that can be called with spawn(),
> which internally calls 'bar' but discards it's value (so basically it's a
> wrapper). This is what I've done in the example code.
> 
> So, is spawning threads on functions that return banned by design? I couldn't read about this anywhere on the D site or TDPL.

The current design does not give you any way to access a return value from a spawned function, even if it allowed you to use such a function. And given that fact, it really doesn't make sense conceptually to have spawn work with non-void functions. I'm not sure that there are any technical barriers to it however. Still, as it stands, if a function isn't _designed_ to be called with spawn, I don't see what good it generally does you. If it's not sending messages to the parent thread, then what's the point of calling it? You can't _have_ side effects unless the function alters a shared global.

I do have an enhancement request related to getting return values from spawned functions though:

http://d.puremagic.com/issues/show_bug.cgi?id=4566

- Jonathan M Davis
January 21, 2011
Sorry, I should be careful with the word side-effects. What I meant was the newly spawned thread does its own job that the main thread doesn't care much about. It doesn't touch main's state or any shared variables. It does some work other than return a value. But the function I wanted the new thread to execute happened to have a return value, so I couldn't spawn the thread.

Really, the issue is that sometimes I have to convert C code to D and concurrency is dealt with differently in the two languages, not to mention the fact that I have next to no experience with using threads in any language (but I'm forced to use them in some cases).

So, sometimes I'll ask silly questions because I'm very new to this, but hopefully I'll learn a thing or two. I do enjoy your long and informative responses though, Jonathan. :)
January 22, 2011
On Friday, January 21, 2011 15:23:37 Andrej Mitrovic wrote:
> Sorry, I should be careful with the word side-effects. What I meant was the newly spawned thread does its own job that the main thread doesn't care much about. It doesn't touch main's state or any shared variables. It does some work other than return a value. But the function I wanted the new thread to execute happened to have a return value, so I couldn't spawn the thread.
> 
> Really, the issue is that sometimes I have to convert C code to D and concurrency is dealt with differently in the two languages, not to mention the fact that I have next to no experience with using threads in any language (but I'm forced to use them in some cases).
> 
> So, sometimes I'll ask silly questions because I'm very new to this, but hopefully I'll learn a thing or two. I do enjoy your long and informative responses though, Jonathan. :)

Well, I'd suggest just wrapping the function call in a lambda, unless spawn doesn't like that for some reason. I suppose that spawn could be made take functions with return values, but it's not generally something that you'd do, and it _is_ pretty easy to wrap such functions, so it seems to me like it's probably better as it is - though I can see why it would be annoying if you kept needing to spawn functions with return values. Still, that makes me wonder about what you're doing. I guess that you could be doing I/O or somethnig, but you certainly can't be talking back to the parent thread without having written things that way (in which case, you'd have made the spawned function void). You could always put in an enhancement request, though it does seem wrong to me (conceptually-speaking, if nothing else) to have a spawned function with a return value.

- Jonathan M Davis