Just wondering if there's any mechanism I can hook into that always triggers when a ref object is destroyed - even on program exit.
I'm just finishing off an odbc lib and thought it would be nice if the connection objects could automatically disconnect if necessary and free their handles when the GC disposes of them. I'd like to avoid the user having to explicitly call a 'freeConnection'/'disconnect' proc if possible.
So, I assumed finalizers would do this but they don't always trigger on program exit, so the handles would get 'leaked', so to speak. I understand that it's fine for memory to be cleaned up by the OS on exit, so I understand why finalizers don't always trigger, but that does mean I need some other method to do this.
Is there anything else I can use? Or, can I force a finalizer to always run for a type? Perhaps there's some way of injecting a 'clean up' proc at the end of the program that isn't intrusive for the user or affect simple code flow (I guess like a top level defer which as I understand it is not available)?
I'm told destructors don't work on refs yet (and that they're still in development)? So I guess that's not an option unless I rework the lib to drop ref semantics (which I'd like to avoid if possible 'cos I do like ref semantics for this).
Ideally I'd like this to work:
var
con = newODBCConnection()
qry = newQuery(con)
con.host = "blah"
con.database = "db"
qry.statement = "SELECT 1"
echo qry.executeFetch
# Now the program is exiting, I'd like to allow people to just leave it at that,
# but qry and con have handles & associated memory allocated in odbc.
# So, a user must now do this:
con.disconnect # ah the pain, how can a user live with having to do this?! :)
Thanks for the tip about addQuitProc Araq. I knew there'd be something I could use - although I think the more relevant point is that this example (and my test code) is in a global variable, so I guess that's why the finalizer doesn't get a chance to trigger.
Having said that, I'm not worried about memory - it's the odbc handles that concern me. I don't think the OS will keep track of these as odbc is linked to different drivers depending on use, and they will maintain a chunk of memory associated with that handle. For connections and queries, as I understand it this will include prepared statements and even cached results in some cases.
Consider if a user is repeatedly running a program that makes a connection to the DB, runs a large query, then returns some result - but doesn't call 'disconnect'. Now the odbc driver is building up these handles and maintaining their memory. Not good :-/
Skyfex: GC_FullCollect doesn't work (likely for the reasons Araq mentions about globals), and anyway, this would just replace calling con.disconnect with GC_FullCollect. Ideally I'd like the user to not have to worry about it at all and for the library to clean up after itself. This would allow very easy scripting type code without any mishaps.
Having said that, I don't want to hook into anything that's too drastic and might affect the use of other libraries. addQuitProc has a limit of 30 calls, which is plenty, but as it has a limit I'll have to think carefully if it is a good idea to do this. Perhaps I could enforce it only as an option.
Well caches are just that -- caches. The memory for them will be re-used sooner or later.
Consider if a user is repeatedly running a program that makes a connection to the DB, runs a large query, then returns some result - but doesn't call 'disconnect'. Now the odbc driver is building up these handles and maintaining their memory. Not good
So you think the ODBC driver isn't protected against crashing clients? (You can always crash, you know ...) I have a hard time believing that.
I couldn't find any concrete info on when, where and how odbc handles are released automatically, as you say in the event of a client crash or a network disconnect - probably because it's driver dependent. I don't want to assume too much about what's left over when there is an option for the lib to clean up automatically.
In the end I used addQuitProc to release them if they're still active and it seems to work pretty well. So, I think this'll be my solution for now!