That was exactly my point; once an object goes out of scope and
effectively
is unreachable through any means available to developer, the only
reference
point to that object is a blocking IOCP thread.
I agree that from pure system's point of view everything works properly,
reference A goes away but reference B is still there. From development
stand
point of view, however, once i place something into IOCP pool there is no
way
for me to signal interrupt therefore, there is a logical assumption that
if i
remove all references to the object and the only one remains is IOCP
reference, it should get garbage collected. Granted, leaving objects out
of
scope without clean up is bad programming habbit as is, but isn't garbage
collection supposed to protect against those type of mistakes?
I think:
( no refernce = garbage) equally (no reference = there is no way for a
developer to access an object) and therefore (no way for a developer to
access an object = garbage)
Whether it is an adjustment to GC or adjustment to IOCP implementation
(give
me an ability to get BeginXXX operation out of wait state) is question
someone in Microsoft (in my opinion) needs to ask and answer.
Willy Denoyette said:
In my specific case there is a socket server listenting on port 1111,
you
can
use anything that accepts a connection.
Note that function openConnections() never really closes a tcp client,
but
instead lets them go out of scope.
Granted in true production environment this should never happen,
however,
garbage collection should prevent exactly that - memory/resource leak.
My original code involves working directly with sockets, to ensure it
is
not
my mistake, I have reproduced it with .NETs internal TCPClient hence no
try/catch no watches for shutdowns and buffers that are thrown away
Garbage collection timer is there to ensure garbage collection is
forced
to
run.
Code Below:
static void openConnections()
{
for ( int op_counter = 0; op_counter < 30; op_counter++ )
{
System.Net.Sockets.TcpClient pt = new
System.Net.Sockets.TcpClient();
Singularity.Communication.Client(System.Net.IPAddress.Any, 0,
System.Net.IPAddress.Loopback, 1111);
pt.Connect(new
System.Net.IPEndPoint(System.Net.IPAddress.Loopback, 1111));
System.Net.Sockets.NetworkStream ns = pt.GetStream();
ns.BeginRead(new byte[100], 1, 0, rcvd, ns);
}
}
static void rcvd(IAsyncResult ar)
{
((System.Net.Sockets.NetworkStream)
(ar.AsyncState)).EndRead(ar);
( (System.Net.Sockets.NetworkStream) ( ar.AsyncState )
).BeginRead(new byte[100], 1, 0, rcvd, (
(System.Net.Sockets.NetworkStream) (
ar.AsyncState ) ));
}
static Timer tmr = new Timer();
static void Main()
{
tmr.Interval = 1000;
tmr.Enabled = true;
tmr.Start();
tmr.Tick += new EventHandler(tmr_Tick);
//f.Show();
//f.FormClosed += new FormClosedEventHandler(f_FormClosed);
//f = null;
openConnections();
Application.Run();
}
static void tmr_Tick(object sender, EventArgs e)
{
Console.WriteLine("Collecting");
tmr.Stop();
GC.Collect();
GC.WaitForPendingFinalizers();
tmr.Start();
}
While ns goes out of scope in openConnections it is passed to BeginRead :
....
ns.BeginRead(new byte[100], 1, 0, rcvd, ns);
as argument and used in rcvd, so you keep a reference to your
NetworkStream
(ns) the wrapped TcpClient (tp) and his underlying socket.
What would you expect from the GC to free here?
Willy.