A re-announce on GC's defects

P

Philip Daniels

<Philip Daniels <Philip Daniels>> wrote:

Agree. The prompt disposal of objects that live for longer than a
function is a problem. It would be really nice to have a new
attribute:

[ReferenceCount(true)]
class MyClass { ... }

The CLR team investigated all manner of solutions fairly exhaustively.
Because you could do something like:

object o = instanceOfMyClass;
object x = y;

etc, *every* assignment that could *possibly* be a reference-counted
type would need to check whether or not reference counting was
required. That's a painful performance hit.

I can see that would be a problem.
 
P

Philip Daniels

But why should this assignment increase the reference counter ?
That's up to a smart pointer.

E.g.:

RefCountedObject obj1;
SmartPointer mySmartPointer1 = obj1; // Increase
SmartPointer mySmartPointer2 = mySmartPointer1; // Increase

RefCountedObject obj2 = obj1; // !!! Assignment



If you don't use another abstraction, like smart pointers, yes.

Smart pointers could IMHO be checked at compile time, otherwise they
would be painfully slow in C++ too. The only downside will be, that
Interlocked function must (should) be used to ensure thread safety,
which doesn't scale that well in multi threaded applications running on
dual core CPUs. But SmartPointers shouldn't be used as a general
replacement, only where they are needed. And then in most cases you will
have simpler and perhaps even faster code.

Andre

I think that what Jon is saying is that ultimately in the CLR every
reference type "is" a System.Object, hence any solution using a
smartptr ultimately begs the question. The CLR has to constantly check
whether an object is a smartptr before it can do "the right thing".

It's not just our code that would be a problem, I am sure there are
many places deep in the .Net runtime where objects are treated in a
generic fashion.
 
J

Jon Skeet [C# MVP]

Andre Kaufmann said:
But why should this assignment increase the reference counter ?
That's up to a smart pointer.

If the assignment *didn't* increase the reference counter, then the
object could end up disposing of itself too early:

MyClass instanceOfMyClass = new MyClass(); // Ref count=1
object o = instanceOfMyClass; // If we don't do anything now...
instanceOfMyClass = null; // Ref count = 0; dispose

instanceOfMyClass = (MyClass) o;
instanceOfMyClass.DoSomething(); // Bang!
E.g.:

RefCountedObject obj1;
SmartPointer mySmartPointer1 = obj1; // Increase
SmartPointer mySmartPointer2 = mySmartPointer1; // Increase

RefCountedObject obj2 = obj1; // !!! Assignment

So SmartPointer would be a completely different type entirely? I
thought the point was that you wouldn't have to explicitly do anything
different when writing code - otherwise you might just as well have a
using statement.
If you don't use another abstraction, like smart pointers, yes.

If you have to do anything different in the client code, there's no
benefit that I can see.
Smart pointers could IMHO be checked at compile time, otherwise they
would be painfully slow in C++ too. The only downside will be, that
Interlocked function must (should) be used to ensure thread safety,
which doesn't scale that well in multi threaded applications running on
dual core CPUs. But SmartPointers shouldn't be used as a general
replacement, only where they are needed. And then in most cases you will
have simpler and perhaps even faster code.

Except that as far as I can see, you haven't actually gained anything -
the client code still needs to know that it's an object which needs to
be disposed at the right time, and so you use SmartPointer
appropriately. If you've got to know that anyway, why not just use the
using statement?
 
A

Andre Kaufmann

Philip Daniels wrote:

Sorry this time my post is a bit longer - had to post code too for
illustration.
On Sat, 20 Jan 2007 18:05:44 +0100, Andre Kaufmann


I think that what Jon is saying is that ultimately in the CLR every
reference type "is" a System.Object, hence any solution using a
smartptr ultimately begs the question.

Yes know that.
The CLR has to constantly check
whether an object is a smartptr before it can do "the right thing".

Don't think so.

Arguments:

1) How does the CLR check the objects type when I
add it to a generic list ? They are all objects ?
How can it be typesafe ?

2) I can implement smart pointers in C++/CLI. How is that possible
the C++/CLI code is compiled to IL code ?

3) ! The killer argument ;-9

Delphi implements reference counted objects. InterfacedObjects

AFAIK this Delphi code
compiles directly without a modification
to .NET 1.1 ? How can this be ?
It's not just our code that would be a problem, I am sure there are
many places deep in the .Net runtime where objects are treated in a
generic fashion.

Yes, but why care about it ?

