One of the guys I work with spent a good part of his day tracking down a tricky problem with some code he had written. The mistake he made is relatively common, so I thought I’d share it here.
The application he had written was managed. He made a delegate to a static managed method, and passed that delegate to unmanaged code, which used it as a callback. When this was written as a single threaded application, everything worked as expected. The problem came when he passed the callback to unmanaged code on a different thread. Once the app was modified to be multithreaded, he would get occasional and random AVs. From looking at them, it appeared that the address of the callback in the unmanaged world was garbage.
What was happening was that the garbage collector was running in the background and collecting his delegate. Initially he didn’t debug in this area because the method was static, and the AppDomain the class the method was on was not yet unloaded. However, the garbage collecter was not collecting the object the method was on (since it was static), it was actually collecting the delegate itself. The solution to the problem was to simply keep a reference to the delegate elsewhere in his code so that the garbage collecter wouldn’t touch it.
The reason the application worked when the app was single threaded was that the GC thread never got scheduled to run before the callback occurred. However, when he the callback occured in a seperate thread, the GC thread would somtimes get run before hand. And since the garbage collector likes to collect objects that were allocated recently, the delegate was usually one of the first things to go.