Jinoh Kang (@iamahuman) commented about dlls/windows.media.speech/recognizer.c:
return ref;
}
+static HRESULT session_join_worker_thread( struct session *session ) +{
- HANDLE thread;
- EnterCriticalSection(&session->cs);
- thread = session->worker_thread;
- LeaveCriticalSection(&session->cs);
If `session_join-worker_thread` is free from data race, no other threads will modify `worker_thread` until `session_join-worker_thread` sets it to `NULL`. The converse is also true: if any other thread could modify `worker_thread`, then `session_join_worker_thread` has a data race regardless of the critical section. Therefore, the critical section acquisition here is unnecessary.
Although I think there are no more obvious race bugs in your code, it is still hard to verify its race-free status due to unclear ownership of resources. In this case, every caller of `session_join_worker_thread` conceptually _owns_ the `worker_thread` variable, but each by different means:
1. `session_StopAsync` owns `worker_thread` by the virtue of transitioning `worker_running` from `TRUE` to `FALSE`. No other threads will mutate `worker_thread` until it is set to `NULL` again. 2. `session_Release` owns `worker_thread` since the COM contract guarantees that no user reference to the object exists when the reference count drops to zero. The only existing reference is from the worker thread, which never accesses `worker_thread`.
I think the code will be much clearer if `session_join_worker_thread` accepted `worker_thread` obtained from the caller's crtical section. This way, only the ownership of the thread handle itself, and not the entire variable, needs to be verified. You can take it further and make sure to set `worker_thread` to an invalid handle for other threads so that the thread handle isn't accessible by others at all. This will guarantee that nothing will concurrently close the handle, greatly simplifying verification.