Sample code which illustrates the idea:
(There are many optimizations missing and which are essential for a good
smart pointer object !!! - and I haven't tested the code !!!!)
-----------------------------------------------------


##### 1 Embedded counter implementation
########################################

class ReferenceCounter { public long value = 0; }

class SmartPointer<T> : IDisposable where T : class, IDisposable
{
public SmartPointer(T initialPtr)
{
stored = initialPtr;
refcounter.value = 1;
}

public SmartPointer()
{
stored = null;
refcounter.value = 1;
}

public void Assign(SmartPointer<T> r)
{
Discard();
refcounter = r.refcounter;
stored = r.stored;
refcounter.value++;
}

public void Assign(T r)
{
if (stored == r) return;
Discard();
stored = r;
refcounter.value = 1;
}

public void Discard()
{
if (--refcounter.value == 0)
if (stored != null) stored.Dispose();
}

public void Dispose() { Discard(); }
ReferenceCounter refcounter = new ReferenceCounter();
T stored;
};

class SimpleObject : IDisposable
{
public void Dispose() {}
}

static void Main(string[] args)
{
SmartPointer<SimpleObject> p1 =
new SmartPointer<SimpleObject>(new SimpleObject());
SmartPointer<SimpleObject> p2 =
new SmartPointer<SimpleObject>();
p2 = p1;
}



###### 2 Interface based implementation:
########################################

// Base interface for reference counted objects
interface IRefCountedObject
{
ulong AddRef();
ulong Release();
}

// Dumb implementation, only for illustration
// ! not thread safe ..... !!
class BaseRefCounted : IRefCountedObject, IDisposable
{
public ulong AddRef () { return ++l; }
public ulong Release() { if (l == 1) Dispose(); return --l; }
public void Dispose () { }
ulong l = 0;
};


// 2 Objects - first reference counted, second not
class ObjectHasRef : BaseRefCounted { }
class ObjectNoRef { }


// Dumb implementation of a SmartPointer,
// only for illustration, not tested

class SmartPointer<T> : IDisposable where T : class, IRefCountedObject
{
// Assign a new object, this should be also possible with an
// assignment operator !!! for convenience
public void Assign(T r)
{
if (r == stored) return;
if (stored != null) stored.Release();
r.AddRef(); stored = r;
}

// Releases the object and eventually calls Dispose
// if the reference is zero
public void Discard()
{
if (stored != null) stored.Release();
stored = null;
}

public void Dispose() { Discard(); }
T stored; // Points to the reference counted object
};


static void Main(string[] args)
{
ObjectNoRef no = new ObjectNoRef ();
ObjectHasRef yes = new ObjectHasRef();
SmartPointer<IRefCountedObject> p1 = new
SmartPointer<IRefCountedObject>();
SmartPointer<ObjectHasRef> p2 = new
SmartPointer<ObjectHasRef>();

p1.Assign(yes);
p2.Assign(yes);
p1.Assign(no); // Boooom compiler error
}


Please don't sue me for any errors, it was just a quick hack.
So what's missing - some automatism implemented in the CLR, which does
the handling, so that I don't have to call an assign method, but can use
an assignment operator and would be perfect if the compiler could also
generate code which automatically disposes the reference counted object,
so optimally the SmartPointer should be a stack allocated / handled
object. Like value objects - but smart ;-)



Andre
 
J

Jon Skeet [C# MVP]

Andre Kaufmann said:
Don't think so.

Arguments:

1) How does the CLR check the objects type when I
add it to a generic list ? They are all objects ?
How can it be typesafe ?

Each object knows what its type is.
2) I can implement smart pointers in C++/CLI. How is that possible
the C++/CLI code is compiled to IL code ?

Is it possible in "pure" CLI mode, with no unmanaged code?
3) ! The killer argument ;-9

Delphi implements reference counted objects. InterfacedObjects

AFAIK this Delphi code
compiles directly without a modification
to .NET 1.1 ? How can this be ?

My guess is that the behaviour is different, or Delphi adds an extra
step in every assignment which could involve a reference counted
object.

My guess is the first, especially given
http://www.midnightbeach.com/dotNetArchitecture.2002.html
<quote>
One thing that reference counting does do better than garbage collection is
resource protection. That file will get closed, that visual cue will
get restored, at the moment when your interface variable goes out of
scope and the object is freed. With garbage collection, you can have a
finalization routine that gets called when the block is scavenged, but
you have no control over when it happens. This means that a whole class
of "failsafe" Delphi techniques that rely on interface finalization are
invalid under .Net.
</quote>

