TextWriter fails on Close() call in destructor

  • Thread starter Thread starter Zytan
  • Start date Start date
Z

Zytan

I have a log class that makes a synchronized TextWriter like so, in
the constructor:

StreamWriter sw = new StreamWriter(filename);
tw = TextWriter.Synchronized(sw);

In the destructor, ~MyLogClass(), I call:

tw.WriteLine("some stuff");
tw.Close();

The Close() call throws an exception saying I can't do this on a
closed file. But, I don't close it anywhere! Is the file
automatically closed by the destructor before I get a chance to use
it? Are resources' Dispose() auto-invoked or something? What's
strange is that the WriteLine doesn't throw an exception (although its
contents never make it to the file).

Zytan
 
I have a log class that makes a synchronized TextWriter like so, in
the constructor:

StreamWriter sw = new StreamWriter(filename);
tw = TextWriter.Synchronized(sw);

In the destructor, ~MyLogClass(), I call:

tw.WriteLine("some stuff");
tw.Close();

That's not a good idea to start with. Finalizers should only deal with
unmanaged resources. Bear in mind that by the time *your* finalizer
runs, the StreamWriter's finalizer may already have run. It sounds
like you may well not want a finalizer at all (they should be very
rare - basically only when you have direct access to unmanaged
resources, eg handles).

Instead, you should implement IDisposable and tell the caller to call
Dispose.
The Close() call throws an exception saying I can't do this on a
closed file. But, I don't close it anywhere! Is the file
automatically closed by the destructor before I get a chance to use
it? Are resources' Dispose() auto-invoked or something? What's
strange is that the WriteLine doesn't throw an exception (although its
contents never make it to the file).

The underlying stream has probably had its finalizer run already.
(Note that this is separate from a Dispose method. Often, and using
MS's pattern, a finalizer will call Dispose, but that's just a matter
of implementation, not something the CLR does for you.)

Jon
 
Jon, writers do not have a Finalize method. The underlying streams
have Finalize methods to close themselves. The result is that the
writer's buffer is never flushed to the stream before the stream is
closed, resulting in lost data (it's designed this way). I assume
WriteLine executes error free because no attempt is made to flush to
the closed stream.

Zytan, avoid Finalizers for unmanaged code because stuff like this
happens. Try a syntax like:
using (StreamWriter stream = new FileInfo(@"c:\test123").CreateText())
{
stream.WriteLine("blah blah blah");
}
 
james said:
Jon, writers do not have a Finalize method.

True, but streams do. I should have been more precise in my post.
Calling Close on the writer will call Close on the stream, which may
well have been finalized, hence the error.
 
No. The error occurs because calling close in the TextWriter flushes
it's buffer to the stream, which may have been finalized. You can
close the stream multiple times with no exception. For instance

tw.Close();
tw.Close();
tw.Close();

is perfectly legit.
 
james said:
No. The error occurs because calling close in the TextWriter flushes
it's buffer to the stream, which may have been finalized. You can
close the stream multiple times with no exception. For instance

tw.Close();
tw.Close();
tw.Close();

is perfectly legit.

Ah, I see. Thanks for correcting me. I really should read more
carefully :)
 
I have a log class that makes a synchronized TextWriter like so, in
That's not a good idea to start with. Finalizers should only deal with
unmanaged resources. Bear in mind that by the time *your* finalizer
runs, the StreamWriter's finalizer may already have run. It sounds
like you may well not want a finalizer at all (they should be very
rare - basically only when you have direct access to unmanaged
resources, eg handles).

