Thread overview
[Issue 2159] New: Thread throws window exception upon termination
Jun 21, 2008
d-bugmail
Jun 22, 2008
d-bugmail
[Issue 2159] Confusion between function call and C++ style function address
Jun 22, 2008
d-bugmail
Jun 27, 2008
d-bugmail
Jun 28, 2008
d-bugmail
Jun 29, 2008
d-bugmail
Jun 30, 2008
d-bugmail
Jun 30, 2008
d-bugmail
Jun 30, 2008
d-bugmail
June 21, 2008
http://d.puremagic.com/issues/show_bug.cgi?id=2159

           Summary: Thread throws window exception upon termination
           Product: D
           Version: 2.016
          Platform: PC
        OS/Version: Windows
            Status: NEW
          Severity: normal
          Priority: P2
         Component: Phobos
        AssignedTo: bugzilla@digitalmars.com
        ReportedBy: bartosz@relisoft.com


This is a very simple test case. The thread runs to completion end then throws
an exception:
Error: Win32 Exception

import std.thread;
void main()
{
        int g;
        int run()
        {
                for(int i = 0; i < 1000; ++i)
                        ++g;
                return 0;
        }
        auto t = new Thread(run);
        t.start;
        t.wait;
}


-- 

June 22, 2008
http://d.puremagic.com/issues/show_bug.cgi?id=2159


torhu@yahoo.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
         Resolution|                            |INVALID




------- Comment #1 from torhu@yahoo.com  2008-06-22 14:37 -------
I hate to be the one to say this, but there's an important typo in your test case.

try this diff:
-auto t = new Thread(run);
+auto t = new Thread(&run);

You ended up calling the ctor that takes a size_t instead of the function pointer one.


-- 

June 22, 2008
http://d.puremagic.com/issues/show_bug.cgi?id=2159


bartosz@relisoft.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|RESOLVED                    |REOPENED
          Component|Phobos                      |DMD
         Resolution|INVALID                     |
            Summary|Thread throws window        |Confusion between function
                   |exception upon termination  |call and C++ style function
                   |                            |address




------- Comment #2 from bartosz@relisoft.com  2008-06-22 16:20 -------
Then this is an even more serious language-design problem. Run was called without me using the call syntax. This might be okay for no-argument methods, but it's a bad idea for non-member functions, as my example illustrates.


-- 

June 27, 2008
http://d.puremagic.com/issues/show_bug.cgi?id=2159


bugzilla@digitalmars.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|REOPENED                    |RESOLVED
         Resolution|                            |INVALID




------- Comment #3 from bugzilla@digitalmars.com  2008-06-27 17:12 -------
This is not a language design problem, it is intended to work this way. The
fact that run() worked as an argument is because run() returns an int and one
of the constructors for Thread takes a size_t.


-- 

June 28, 2008
http://d.puremagic.com/issues/show_bug.cgi?id=2159


bartosz@relisoft.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|RESOLVED                    |REOPENED
         Resolution|INVALID                     |




------- Comment #4 from bartosz@relisoft.com  2008-06-28 13:49 -------
For me there's no doubt that it's a design flaw in the language and I'll try to explain my reasoning.

For people coming from C/C++: A function name in C is treated as a function pointer. It's okay for D to make this syntax illegal. It's not okay to give this syntax a completely different meaning (function call), expecially if there are situations where the error is undetectable. I was lucky the the result of run() was too small for the stack size, otherwise my unit test would run without a problem.

Program understanding: How does the compiler know that "run" is a call and not a variable? Because it has the symbol table. The symbol might be defined arbitrarily far from the point of use. That's fine for the compiler but not for the programmer who's reading somebody else's code. Now the reviewer must chase multiple files in order to figure out if something is a function call or a variable. You can't really say that it shouldn't matter for the reviewer whether a given statement is a function call.

Notice that the analogous method-call syntax is not bad, because it has a different context. There is always an object involved. Without any knowledge of the symbol table, the programmer assumes that it's an access to a public data member. The fact that it expands to a method call is the matter of the object, whose designer decided to hide this implementation detail. This is very much in the spirit of object-oriented programming--implementation hiding. But there is a well-defined owner/encapsulator of implementation details--the object.

From the abstract point of view, when you allow a symbol to transparently expand into a function call, you are making a unification. The new abstraction is a "variable or a function call". What is this abstraction? Do you have a name for it? If you don't, it's just a hack that saves two keystrokes and makes the code more obfuscated. Keystroke-saving improvements should not be a design goal in itself.

Note that there is an analogous abstration for method calls. It's called a "property".

When we discuss this topic in writing, you use the syntax "run()" rather than "run". That's because you want to be clear that you mean a function call. When you write a program you also communicate with other programmers. You should be as clear about the meaning as you are in our discussion. If I were reviewing somebody else's code I would demand they use the function-call syntax.


-- 

June 29, 2008
http://d.puremagic.com/issues/show_bug.cgi?id=2159


bugzilla@digitalmars.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Severity|normal                      |enhancement




-- 

June 30, 2008
http://d.puremagic.com/issues/show_bug.cgi?id=2159





------- Comment #5 from davidl@126.com  2008-06-29 22:27 -------
yes, this is really nasty.

One solution is change the interface of Thread class. But it's not a good
solution.
Because that relies on the higher interface design quality of lib
developers(and it's not necessary in other langs).

So perhaps a qualifier "property" like C# to explicit declare if this should/could be used as a property by property syntax is better.

For this case, the real problem is that "run" in normal sense is not a "property", but it can be misused in property syntax.


-- 

June 30, 2008
http://d.puremagic.com/issues/show_bug.cgi?id=2159





------- Comment #6 from bartosz@relisoft.com  2008-06-30 12:08 -------
In the meanwhile I found an example of ambiguity. Here’s some sample code from the documentation of std.algorithm:

inPlace!(writeln)(arr1); // print the array

Why isn't writeln executed on the spot? Are we passing a function of no arguments as an alias parameter to a template, or are we evaluating the function and passing its result to the template? Note that here additional parentheses would change the meaning of code.

inPlace!(writeln())(arr1);


-- 

June 30, 2008
http://d.puremagic.com/issues/show_bug.cgi?id=2159





------- Comment #7 from shro8822@vandals.uidaho.edu  2008-06-30 12:16 -------

switch writeln to a CTFE able function and that would be a problem. In that exact cases the alias must be used because it's the only compile-time option. And then I think it's only 2.0 that allows values as aliases.


--