Note the last sentence.
Yes, but why care about it ?

Because you don't want things to be disposed earlier than they should
be.
Sample code which illustrates the idea:
(There are many optimizations missing and which are essential for a good
smart pointer object !!! - and I haven't tested the code !!!!)
-----------------------------------------------------

static void Main(string[] args)
{
SmartPointer<SimpleObject> p1 =
new SmartPointer<SimpleObject>(new SimpleObject());
SmartPointer<SimpleObject> p2 =
new SmartPointer<SimpleObject>();
p2 = p1;
}

So that adds an implicit call to Assign during assignment, right?
So I could make it fail very easily using:

object o = p1; // No reference count increase?

If a smart-pointer implementation is to be useful, it mustn't fail in
situations like the above, IMO.
Please don't sue me for any errors, it was just a quick hack.
So what's missing - some automatism implemented in the CLR, which does
the handling, so that I don't have to call an assign method, but can use
an assignment operator and would be perfect if the compiler could also
generate code which automatically disposes the reference counted object,
so optimally the SmartPointer should be a stack allocated / handled
object. Like value objects - but smart ;-)

But you can't make it perform *and* cope with people using
interfaces/object references instead of SmartPointer references, IMO.

If it were simple to achieve, don't you think the CLR team would have
thought of it? See http://blogs.msdn.com/brada/pages/371015.aspx for
some evidence that they've thought quite hard about this topic.
 
W

Willy Denoyette [MVP]

Andre Kaufmann said:
Philip Daniels wrote:

Sorry this time my post is a bit longer - had to post code too for illustration.


Yes know that.


Don't think so.

Arguments:

1) How does the CLR check the objects type when I
add it to a generic list ? They are all objects ?
How can it be typesafe ?

..The CLR (CLI) has a rich type system and each object knows it's exact type.
2) I can implement smart pointers in C++/CLI. How is that possible
the C++/CLI code is compiled to IL code ?


I don't know how your implementation looks like, but I'm pretty sure it is NOT compiled
using /clr:safe. That means that the compiler is free to generate native code for what's not
"translatable" to IL. More, if you are using templates or native objects, then these are not
managed types and as such do not end on the GC heap.

Willy.
 
A

Andre Kaufmann

Willy said:
Andre Kaufmann said:
Philip Daniels wrote:
[...]
I don't know how your implementation looks like, but I'm pretty sure it

For example boost::smart_ptr compiles in /clr:pure mode. But surely can
only handle native classes. But I don't see any reasons why it shouldn't
be possible to convert the code to handle managed ones. And instead
calling delete on the object call Dispose ?
is NOT compiled using /clr:safe.

The compiler will throw a warning if native code is generated, by
default it does not generate mixed code, without warning.
That means that the compiler is free to
generate native code for what's not "translatable" to IL. More, if you

Besides that it's not the case I think Reflector would tell me if
there's native code inside ;-)
are using templates or native objects, then these are not managed types
and as such do not end on the GC heap.

Hm, what's the difference between the object constructed on the native
or managed heap regarding smart pointers ? All I want is to release the
resources. Not the memory.

For managed ones I call Dispose, for native ones Delete. I don't see
any difference in handling, regarding smart pointers.

Andre
 
A

Andre Kaufmann

Jon said:
Each object knows what its type is.

Yes. But the point is the smart pointer either doesn't need to know if
it allocates the reference counter value externally, or it knows that
the object is a reference counted one if you inherited from a
IReferenceCounted interface or use attributes to add such code.

You surely can cast to System.Object and shoot yourself into the foot,
but you can do the same with all other objects or with Dispose, or by
using destructors (finalizers) everytime or.... there are many examples
where C# allows one too to write bad error prone code.
Is it possible in "pure" CLI mode, with no unmanaged code?

Yes. boost::smart_ptr compiles, but surely can only handle native
objects. But as I wrote in my other post, if you replace * with ^ and
delete with Dispose it should also compile with managed ones.
I have a basic implementation, doing this.
[...]
My guess is that the behaviour is different, or Delphi adds an extra

In Delphi every object is derived from TObject - same as in C#.
So it should be comparable - at least I think so.
step in every assignment which could involve a reference counted
object.

I guess the latter. In fact it's doing that in native code, and I don't
see any reason why it shouldn't do the same in managed one.

E.g.: when I assign

InterfacedObject1 := InterfacedObject2

In Delphi the compiler calls: (not 100% exact and meta code)

