.NET Forum / Languages / Managed C++ / June 2004
Destructor and garbagecollector
|
|
Thread rating:  |
Peter Hemmingsen - 21 Jun 2004 09:23 GMT Hi,
I have written a dotnet class that in its constructor consumes a license from a central license pool. In the destructor it free the license again. But since I don't know when the destructor is called (that is up to the garbage collector) the object (and hence the application) consume a license although it is not in use any more.
I could of course write a specific "logoff" method to run the specific part of the destructor code that frees the license, but then I have to deal with an object that is "de-initialized" and therefore not usable.
Is there any other place to implement destructor code that are always called immediately when the object is deleted?
Peter
Steve McLellan - 21 Jun 2004 12:38 GMT Hi,
Check out the Dispose pattern (Google for "Dispose pattern MSDN" without the quotes and you should find it). There is no way to get the same behaviour as C++ stack objects, but Dispose is the recognized way of getting stuff to release resources in a timely fashion.
Steve
> Hi, > [quoted text clipped - 12 lines] > > Peter Peter Hemmingsen - 21 Jun 2004 14:24 GMT Hi Steve,
Thanks for your reply. I guess that is the way I have to do it. But then I need to add code to all the method (and properties) to check if the Dispose has been called,- and if so throw an exception. The nice thing about the destructor is that the object simply no longer exist so the methods can not be called.
Peter
> Hi, > [quoted text clipped - 25 lines] > > > > Peter Steve McLellan - 21 Jun 2004 14:42 GMT Hi,
The Dispose pattern helps you cope with that - it should be safe to call it multiple times without a problem. As long as you know when you're finished with something it should work. Incidentally, having something just suddenly not be there isn't a great way to be done with it - if you have something storing pointers to objects and the things pointed to aren't there any more, you'll get null pointer exceptions. It sounds like you might want to use something similar to a weak pointer (or weak reference, the .NET stuff calls it). I've never used them in MC++ but the gist is that weak pointers will not keep an object alive as far as the GC is concerned, but if the object is still alive and you try to use it from the weak pointer it will create a strong pointer for you to use. If you're using unmanaged C++ the Boost library has a great imlpementation; otherwise check out the .NET docs. for 'weak references'.
Additionally, you may want to consider some kind of messaging system to let the container know that something it should be storing doesn't exist any more. Maybe you can post simplified code here so I can get a better idea of what you're doing?
Steve
> Hi Steve, > [quoted text clipped - 38 lines] > > > > > > Peter Peter Hemmingsen - 21 Jun 2004 15:43 GMT Hi Steve,
Thanks a lot for your reply. My constructor connects to a server and consume a license. If this succeed I now have a "connection" object with methods to get work done on the server (e.g: search for something on the server by calling ConObj.Find(....)).
If the user of my library (that is the programmer) calls Dispose (release the license and disconnect from the server) and then later try to call the Find method it will fail (somehow) because the connection to the server is gone. It would be nice if all methods would throw the same "ObjectHasBeenDisposed" exception but that would require me to make a check in all methods. Alternatively the programmer must be prepared for a number of different exception if he keeps working on an object that has been disposed.
Again thanks.
Peter
> Hi, > [quoted text clipped - 66 lines] > > > > > > > > Peter Steve McLellan - 21 Jun 2004 16:54 GMT Hi,
It sounds as though instead of just keeping the connection as a member state you could do with having it wrapped somewhere, with an accessor function. The accessor function can then check whether the connection exists. Something like what's below (excuse any errors, I'm doing this in between links). This way, you need to put your exception call in one place and it also breaks things up a bit. Really, if the user of your lib calls Dispose() it implies they've got no use for it. Using a Disposed object is not a good thing to do. Instead, you may just want to have Connect and Disconnect functions. What's the context for all this?
__gc class ConnectionWrapper { void DisposeConnection() { conn->Dispose(); conn = 0; }
void CreateConnection() { .... }
Connection *GetConnection() { if ( conn ) { return conn; } else { throw HorribleException; // Or CreateConnection, whichever you want to do. } } private: Connection *conn; }
> Hi Steve, > [quoted text clipped - 99 lines] > > > > > > > > > > Peter Peter Hemmingsen - 22 Jun 2004 09:00 GMT Hi Steve,
We are developing a client/server based document management system (first version released back in 1988) so the server code and the client library (using RPC) is developed in C. We have know wrapped the client C library into MC++ and then clients can be written in C#.
In fact I already have a GetConnection method. It is static and provides a singleton scheme for the connection object. However I can't avoid the programmer to write something like this:
SEConnection con = SEConnection.GetConnection(); ... con.Dispose();
con.Find(...); // Horrible error
but I guess that is up to the programmer never to use a disposed object.
Peter
> Hi, > [quoted text clipped - 153 lines] > > > > > > > > > > > > Peter Steve McLellan - 22 Jun 2004 10:39 GMT Hi,
Yeah, the programmer should spot that (and more importantly it will be very obvious at first test that something's wrong). But using C++ stack objects wouldn't help this - you'd get
{ SEConnection con = SEConnection.GetConnection(); ... } con.Find();
and get a compiler error. This might be slightly better than the runtime error you'd get using Dispose but it's not ideal. Incidentally, you can still scope the calls so the con.Find() line won't compile. Dispose is used as a way of releasing resources in a timely fashion rather than waiting for the garbage collector.
Steve
> Hi Steve, > [quoted text clipped - 192 lines] > > > > > > > > > > > > > > Peter Peter Hemmingsen - 24 Jun 2004 13:39 GMT Thanks for all you comments.
One last question:
What do I gain from deriving from IDisposable? (As apposed to just making a public Dispose method)
Peter
> Hi, > [quoted text clipped - 232 lines] > > > > > > > > > > > > > > > > Peter Steve McLellan - 24 Jun 2004 14:47 GMT Hi,
Have a look at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cp conimplementingdisposemethod.asp
It's basically just a standardised way of doing things (it's a pattern), though there are probably things the GC can do if it knows a class is IDisposable. Since it is an 'official' pattern, anyone else looking at your code will know exactly what it does (assuming it does what it's supposed to
:-) ) Steve
> Thanks for all you comments. > [quoted text clipped - 263 lines] > > > > > > > > > > > > > > > > > > Peter Peter Hemmingsen - 25 Jun 2004 10:36 GMT Hi,
Have a look at this sample MC++ code:
public __gc class MyTable : public DataTable, public IDisposable { public: MyTable() {GC::ReRegisterForFinalize(this); m_p=malloc(1000);}; ~MyTable() {GC::SuppressFinalize(this);Dispose();}; virtual void Dispose() {free(m_p);};
private: void* m_p; };
First of all the GC::ReRegisterForFinalize in the constructor is required because the DataTable implementation will always have GC::SuppressFinalize meaning that the MyTable destructor by default will never be called.
The above code can't compiler because the DataTable has a derived Dispose method that are marked as sealed.
If I instead of naming my method Dispose name it CleanUp then the user of MyTable could still just call Dispose (not realizing that there is a CleanUp method) and then the MyTable unmanaged resources are not released.
Any suggestion?
Peter
Steve McLellan - 25 Jun 2004 12:00 GMT The Dispose() method in MarshalByValueComponent should call Dispose( Boolean ), passing in true. The version with the argument is virtual, so can be overriden. The docs for MarshalByValueComponent say:
"Notes to Inheritors: When you inherit from this class, you can override the Dispose, Site, and the GetService methods."
You probably don't want to separately implement IDisposable (I'm surprised it lets you do that at all) - MarshalByValueComponent implements it, so you just want to override its Dispose function.
Steve
> Hi, > [quoted text clipped - 24 lines] > > Peter Peter Hemmingsen - 25 Jun 2004 12:57 GMT Thanks a lot Steve. Your help is very much appreciated.
Peter
> The Dispose() method in MarshalByValueComponent should call Dispose( > Boolean ), passing in true. The version with the argument is virtual, so can [quoted text clipped - 38 lines] > > > > Peter Steve McLellan - 25 Jun 2004 14:21 GMT No probs, these newsgroups are a great resource, and one of the reasons we develop PC versions of our stuff before OS X releases - the Mac's a great platform but it's so hard finding any developer resources. Good luck with things!
Steve
> Thanks a lot Steve. Your help is very much appreciated. > [quoted text clipped - 48 lines] > > > > > > Peter
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|