Are SetProp and RemoveProp thread-safe?

A customer wanted to know whether the Set­Prop and Remove­Prop functions are thread-safe with respect to a specific window and property. "It seems to work under the debugger, but that doesn't prove that it always will work."

It depends on what you mean by "thread-safe". If you have two threads which call Set­Prop or Remove­Prop without synchronization between them, then each individual call will be atomic. For example, two non-synchronized calls to Set­Prop will result in the final property being one value or the other, not a mix of the two values.

On the other hand, you cannot predict what order the operations were ultimately performed. All that is guaranteed is that the result will be consistent with some ordering of the operations.

For example, suppose you have a window and a property whose initial value is 1. One thread calls Set­Prop(hwnd, 2) and the other calls Remove­Prop(hwnd). There are two possible outcomes:

Outcome 1 Outcome 2
Property value is 1 Property value is 1
SetProp(hwnd, 2); Changes property to 2 RemoveProp(hwnd); Property is removed
Returns 1 (removed value)
RemoveProp(hwnd); Property is removed
Returns 2 (removed value)
SetProp(hwnd, 2); Changes property to 2
Property is removed Property value is 2

The customer seemed satisfied with this answer.

Note that only the individual call to Set­Prop or Remove­Prop is atomic. If you make multiple calls in succession, you cannot guarantee that another thread won't sneak in between your calls and mess with the property.

Next time, a follow-up question that was never asked.

Comments (5)
  1. IanBoyd says:

    I assume he meant in the sense that COM objects are thread hostile. If you construct a COM object in one thread (e.g. a STA), you are forbidden from accessing it from another thread (e.g. another STA). Even though i may guarantee no overlapping operations (e.g. that only one thread at a time will ever attempt to access the COM object), that is strictly forbidden by the rules of COM.

    A STA COM object, constructed in a STA apartment, is only ever allowed to be used from that apartment. No amount of synchronization or coordination will change that rule. It’s not a thread safety issue, it’s just the way it is.

    Hopefully he had two threads, both CoInitialized into STA, and both constructing their own instances of the various shell COM objects. Hopefully he’s not sharing IShellItem interface pointer between two threads.

    1. Darran Rowe says:

      Just to be a bit nit-picky here, but you mean that STA COM objects can’t be directly called from another thread without marshalling right?

      1. IanBoyd says:

        CoMarshallInterface creates the proxy that brings any interaction with the COM object back to the thread that created it. So it’s still only being accessed from the one thread – ever.

        1. Darran Rowe says:

          But the problem is that access is too general a word to be used for this since access has the general definition of “the opportunity or right to use something”.
          So calling a member function on an STA COM object via the proxy is using that object, hence you have access. But using an object through the proxy is indirect access where using the object directly is obviously direct access.
          While I understand what you mean, the term access is too weak to mean what you mean here, which is why I did try to drop the hint with the use of direct in my last reply.

  2. Glassware says:

    I believe the real question being asked is “Will there be an unexpected crash due to two threads conflicting in ways I don’t understand?”

    Charitably, in the standard Petzold Windows co-operative multitasking model, everything can only happen once and nobody can set up crazy conflicts. So people get used to the fact that functions are always safe.

    But, when you go to the new world model of threads and processes always running, it’s easy to forget that some APIs are simple and some APIs are complex. If you use a COM component that has some poorly implemented shared heap, two threads trying to allocate memory at the same time will definitely cause unexpected crashes, even if they’re only doing something as simple as SetProp/RemoveProp.

    I think we’ve all been trained by bad libraries and bad components to always expect problems in multithreading. I recently heard of a colleague using an address validation component that, in 2015, required single threading in order to match addresses to geospatial coordinates – an operation we all assumed was thread-safe.

Comments are closed.

Skip to main content