InterfacedObject1._release_old_ptr;
InterfacedObject1._call_delete_if_zero;
InterfacedObject1._inc_ref_counter_to_new_pointer;
InterfacedObject1._assing_new_pointer;

The managed version (don't know if that the case)
should be something like:

InterfacedObject1._release_old_ptr;
InterfacedObject1._call_Dispose_if_zero;
InterfacedObject1._inc_ref_counter_to_new_pointer;
InterfacedObject1._assing_new_pointer;

[...] This means that a whole class
of "failsafe" Delphi techniques that rely on interface finalization are
invalid under .Net.
</quote>

Note the last sentence.

Don't know exactly what the author means with interface finalization.
I suppose the finalizer method. But as Delphi (AFAIK) maps the
Destructor to the Dispose method there is no need for having or relying
on finalization. The smart pointer simply calls Dispose -> indirectly.
Because you don't want things to be disposed earlier than they should
be.

As I wrote above you can always shoot yourself into your own foot. But
such errors are normally easier to find as errors caused by objects
which aren't Disposed at all, relying on the GC eventually calling it.
[...]
p2 = p1;
}

So that adds an implicit call to Assign during assignment, right?

Oops and yes. Should be: p2.Assign(p1);

Sorry. I used the syntax I'm used to in C++. I would like to use this
syntax in C# too. But I suppose the CLR teams would argue that there
would be too much going on under the hood and it wouldn't be clear to
the programmer.

Anyways I could live with p2.Assign(p1);
So I could make it fail very easily using:

object o = p1; // No reference count increase?

As I wrote above, it was the wrong syntax and I only would prefer such a
syntax in C# too. In this case it perhaps could simply deal it as a weak
reference pointer. No increment necessary. Just an additional reference.
I don't claim that I have thoroughly thought it all over, or that I'm
know it better than all the CLR developers. Perhaps the solution isn't
perfect at all and has many pitfalls, but the same applies to the
Dispose pattern.
If a smart-pointer implementation is to be useful, it mustn't fail in
situations like the above, IMO.

Agreed. But it hasn't failed yet ;-). It only fails if both smart
pointer objects are Disposed (automatically or not).
But you have the same problem for normal objects. You shouldn't Dispose
all references to the same object ;-).

So what would the code do:

object o1 = p1; // No reference count increase
object o2 = o1; // - " - same here

SmartPointer<MyObject> ptr =
(SmartPointer<MyObject>)o2;
// here it would increase the reference counter

Now the big question arises - who calls and when Dispose function of the
smart pointer ? At function level if the function is left, at object
level if the object holding the smart pointers is Disposed.
[...]

But you can't make it perform *and* cope with people using
interfaces/object references instead of SmartPointer references, IMO.

The same applies to all other languages, with smart pointer classes. You
can always make failures and smart pointers aren't the holy grail. In
fact I would reduce their usage to a minimum. But sometimes IMHO it
would be better to use reference counting, that relying on a library
where I don't know if the objects are properly Disposed, if I have to
Dispose them etc.

Think of multiple threads dealing with the same file. Which thread will
dispose the file object ? The last one terminating. For this you need
some kind of reference counting, or implement some kind of thread
counter, which effectively is the same. With a smart pointer every
thread would call Dispose on it's smart pointer and the last one would
automatically Dispose the file object.
If it were simple to achieve, don't you think the CLR team would have
thought of it? See http://blogs.msdn.com/brada/pages/371015.aspx for
some evidence that they've thought quite hard about this topic.

Don't deny that they have thought about it, but I think the discussion
they've done was about the basic GC implementation and what's better a
Dispose pattern or generally using reference counting.

Not about adding some syntax elements that allow me to use reference
counting additionally, if I need too.

In fact I do it already, but it would be nice if the CLR would support
it natively by an attribute.

GC helps a lot, where in native languages smart pointers must be used.
In C# or generally managed code there are only few cases where they are
needed - for handling native resources.
So currently there's no big pressure for me to have this feature, would
only be nice to have (not only in C++ but in C# too) ;-)

Reference counting is dangerous and can too lead to memory leaks. But
IMHO the Dispose pattern introduces similar problems.

Andre
 
A

Andre Kaufmann

Jon said:
Andre Kaufmann <[email protected]> wrote:

See my other post.
appropriately. If you've got to know that anyway, why not just use the
using statement?

3 examples where using doesn't help:
------------------------------------


1. Multiple threads are dealing with the same object
holding a native resource. The last thread terminating
should call Dispose. How do I know which one is the
last one ? I count them. Effectively I do reference counting ;-)

