views:

244

answers:

1

Hi. I'm writing a managed C++/CLI library wrapper for the MySQL embedded server. The mysql C library requires me to call mysql_thread_init() for every thread that will be using it, and mysql_thread_end() for each thread that exits after using it.

Debugging any given VB.Net project I can see at least seven threads; I suppose my library will see only one thread if VB doesn't explicitly create worker threads itself (any confirmations on that?). However, I need clients to my library to be able to create worker threads if they need to, so my library must be thread-aware to some degree.

The first option I could think of is to expose some "EnterThread()" and "LeaveThread()" methods in my class, so the client code will explicitly call them at the beginning and before exiting their DoWork() method. This should work if (1) .Net doesn't "magically" create threads the user isn't aware of and (2) the user is careful enough to have the methods called in a try/finally structure of some sort.

However, I don't like it very much to have the user handle things manually like that, and I wonder if I could give her a hand on that matter. In a pure Win32 C/C++ DLL I do have the DllMain DLL_THREAD_ATTACH and DLL_THREAD_DETACH pseudo-events, and I could use them for calling mysql_thread_init() and mysql_thread_end() as needed, but there seem to be no such thing in C++/CLI managed code. At the expense of some performance (not much, I think) I can use TLS for detecting the "usage from a new thread" case, but I can imagine no mechanism for the thread exiting case.

So, my questions are: (1) could .net create application threads without the user being aware of them? and (2) is there any mechanism I could use similar to DLL_THREAD_ATTACH / DLL_THREAD_DETACH from managed C++/CLI?

Thanks in advance.

A: 

The fundamental problem seems to be that your library wrapper doesn't get enough visibility into the threading information that it needs to correctly communicate with the underlying MySQL library. When possible in this situation, I try to fix the root problem directly.

In this case, that means giving your wrapper full knowledge and control over what thread(s) touch MySQL, when those threads start, and when they stop.

So for example the top-level object in your wrapper library could start and own a threadpool internally, with each thread in the pool calling mysql_thread_init() on startup and each thread calling mysql_thread_end() on shutdown. This threadpool would be invisible to the VB.net client code, but behind the scenes every call to your wrapper library would hand off to one of the threadpool threads for the DB communication, which would then hand the database response back to the calling thread.

When the client destroys the wrapper object, the wrapper object shuts down and joins all the threads in its internal threadpool (which each call mysql_thread_end() before exiting). And then everything is accounted for.

This may not be possible depending on the specifics of your situation (you'll incur context switch costs for every call into your wrapper; watch out!), but I thought it was worth suggesting.

If something like this won't work for you, then you either need to invoke some sort of magic system voodoo along the lines you mentioned (I'm not a CLI guy or Windows guy, so I can't help you there), or you need to pass the responsibility along to the client. One simple way of doing the latter is just to say "the wrapper object can only be used in the thread in which it was created." You're still opening yourself up to a new class of possible bugs there, of course, when clients inevitably fail to follow this constraint...

Will Robinson
Hi, thanks, Will. However, the whole idea was to avoid switching costs, as it is not likely that there will be several threads simultaneously accessing MySQL. Appartment threading has the performance hit of the context switch, and that was what I wanted to avoid. Anyway, I migrated to SQLite now, so this is all academic. :)
Guillermo Prandi