Why's this timer never stop?

M

Michael C

I've created a timer object and set it running without keeping a reference
to it and not stopping it. Shouldn't the garbage collector pick up that
there's no reference to the timer, call finalize and hence stop the timer?
Before anyone says 'just call dispose' I'm just testing to try to work out
what's going on with creating/destroying forms and controls etc.

Code sample is below. Thanks for any replies
Michael

using System;
using System.Windows.Forms;

namespace TestStuff
{
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button btnStart;
private System.Windows.Forms.Button btnCollect;

public Form1()
{
this.btnStart = new System.Windows.Forms.Button();
this.btnCollect = new System.Windows.Forms.Button();
this.SuspendLayout();
this.btnStart.Location = new System.Drawing.Point(176, 56);
this.btnStart.Size = new System.Drawing.Size(80, 24);
this.btnStart.Text = "Start";
this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
this.btnCollect.Location = new System.Drawing.Point(176, 88);
this.btnCollect.Size = new System.Drawing.Size(80, 24);
this.btnCollect.Text = "GC.Collect";
this.btnCollect.Click += new System.EventHandler(this.btnCollect_Click);
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Controls.Add(this.btnCollect);
this.Controls.Add(this.btnStart);
this.ResumeLayout(false);
}

private void btnCollect_Click(object sender, System.EventArgs e)
{
GC.Collect();
}

private void btnStart_Click(object sender, System.EventArgs e)
{
Timer t = new Timer();
t.Tick += new EventHandler(t_Tick);
t.Interval = 500;
t.Start();
t = null;
}

private void t_Tick(object sender, EventArgs e)
{
Console.WriteLine(Environment.TickCount.ToString());
}
}
}
 
V

Vadym Stetsyak

Hello, Michael!

When you create and start timer, Timer object is "pinned" via GCHandle.Alloc in
the internals of Timer.Enabled property.
That is why Timer object is not destroyed.

And now, about question why it is generating ticks. Timer internally uses Win32 API
function SetTimer
( http://msdn.microsoft.com/library/d...rs/timerreference/timerfunctions/settimer.asp )

After calling SetTimer - window that creaeted timer will receive WM_TIMER windows message.
These messages will be sent until you call Timer.Stop, which will call KillTimer internally.
--
Regards, Vadym Stetsyak
www: http://vadmyst.blogspot.com
 
M

Michael C

Vadym Stetsyak said:
Hello, Michael!

When you create and start timer, Timer object is "pinned" via
GCHandle.Alloc in
the internals of Timer.Enabled property.
That is why Timer object is not destroyed.

Thanks for the reply, that makes sense but is there any reason they do it
that way? Are there any other classes in the framework that use a similar
technique?
And now, about question why it is generating ticks. Timer internally uses
Win32 API
function SetTimer
(
http://msdn.microsoft.com/library/d...rs/timerreference/timerfunctions/settimer.asp )

After calling SetTimer - window that creaeted timer will receive WM_TIMER
windows message.
These messages will be sent until you call Timer.Stop, which will call
KillTimer internally.

I know the SetTimer api reasonably well and can't think of any reason they'd
need to pin the managed class when creating a wrapper.

Michael
 
V

Vadym Stetsyak

Hello, Michael!

MC> Thanks for the reply, that makes sense but is there any reason they do
MC> it that way? Are there any other classes in the framework that use a
MC> similar technique?

Yes, there are. Using Reflector tool you can observer how many classes use GCHandle.Alloc(...)

MC> I know the SetTimer api reasonably well and can't think of any reason
MC> they'd need to pin the managed class when creating a wrapper.

IMO if they wouldn't pin that class - there would be no guarantee that WM_TIMER
message handler will not cause exception.

If timer object will be "collected" by GC and KillTimer will not be called, who then will handle WM_TIMER?

--
Regards, Vadym Stetsyak
www: http://vadmyst.blogspot.com
 
M

Michael C

Vadym Stetsyak said:
Yes, there are. Using Reflector tool you can observer how many classes use
GCHandle.Alloc(...)

Cool, I never realised you could use the "Used By" feature in reflector on
function calls. I've only ever used it on objects.
IMO if they wouldn't pin that class - there would be no guarantee that
WM_TIMER
message handler will not cause exception.

If timer object will be "collected" by GC and KillTimer will not be
called, who then will handle WM_TIMER?

Wouldn't the GC cause the finalizer to be called which could call KillTimer?

Michael
 
V

Vadym Stetsyak

Hello, Michael!

MC> Wouldn't the GC cause the finalizer to be called which could call
MC> KillTimer?

Timer class has only Dispose method that can cause KillTimer call.
Its base class ( Component ) has finalizer, but it calls Dispose with false parameter.
That result in timer.Enabled=false not beeing called,
that's why when Timer object is collected KillTimer will not be called.

--
Regards, Vadym Stetsyak
www: http://vadmyst.blogspot.com
 
M

Michael C

Vadym Stetsyak said:
Hello, Michael!

MC> Wouldn't the GC cause the finalizer to be called which could call
MC> KillTimer?

Timer class has only Dispose method that can cause KillTimer call.
Its base class ( Component ) has finalizer, but it calls Dispose with
false parameter.
That result in timer.Enabled=false not beeing called,
that's why when Timer object is collected KillTimer will not be called.

Couldn't they have just called KillTimer still?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top