Ok, I want a logging method where Trace isn't good enough (I want
formatted logs), so I will write the log as HTML or RTF. The log
should be accessible from any method, so it needs to be 'global', so
it'll be a static class. The static constructor will open the log
file. A WriteLine method will write to it, and flush, to ensure the
data it written in the event of a crash, so I can see what last
happened (slower, yes, I know, but speed isn't an issue). And
sometime before the program ends, the file should close.
Instead, you should implement IDisposable and tell the caller to call
Dispose.

Ok. I will make it use the IDospable interface, thus implementing a
Dispose method, which will call tw.Close(). I'll see if that works.
The underlying stream has probably had its finalizer run already.
(Note that this is separate from a Dispose method. Often, and using
MS's pattern, a finalizer will call Dispose, but that's just a matter
of implementation, not something the CLR does for you.)

Yes, I am vaguely aware of th differences between finalizer (which I
call a d'tor, I hate that C# has c'tors and doesn't use the same term
d'tor), and the Dispose method. Thanks for the explanation.

Zytan
 
Jon, writers do not have a Finalize method. The underlying streams
have Finalize methods to close themselves. The result is that the
writer's buffer is never flushed to the stream before the stream is
closed, resulting in lost data (it's designed this way). I assume
WriteLine executes error free because no attempt is made to flush to
the closed stream.

I see.
Zytan, avoid Finalizers for unmanaged code because stuff like this
happens. Try a syntax like:
using (StreamWriter stream = new FileInfo(@"c:\test123").CreateText())
{
stream.WriteLine("blah blah blah");
}

Yes, I know all about the using statement, and it's nice, and yeah, I
suppose it indicates that the TextWriter must have a Dispose, since it
forces it to be called, doesn't it? The problem is, is that this is
for logging. The file must close only after I am 100% certain that no
more calls to the static class's methods will be made (to log more
data).

Zytan
 
No. The error occurs because calling close in the TextWriter flushes
it's buffer to the stream, which may have been finalized. You can
close the stream multiple times with no exception. For instance

tw.Close();
tw.Close();
tw.Close();

is perfectly legit.

So, the TextWriter has not finalized, but the StreamWriter it wrap
has?

I admit I was a little confused with the whole:
StreamWriter sw = new StreamWriter(filename);
tw = TextWriter.Synchronized(sw);

I was hoping that calling
tw.Close();
was all that I had to do, thinking, I was, in effect, calling
sw.Close();

But, apparantly, these are still two distinct objects, even though one
claims to wrap the other.

Zytan
 
Instead, you should implement IDisposable and tell the caller to call
Ok. I will make it use the IDisposable interface, thus implementing a
Dispose method, which will call tw.Close(). I'll see if that works.

(Let me fix that spelling: IDisposable)

Just as I suspected:
System.IDisposable': static classes cannot implement interfaces

Damn. Static c'tors exist, why not static d'tors? Is there anyway I
can hack one in?

Zytan
 
Ok, I want a logging method where Trace isn't good enough (I want
formatted logs), so I will write the log as HTML or RTF. The log
should be accessible from any method, so it needs to be 'global', so
it'll be a static class. The static constructor will open the log
file. A WriteLine method will write to it, and flush, to ensure the
data it written in the event of a crash, so I can see what last
happened (slower, yes, I know, but speed isn't an issue). And
sometime before the program ends, the file should close.

Wait, since I'm just writing text to it, and want to format the HTML
or RTF myself (RichTextBox is sloooooooow since it reformats on each
AppendText, and with the nature of logging, it's difficult to group up
a bunch of messages all at once to write in one go), then Trace is
fine, as I can write any text I want to it. I'll have a peak at using
System.Diagnostic.Trace again. I think this is the solution.

Back to the basics.

Zytan
 
Yes, I am vaguely aware of th differences between finalizer (which I
call a d'tor, I hate that C# has c'tors and doesn't use the same term
d'tor), and the Dispose method. Thanks for the explanation.

Actually, in C++, there exists finalizers and destructors:

class classname {
~classname() {} // destructor
!classname() {} // finalizer
};

So, the distinction is necessary.

Zytan
 
Ok, I want a logging method where Trace isn't good enough (I want
formatted logs), so I will write the log as HTML or RTF. The log
should be accessible from any method, so it needs to be 'global', so
it'll be a static class. The static constructor will open the log
file. A WriteLine method will write to it, and flush, to ensure the
data it written in the event of a crash, so I can see what last
happened (slower, yes, I know, but speed isn't an issue). And
sometime before the program ends, the file should close.

I tried making a hack of a static destructor (by making the class non-
static, and making a static variable that is an instance of the class,
thus, the destructor will be called on it when it goes bye bye), and
even then, the same error occurs.

The underlying stream must be finalized before I get a chance to stab
at it.

Since I have this hack of a destructor call, by making the class non-
static, it means I can now try IDisposable, and implement Dispose.
I'll try that now.

Zytan
 
The underlying stream must be finalized before I get a chance to stab
at it.

Since I have this hack of a destructor call, by making the class non-
static, it means I can now try IDisposable, and implement Dispose.
I'll try that now.

I have no idea how to make it call Dispose() automatically. Dispose()
is so that I can 'destruct' the object manually when I want, so this
really does nothing. The finalizer is auto called. It's just called
too late.

HOW can it be called on an object, when I still have another variable
that is alive which wraps it? I need to get rid of the wrapper, I
think, and deal with it directly.

(Since the wrapper provides thread safety, I'll have to implement that
myself.)

Zytan
 
HOW can it be called on an object, when I still have another variable
that is alive which wraps it? I need to get rid of the wrapper, I
think, and deal with it directly.

Nope, even when I do that, it is finalized when I make the call to
streamWriter.Flush(); A call to streamWrite.WriteLine("xxx"); works
fine (but doesn't get written) immediately before the call to Flush();

I can't believe C# doesn't give me access to dispose of my own objects
before it finalizes them itself. Why is my code even still running
when C# has already finalized some of my variables?

Zytan
 
Zytan said:
So, the TextWriter has not finalized, but the StreamWriter it wrap
has?

No. Neither of them has.

So, there are four objects involved:

1) Yours - with a finalizer
2) The original StreamWriter - no finalizer
3) The TextWriter - no finalizer
4) The Stream underlying the StreamWriter - with a finalizer