2. The other one, for which I really like reference counting.

I have multiple lists. Each of them are holding the same object
and if the object is removed from the last list it shall be
disposed.

How can I check if the object isn't held anymore. Effectively
I would either use some kind of counting or check all the lists,
if the object has already been removed. But IMHO that's
not a good solution.

3. I have a function returning an object. The object holding the
object is Disposed, which object is responsible for Disposing ?
Which of the other object still holding my returned one ?


Andre
 
B

Barry Kelly

Andre said:
3) ! The killer argument ;-9

Delphi implements reference counted objects. InterfacedObjects

AFAIK this Delphi code
compiles directly without a modification
to .NET 1.1 ? How can this be ?

Delphi .NET interfaces aren't ref-counted. In fact, this can be
compatibility problem.

Even if they were ref-counted in Delphi .NET, Delphi can't control what
other languages do with the references. The same problem occurs in any
language that targets the CLR: you can only add these kinds of features,
features which expect all other users to play along, either to the core
CLR, or make that portion of the language incompatible with other CLR
languages.

-- Barry
 
A

Andre Kaufmann

Barry said:
Delphi .NET interfaces aren't ref-counted. In fact, this can be
compatibility problem.

Agreed. Just checked the compiled code in BDS 2006. The TInterfaced
object is implemented empty, without functions, with a note IInterface
is handled differently, I'll have to dig deeper how it's handled next
week. So far to the mystery that nearly everything compiles to .NET
seamlessly.
I just wonder why I haven't heard about that portability issue in the
newsgroups.
Even if they were ref-counted in Delphi .NET, Delphi can't control what
other languages do with the references.

Agreed. The same problem2 you are facing if you are using COM objects in
..NET.
The same problem occurs in any
language that targets the CLR: you can only add these kinds of features,
features which expect all other users to play along, either to the core
CLR, or make that portion of the language incompatible with other CLR
languages.

Yes. I would perhaps prefer going on without reference count support,
than using an incompatible CLR.

Well I perhaps can live with the manual SmartPointer:

SmartPointer<O> p =
new SmartPointer<O>(otherSmartPointer); // Increase reference

...... do something with

p.Dispose(); // Decrease reference and Dispose held object if zero

That's always possible.
And compatible with all .NET languages ;-)

Andre
 
J

Jon Skeet [C# MVP]

Andre Kaufmann said:
3 examples where using doesn't help:
------------------------------------


1. Multiple threads are dealing with the same object
holding a native resource. The last thread terminating
should call Dispose. How do I know which one is the
last one ? I count them. Effectively I do reference counting ;-)

Right. You can do that fairly easily without language/CLR support.
2. The other one, for which I really like reference counting.

I have multiple lists. Each of them are holding the same object
and if the object is removed from the last list it shall be
disposed.

How can I check if the object isn't held anymore. Effectively
I would either use some kind of counting or check all the lists,
if the object has already been removed. But IMHO that's
not a good solution.

Again, you can do it without language/CLR support.
3. I have a function returning an object. The object holding the
object is Disposed, which object is responsible for Disposing ?
Which of the other object still holding my returned one ?

Either that should be documented, or you can do it with your own
classes. Implementing your own manual reference counting via a proxy
object isn't too hard - but it's *automatic* reference counting where I
could see a benefit, and I'd want any such automatic scheme to not fail
just because I use "object" instead of the specific type.
 
A

Andre Kaufmann

Jon said:
[...]
Either that should be documented, or you can do it with your own
classes. Implementing your own manual reference counting via a proxy
object isn't too hard - but it's *automatic* reference counting where I
could see a benefit, and I'd want any such automatic scheme to not fail
just because I use "object" instead of the specific type.

Agreed, it should be fail safe as far as possible.
Otherwise I agree it's better not to implement such a feature.


Andre
 
B

Bruce Wood

Andre said:
3. I have a function returning an object. The object holding the
object is Disposed, which object is responsible for Disposing ?
Which of the other object still holding my returned one ?

This shows up an interesting problem relating more to programming idiom
than reference counting, IMHO.

When I program in C, Java, or C#, this sort of situation causes me to
think about, and carefully define, who "owns" the object in question at
any given moment. Circuits in my brain automatically kick in that keep
track of "who owns the football now?" Of course, there are cases in
which two owners need the object at the same time and that's when I
need some sort of reference counting mechanism. However, I tend to do a
little extra work to avoid those situations, just because it makes
things easier in the language I'm using.

