Search code examples
solace

How to cancel timers registered on a Solace context in a thread-safe way?


Situation

I am using the Solace C API.

I want to associate a state object (in my case a C++ object, but to the C API it's just void* user data) when a timer fires and Solace calls my timer callback routine. I register my timer with the Solace API call solClient_context_startTimer.

Suppose I also want to cancel the timer from another thread, knowing its timer ID which has been returned by solClient_context_startTimer. My problem is that I also want to de-allocate the state object. So the user-data pointer becomes dangling if the timer could ever fire from the context thread.

Could a race exist between the call to solClient_context_stopTimer and the context thread entering the callback function to the now-dead timer?

Possible solutions which might or might not work

If so, could anyone suggest ways to avoid trying to access the dangling pointer? I can think of starting points, but they are all have their own gotchas:

  1. Instead of passing a raw pointer as the user data, pass an ID of some sort, basically a weak reference, that I can look up in a hashtable. Unfortunately that means the hashtable has to be a global variable (which I would prefer to avoid), because there is only one user-data pointer and I cannot pass the address of the hashtable object into the callback function.
  2. Use message-passing to cancel the timer instead to avoid cross-thread object ownership issues. That means I have to override the code to run the context thread, to add queue processing, and that's seems to be way too complex for my needs.
  3. Use some kind of garbage-collection queue for the object states so that user-data pointers never become dangling, without explicit acknowledgement from the context thread. Again, seems to involve more complexity, in that this essentially involves specializing memory allocation for these timer objects/states.
  4. Have both threads mutually exclude each other when working with timers. Still, do avoid receive a dangling pointer in my callback routine in the first place, locks would have to be taken before entering the timer callback, and that means modifying the context thread's event loop, like (2).

In addition, I see the timer ID is not passed to the timer routine so it is impossible for it to check on its own whether it is executing in that racy condition.

If solClient_context_stopTimer blocks the current thread until any in-progress callback routines finish, then it would be easy to delete the state object only after making that call. But it is not clear from the API documentation whether solClient_context_stopTimer has such behavior.


Solution

  • Yes, C does not have smart pointers. The Solace C API takes care of this case by waiting for the context thread to complete the timer callbacks, and would return SOLCLIENT_FAIL the wait retries are too long.

    In short, the race condition is taken care of and you should not need to have complex workarounds.