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.

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.

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.

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.