From: Michael P. <mic...@gm...> - 2010-01-27 03:42:35
|
Orin Eman wrote: >> InterlockedCompareExchangePointer() would be one way to go for init... >> though you do have to make sure the operands are 64bit aligned. Hrm. I hadn't considered alignment; that could be tricky, but I'm sure it can be worked out with bit arithmetic, an oversized item, and aliasing, wrapped in a macro. There may even be a pragma or attribute for it. Or maybe there's a way to do something with a dummy structure and force alignment that way; I don't know. >> How would you handle the libusb_exit code as the last instance should free the mutex? That's already solved for the api_*_available variables; see the concurrency-public branch. The idea is you use InterlockedIncrement on init, and InterlockedDecrement on exit, and make it a long instead of a bool. When the variable is zero, you can free it. Or you could just leave it and not init it again in the next call to init(). You'd be leaking one mutex per program execution (not per thread), and Windows will clean that up when the program terminates. The InterlockedIncrement solution can also be used to make sure initialization only happens once, particularly in the combination shown below... >> Static initialization is problematic as you might not know what order such >> initializations are done in. I could have a static C++ object that uses >> libusb - when will its constructor be called relative to any other static initialization? Not sure. But you wouldn't use it in such a way that it matters, if you use it at all. Creating an unowned mutex is no big deal wrt initialization order, you just need to check it didn't fail in init (it's okay to check every time; it will never become non-null). But I'm not recommending creating it statically, only saying it could work. I can elaborate if you don't like the solution below, but no guarantees it'll be better. >> So the code I gave Pete punted the problem to the OS with a named >> semaphore (could have been a named mutex, but six of one...). I would not use a named primitive. The problem is that every other libusb-using process will think libusb is already initialized for it too, except that it doesn't share memory, so it's not. Even if you add the process ID, adding naming pretty much requires you delve into security descriptors to avoid people tampering (I think?). A solution that avoids this is more like, in pseudocode: (global) HANDLE global_mutex = NULL; // needs to be aligned. (ToDo) (global) LONG global_count = 0; windows_init() { [...] bool exchange_succeeded = true; HANDLE temp_mut = CreateMutex(unnamed); // could be semaphore instead if(!temp_mut) return failure; WaitForSingleObject(temp_mut); // need to own in case exchange works if(NULL != InterlockedCompareExchangePointer(&global_mutex, temp_mut, NULL)) // atomically replace iff was NULL. { exchange_succeeded = false; ReleaseMutex(temp_mut); // they beat us to it; kill temp var CloseHandle(temp_mut); WaitForSingleObject(global_mutex); // wait for other guys to init or fail if(InterlockedIncrement(&global_count) > 1) { ReleaseMutex(global_mutex); return success; // already inited } // Fall through; init failed or thread already exited and // tore stuff down. We own mutex. } [...do first-time init...if fail, release mutex, but don't destroy it...] // continue here iff first init successful if(exchange_succeeded) // don't do it twice if we fell through InterlockedIncrement(&global_count); // let other threads know it succeeded ReleaseMutex(mutex); // release threads waiting for init. return success; // now inited } windows_exit() { [...] WaitForSingleObject(global_mutex); if(InterlockedDecrement(&global_count) > 0) { ReleaseMutex(global_mutex); return; // still other threads using libusb } // teardown. don't free global_mutex ever. ReleaseMutex(global_mutex); } As an artifact of the mutex, the InterlockedIncrement/Decrement really aren't necessary, and could be replaced by prefix ++/--, respectively, but I wanted to be clear that the point of the mutex is *NOT* to protect global_count; it just happens to. I may have missed problems here, though, so please check it carefully. Like I said, a good solution is harder without the private data, but I still think we can come up with one that works. How badly do we want it? I spent a good 30 minutes writing/rewriting this, but I may have to take a second stab. It's tricky. Michael |