I can see how, in C++, you wouldn't worry about that, and so you would
use the multiple-owners idiom more liberally.

I guess I'm saying that I almost always avoid having to do any sort of
reference counting in this situation by building my system so that the
object has only one owner at a time, because in the languages I work in
this is a better design.
 
A

Andre

I've thought it over.

To make a long story short I could live with a manual SmartPointer, where I
have to assign the reference counted objects by calling a method and I agree
that it was a good idea of the CLR team not to implement automatic reference
counting for objects.

The only thing I'm missing in C# (not generally in the CLR) is then some
kind of AutoDispose as in C++. Meaning some kind of "using" at object scope.

E.g.:

class Sample
{
using AnotherObject o = new AnotherObject(); // Auto create and
will be disposed
autodispose AnotherObject o = new AnotherObject(); // in the
Sample.Dispose method too
};

I know, that would have side effects too. Assigning new pointers should be
forbidden, etc.
So O.K. C++ semantics doesn't fit that well in C#.

C# offers me enough advantages over C++, otherwise I'm free to use mixed
code, where necessary C++/CLI and C#.

And if VStudio will support directly mixing languages in a single project
I'll be perfectly happy
;-) ( And if IIRC it will support this in a future version)

Andre
 
W

Willy Denoyette [MVP]

Andre Kaufmann said:
Willy said:
Andre Kaufmann said:
Philip Daniels wrote:
[...]
I don't know how your implementation looks like, but I'm pretty sure it

For example boost::smart_ptr compiles in /clr:pure mode. But surely can only handle native
classes. But I don't see any reasons why it shouldn't be possible to convert the code to
handle managed ones. And instead calling delete on the object call Dispose ?

Pure is NOT safe, pure is not verifiable please run peverify.exe on your assembly and watch
the errors. The compiler is free to inject native code whenever it sees fit, and this has
nothing to do with unmanaged code (#pragma unmanaged). Only /clr:safe is guaranteed to
produce verifiable code.
But this asside, you are mixing different object models in your assembly when compiling with
/clr or /clr:pure.
The compiler will throw a warning if native code is generated, by default it does not
generate mixed code, without warning.
No, it produces an *error* when using
1) native code blocks (unmanaged code) and
2) when using C++ classes and templates
when compiling in /clr:safe mode.

It produces an error when using native code not marked as unmanaged when compiling using
/clr:pure.
Besides that it's not the case I think Reflector would tell me if there's native code
inside ;-)

