June 17, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=10344



--- Comment #10 from Steven Schveighoffer <schveiguy@yahoo.com> 2013-06-17 08:28:30 PDT ---
(In reply to comment #9)
> 
> But this is an argument against our current approach to stdout (we check and
> throw on all errors).

Yes, but there is a difference -- the error occurs DURING program execution.

In fact, one could diligently check for exceptions (and throw them away because output isn't critical) on every writeln, but still have this unexpected error.

> The error in the program you mentioned may very well
> intervene in writeln under one or more of the following conditions:
> 
> * sufficient characters output

My post specifically said this is not the case.  a simple output message like "X rows updated" will never exceed 4096 characters.

> * line buffered or unbuffered stdout

The conditions that cause this error preclude having stdout decide to flush on newlines (a console is neither an invalid descriptor, nor in danger of running out of space).

> In such cases, succeeding to produce stdout is just as unimportant to the program, but the program will fail with nonzero error code. Are you saying we should change that?

No, the program has not decided what it will return at that point.  It's a standard exception, and if unhandled, that is a bug, or expected case in the program (that should be documented).  I agree it's unlikely for most developers to handle failed stdout, but it's reasonable that a diligent and careful programmer will wrap his entire program in a try-catch block, and expect all exceptions to be handled by that block.

> At the highest level, it is well understood there examples can be given that show the stdout output was of small consequence to the program. The point is we cannot decide on behalf of the program that it's okay to have truncated output.

I don't think we can decide either way.  At the time this flush occurs, the program has made a concrete decision of success or failure.  Overriding that decision is not something the runtime should be doing.

Consider the case where the filesystem driver fails to finish writing the file after the program exits.  How do we handle that?

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
June 17, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=10344



--- Comment #11 from Andrei Alexandrescu <andrei@erdani.com> 2013-06-17 12:28:21 PDT ---
(In reply to comment #10)
> I don't think we can decide either way.  At the time this flush occurs, the program has made a concrete decision of success or failure.  Overriding that decision is not something the runtime should be doing.

The program has made a decision that assumes writes to stdout, if any, have succeeded.

> Consider the case where the filesystem driver fails to finish writing the file after the program exits.  How do we handle that?

That is not under our control.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
June 17, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=10344



--- Comment #12 from Steven Schveighoffer <schveiguy@yahoo.com> 2013-06-17 13:21:02 PDT ---
(In reply to comment #11)
> The program has made a decision that assumes writes to stdout, if any, have succeeded.

You seem to be ignoring the use case.  The return code is based on database update success, not stdout success.  In fact, I can write the code in such a way that specifically ignores stdout failures, yet still have this come up.

> > Consider the case where the filesystem driver fails to finish writing the file after the program exits.  How do we handle that?
> 
> That is not under our control.

None of this is under our control.  It's outside our visibility.  But the effect is the same -- data is not written.  We can't say definitively that a D program guarantees the data goes to it's final destination.  The error is in fact useless because success doesn't guarantee the write occurs.

In fact, all a success guarantees is that the program succeeded according to it's charter.

I have another compromise that I will post to this bug report separately.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
June 17, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=10344



--- Comment #13 from Steven Schveighoffer <schveiguy@yahoo.com> 2013-06-17 13:25:14 PDT ---
Proposed compromise:

In addition to the proposed change that throws on the final flush and adjusts the error code, provide a writable flag in File that indicates an i/o error should not throw.  Then if stdout output errors are deemed not critical, the final flush will not throw or adjust the error code.  In addition, any failure during execution when fflush is called will not throw.

This might actually be a better solution to the use case I have provided, because the intent is clearly stated from the developer up front, that stdout's failures are not critical to the program.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
June 17, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=10344



--- Comment #14 from Andrei Alexandrescu <andrei@erdani.com> 2013-06-17 13:31:35 PDT ---
(In reply to comment #12)
> (In reply to comment #11)
> > The program has made a decision that assumes writes to stdout, if any, have succeeded.
> 
> You seem to be ignoring the use case.

Apparently you are ignoring my reply to it, which addressed it directly acknowledging that the success of producing stdout output does not define the success of the program.

>  The return code is based on database
> update success, not stdout success.

I understood that the first time.

> In fact, I can write the code in such a
> way that specifically ignores stdout failures, yet still have this come up.

No, if you flush (and catch) at the end of the program you're in good shape.
Consider:

import std.stdio;
int main()
{
    writeln("test");
    fflush(null);
    return fflush(null);
}

This will never fail. The first flush fails, but the second does not.

> > > Consider the case where the filesystem driver fails to finish writing the file after the program exits.  How do we handle that?
> > 
> > That is not under our control.
> 
> None of this is under our control.  It's outside our visibility.  But the effect is the same -- data is not written.  We can't say definitively that a D program guarantees the data goes to it's final destination.  The error is in fact useless because success doesn't guarantee the write occurs.

Untrue. The success guarantees that the program succeeded in passing data forward to the operating system. Please let's not make this an argument about the tree in the forest.

D checks all flushes. Consequently it should check the last flush. No two ways about it. End of story.

> In fact, all a success guarantees is that the program succeeded according to it's charter.

That is correct. The problem with ignoring the last flush is that we assume that the charter of the program is it's okay to produce truncated output.

> I have another compromise that I will post to this bug report separately.

Looking forward to it.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
June 17, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=10344



--- Comment #15 from Andrei Alexandrescu <andrei@erdani.com> 2013-06-17 13:34:56 PDT ---
(In reply to comment #13)
> Proposed compromise:
> 
> In addition to the proposed change that throws on the final flush and adjusts the error code, provide a writable flag in File that indicates an i/o error should not throw.  Then if stdout output errors are deemed not critical, the final flush will not throw or adjust the error code.  In addition, any failure during execution when fflush is called will not throw.
> 
> This might actually be a better solution to the use case I have provided, because the intent is clearly stated from the developer up front, that stdout's failures are not critical to the program.

Sounds good, but this is the approach taken by C++'s iostreams: http://msdn.microsoft.com/en-us/library/xybta3wf(v=vs.80).aspx. It hasn't enjoyed a lot of success.

Then we'll need to define ways for people to access error codes etc. which means writeln and friends would need to return them, or provide lastError() APIs etc. It would necessitate some design.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
June 17, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=10344



--- Comment #16 from Steven Schveighoffer <schveiguy@yahoo.com> 2013-06-17 13:58:51 PDT ---
(In reply to comment #15)
> Then we'll need to define ways for people to access error codes etc. which means writeln and friends would need to return them, or provide lastError() APIs etc. It would necessitate some design.

We don't need to design any APIs.  Just don't throw.  If you want to handle errors as they happen, don't set the flag.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
June 18, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=10344


Lionello Lunesu <lio+bugzilla@lunesu.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |lio+bugzilla@lunesu.com


--- Comment #17 from Lionello Lunesu <lio+bugzilla@lunesu.com> 2013-06-17 23:32:08 PDT ---
Adding

    if (fflush(null) != 0)
            throw new Error("write error");

in druntime/src/rt/dmain2.d line 621, between rt_moduleDtor() and gc_term(),
seems to work:

$ ./helloworld 1</dev/null
object.Error: write error
----------------
5   hw                                  0x0000000108047755 extern (C) int
rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).void
tryExec(scope void delegate()) + 45
6   hw                                  0x0000000108047709 _d_run_main + 457
7   hw                                  0x0000000108047538 main + 20
8   libdyld.dylib                       0x00007fff8e6107e1 start + 0
9   ???                                 0x0000000000000001 0x0 + 1
----------------

But the question is: what to throw?

Unfortunately ErrnoException is in phobos, not in druntime. We could move it to druntime (adding an alias for backcompat.) Moving it would not introduce any additional dependencies to druntime.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
June 18, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=10344


Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |bugzilla@digitalmars.com


--- Comment #18 from Walter Bright <bugzilla@digitalmars.com> 2013-06-18 01:51:27 PDT ---
(In reply to comment #17)
> But the question is: what to throw?

I think throwing an Error would be fine, because the user isn't going to be catching it anyway.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
June 18, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=10344



--- Comment #19 from Lionello Lunesu <lio+bugzilla@lunesu.com> 2013-06-18 02:32:54 PDT ---
(In reply to comment #18)
> (In reply to comment #17)
> > But the question is: what to throw?
> 
> I think throwing an Error would be fine, because the user isn't going to be catching it anyway.

It would be nice to show at least the actual error, based on errno, instead of an hardcoded string.

druntime does have the declaration for the C runtime strerror() and .errno so
we can do:

        if (fflush(null) != 0)
        {
            auto s = strerror(.errno);
            throw new Error(s[0..strlen(s)].idup);
        }

resulting in:

$ ./hw 1</dev/null
object.Error: Bad file descriptor
----------------
5   hw                                  0x0000000109e4b765 extern (C) int
rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).void
tryExec(scope void delegate()) + 45
6   hw                                  0x0000000109e4b719 _d_run_main + 457
7   hw                                  0x0000000109e4b548 main + 20
8   libdyld.dylib                       0x00007fff8e6107e1 start + 0
9   ???                                 0x

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------