Your finalizer was calling Close on the TextWriter, which was calling
Close on the StreamWriter, which was trying to flush its buffer - and
that's what was wrong.
I admit I was a little confused with the whole:
StreamWriter sw = new StreamWriter(filename);
tw = TextWriter.Synchronized(sw);

I was hoping that calling
tw.Close();
was all that I had to do, thinking, I was, in effect, calling
sw.Close();

Yes, you are - the call will be propagated.
But, apparantly, these are still two distinct objects, even though one
claims to wrap the other.

They're still two distinct objects, but the TextWriter has a reference
to the StreamWriter, and it just proxies calls onto the StreamWriter,
with appropriate synchronization.
 
Zytan said:
(Let me fix that spelling: IDisposable)

Just as I suspected:
System.IDisposable': static classes cannot implement interfaces

Damn. Static c'tors exist, why not static d'tors?

Because classes are initialized, but never finalized.
Is there anyway I can hack one in?

No. I suggest you just flush your TextWriter after each call, and let
the OS close the handle automatically when the program exits.
 
Zytan said:
I can't believe C# doesn't give me access to dispose of my own objects
before it finalizes them itself. Why is my code even still running
when C# has already finalized some of my variables?

What else could it do? You could have two objects with references to
each other, but no other references to them. *One* of them has to be
finalized before the other, doesn't it?
 
Zytan said:
Nope, even when I do that, it is finalized when I make the call to
streamWriter.Flush(); A call to streamWrite.WriteLine("xxx"); works
fine (but doesn't get written) immediately before the call to Flush();

I can't believe C# doesn't give me access to dispose of my own objects
before it finalizes them itself.

The reason is that there is no finalizing dependency that tells the
memory management that one object has to be finalized before the other.
Why is my code even still running
when C# has already finalized some of my variables?

Because it's calling the finalizers after your main code has finished.
You can't predict when the objects will be finalized, so theoretically
the finalizer may be executed a long time after the main code has finished.


Why not make an object that works in two states? While the Page class
exists, the file is open, and in the Dispose method of the Page, call
Dispose on the object that will close the log file and change the state
of the object. Any writes to the object after that will be done by
opening the file, write the text and the close the file.
 
So, the TextWriter has not finalized, but the StreamWriter it wrap
No. Neither of them has.

So, there are four objects involved:

1) Yours - with a finalizer
2) The original StreamWriter - no finalizer
3) The TextWriter - no finalizer
4) The Stream underlying the StreamWriter - with a finalizer

Your finalizer was calling Close on the TextWriter, which was calling
Close on the StreamWriter, which was trying to flush its buffer - and
that's what was wrong.

Ok, right, thanks for the clarification. It's hard to understand
everything when I am dealing with a wrapper of a wrapper of an object
I am not well versed in.

Please note that StreamWriter does have a finalizer:
http://msdn2.microsoft.com/en-us/library/aa328973(VS.71).aspx
StreamWriter.Finalize Method

And this page implies that TextWriter also has a finalizer:
http://msdn2.microsoft.com/en-us/library/ms227564.aspx
TextWriter.Dispose Method ()
"Always call Dispose before you release your last reference to the
TextWriter. Otherwise, the resources it is using will not be freed
until the garbage collector calls the TextWriter object's Finalize
method."

One question: Is it ok for me NOT to call .Close() or .Dispose() on
the StreamWriter or TextWriter, and let the GC do its work when the
application closes? Presumably, this will properly close the file, as
the GC does call the finalizer on both of them, which does the clean
up work?
They're still two distinct objects, but the TextWriter has a reference
to the StreamWriter, and it just proxies calls onto the StreamWriter,
with appropriate synchronization.

Right.

Thanks again, Jon.

Zytan
 
Back
Top