No, it won't tell you, ildasm and peverify will do.
Hm, what's the difference between the object constructed on the native or managed heap
regarding smart pointers ? All I want is to release the resources. Not the memory.
No what you are after is "deterministic" destruction PLUS deterministic finalization, and
this is the whole point of the discussion. Managed types cannot be "finalized"
deterministically because they are in the hands of the GC and the finalizer, they can only
be destructed (disposed of) deterministically , something that can't be done without the
help of the user ('using' idiom in C# and VB.NET or an explicit call to Dispose()). If the
user fails to apply the "using" idiom or fails to call Dispose, you are in the hands of the
finailzer which is non deterministic.
Unmanaged types are *destructed* deterministically when calling delete, but here the
object's memory and the resources are/can cleaned-up at exactly the same time, this can be
"automated" by using smart pointer like constructs (auto_ptr and auto_ptr_ref).
For managed ones I call Dispose, for native ones Delete. I don't see
any difference in handling, regarding smart pointers.
I guess whe are side tracked here, who is talking about unmanaged types? The discussion is
about automating destruction of managed types, not unmanaged ones.


Willy.
 
A

Andre

Pure is NOT safe, pure is not verifiable please run peverify.exe on your
assembly and watch
[...]

Quote from my post "...the C++/CLI code is compiled to IL code".
Is mode pure compiling to IL code or is it not ? This all ends now into
nitpicking.

But to make a long story short: I also have code which compiles in safe
mode.
[...] No, it won't tell you, ildasm and peverify will do.

O great, and ildasm shows me the information Lutz Roeders Reflector not ?

ildasm prompts:
// Embedded native code
// Disassembly of native methods is not supported.

Reflector:
.....System::Runtime::CompilerServices::MethodCodeType::Native....

What else must I know to classify the disassembled method being native ?
[...]

this is the whole point of the discussion. Managed types cannot be
"finalized"

Please don't tell me what my intention is - I should know it better.
What shall I do that you don't continue to interpret what my intention is ?

I'm discussing about *Disposing* not *Finalization* - resources not memory.

In C++/CLI embedded objects (not as handles) are automatically disposed if
the main object is disposed, same for objects created at function scope.
Also I have operator overloading.
That's all need to implement a smart pointer which does automatically call
*Dispose*, nothing more or less.
---------------------------------------------------------------------------------------------------------

To sum it up:

C++/CLI allows me to handle Disposing more automatically than C# and
therefore I can also implement a proxy (smartpointer), which allows me to do
reference counting automatically too.
C# has many features that C++/CLI (C++ general) lacks and it wouldn't be
good to port the same automatism from C++/CLI to C#.

I can live with that, so we should stop the discussion here.

Andre
 
W

Willy Denoyette [MVP]

Andre said:
Pure is NOT safe, pure is not verifiable please run peverify.exe on your assembly and
watch
[...]

Quote from my post "...the C++/CLI code is compiled to IL code".
Is mode pure compiling to IL code or is it not ? This all ends now into nitpicking.

No, this is not nitpicking, the C++/CLI compiler has reserved the freedom to produce X86
code when it sees fit when using /clr or /clr:pure (that's why both result in unverifiable
IL), which is not the case when compiled with /clr:safe, and even then It's even not
guaranteed that in *safe* mode the resulting assembly is verifiable (see later sample). The
X86 code is stored in a separate binary blob in the IL.


But to make a long story short: I also have code which compiles in safe mode.

Even in safe mode the C++/CLI compiler allows you to generate unsafe contructs
(unverifiable code).
[...] No, it won't tell you, ildasm and peverify will do.

O great, and ildasm shows me the information Lutz Roeders Reflector not ?

Don't trust your tools blindly, what Reflector shows you is not always what you get, only
the IL can be trusted.

To illustrate, try this:

// C++/CLI file, compile with /clr:safe
// Note that thesame is not allowed in C# or any other pure managed language

using namespace System;

ref class A
{
public:
virtual void f()
{
Console::WriteLine("A::f");
}

};
ref class B : public A
{
public:
virtual void f() override
{
Console::WriteLine("B::f");
}
};

int main()
{
B b;
b.f();
b.A::f(); // ------>call A::f through b's instance pointer
return 0;
}
// end of file

Reflectors show you this for main (C# code and MC++):

internal static int main()
{
B b1 = new B();
b1.f();
b1.f(); //???????
return 0;
}
Running this should output:
B::f
B::f
.... but the output is:
B::f
A::f

reflector's 'IL' and ildasm's output are correct!

Note that despite that /clr:safe was used to compile above code, the output is non
verifiable IL! run peverify on it and watch, C++/CLI doesn't care that much about the
verificaion rules of the CTS.
I've seen other samples, where the compiler produces non verifiable IL, just because it
tries to optimize too much.
ildasm prompts:
// Embedded native code
// Disassembly of native methods is not supported.

Reflector:
....System::Runtime::CompilerServices::MethodCodeType::Native....

What else must I know to classify the disassembled method being native ?

This is the output from a /clr assembly containing unmanaged code, not from a 'pure'
assembly...

[...]

this is the whole point of the discussion. Managed types cannot be "finalized"

Please don't tell me what my intention is - I should know it better.
What shall I do that you don't continue to interpret what my intention is ?

Show an example that illustrates your point, please no pseudo code....

you seem to
I'm discussing about *Disposing* not *Finalization* - resources not memory.

Good...

In C++/CLI embedded objects (not as handles) are automatically disposed if the main object
is disposed, same for objects created at function scope.

You mean with stack semantics, right? References types with stack semantics at function
scope (or smaller scope) are destroyed when leaving the scope, declared as class members
they are not.

Example:

public ref class Resource{
public:
Resource(){
Console::WriteLine("Resource::ctor.");
ptr = new unsigned char[1000000];
}
~Resource(){
Console::WriteLine("Resource:dtor");
if(ptr)
delete[] ptr;
}
void Foo()
{
Console::WriteLine("Resource::Foo");
}
protected:
void * ptr;
};

public ref class D
{
public:
Resource r;// r will never get destructed (Disposed), it should be declared at function
scope or delete should be called!!!
void Foo()
{
// Resource r; // here c will auto destruct when Foo leaves the scope
c.Foo();
}
};

int main()
{
D^ d = gcnew D();
d.Foo();
.. remaining part of the code
....

Also I have operator overloading.
That's all need to implement a smart pointer which does automatically call *Dispose*,
nothing more or less.

The same can be done in C#.
---------------------------------------------------------------------------------------------------------

To sum it up:

C++/CLI allows me to handle Disposing more automatically than C# and

Not true, as been shown you by other posters too, it's up to the user of the class to apply:
a) "stack allocated semantics" or,
b) 'using' (C#),
both boiling down to exactly the same IL produced by the compilers (syntactic sugar).
Failing one or the other will result in non-determinism.
therefore I can also implement a proxy (smartpointer), which allows me to do reference
counting automatically too.

The same can be done in C#.
C# has many features that C++/CLI (C++ general) lacks and it wouldn't be good to port the
same automatism from C++/CLI to C#.
??

I can live with that, so we should stop the discussion here.

As you like...
Willy.
 
A

Andre

[...]

I've stated:

The embedded objects will be automatically Disposed if you Dispose the
object holding the embedded ones.

Not that the holding object will be automatically disposed too.
int main()
{
D^ d = gcnew D();
d.Foo();
.. remaining part of the code

You didn't delete / Dispose Object d. So surely the embedded ones won't be
disposed too.

[...]
Not true, as been shown you by other posters too, it's up to the user of
the class to apply:
a) "stack allocated semantics" or,

May I disagree ? - example:

In the following example Embedded destructor (as you know in other .NET
languages Dispose) is called twice - automatically, which I can't do in C#.

ref class Embedded
{
public:
~Embedded() { Console::WriteLine("Auto Disposed"); }
};

ref class Second : public IDisposable
{
public:
Embedded s;
};

int main(array<System::String ^> ^args)
{
Second a1;
Second^ a2 = gcnew Second();
delete a2; // Calls Dispose and outputs "Auto Disposed";
return 0; // Destroys a1 (dispose) and outputs "Auto Disposed"
}
The same can be done in C#.

Unfortunately if I embed the proxies I have to Dispose them manually.
Though - no big deal.
As you like...

Have been forced to go on :-9 , ;-)

Andre
 
W

Willy Denoyette [MVP]

Andre said:

I've stated:

The embedded objects will be automatically Disposed if you Dispose the
object holding the embedded ones.

Not that the holding object will be automatically disposed too.
int main()
{
D^ d = gcnew D();
d.Foo();
.. remaining part of the code

You didn't delete / Dispose Object d. So surely the embedded ones won't be disposed too.

Andre, this is the whole point of the discussion. I said that you can make errors even using
stack based semantics.
The above sample was just to illustrate the point , the main method creates an instance of a
non disposable class D, so I'm not supposed to call delete on it as long as the class
doesn't hold embedded disposable objects even using stack semantics.
When the class holds a disposable object as a class member (Resource in the sample), you
should call Delete on it, when the disposable object is declared at function scope,
destruction is done automatically when leaving the function scope.
And this is where the discussion started, a user of class B should not need to know the
internals of D (Resource class member can be declared private in D), and as D doesn't
*explicitly* derive from IDisposable so... there is no obligation for the user of D to call
delete, he trusts the GC to remove the garbage when D gets collected.

[...]
Not true, as been shown you by other posters too, it's up to the user of the class to
apply:
a) "stack allocated semantics" or,

May I disagree ? - example:

Sure, you're welcome :).
In the following example Embedded destructor (as you know in other .NET languages Dispose)
is called twice - automatically, which I can't do in C#.

ref class Embedded
{
public:
~Embedded() { Console::WriteLine("Auto Disposed"); }
};

ref class Second : public IDisposable
{
public:
Embedded s;
};

int main(array<System::String ^> ^args)
{
Second a1;
Second^ a2 = gcnew Second();
delete a2; // Calls Dispose and outputs "Auto Disposed";
return 0; // Destroys a1 (dispose) and outputs "Auto Disposed"
}


Yep, but here you explicitly state to the user of the class that it implements *IDisposable*
(which is a good think, see above), so it's obvious for the user that he has to call delete
when he wants to destroy a gcnew'd instance of this class. But this is not common and surely
not a requirement, the C++/CLI compiler's machinery will make sure the class implements
IDisposable when defined as:

ref class Second
{
public: Embeded s; // stack allocated semantics
}

this because it holds a "stack allocated semantic" object reference. And this is IMO the
source of a lot of confusion and errors and in no way better than C# and VB.NET "using"
idiom.

Willy.
 

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