I’ve asked these sorts of questions about preemptive threads in the past and never really got a detailed answer. Cooperative threads barely need any concurrency management. So for preemptive threads I lock every access of any property that could be used by multiple threads. The trouble is, logically the CriticalSection itself has to reside inside a property, so Xojo must be doing some kind of thread safety.
This leads me to suspect that property access itself is safe. Read and write to your heart’s content. It’s the stuff inside that needs to be taken care of. Anything that requires multiple lines of code be guaranteed to be executed without interruption.
A good example would be reading a value from a Dictionary. Calling Dictionary.Value is fine, but something like If Dict.HasKey(Key) Then Dict.Remove(Key) is trouble because it wouldn’t be impossible for the key to have already been removed by another thread. This is where you’d need to lock so that the two actions are performed without interruption.
Certain code structures are a problem with this though. Consider a queue:
While StringArray.Count > 0
Var Member As String = StringArray(0)
StringArray.RemoveAt(0)
// Do things
Wend
You can’t just lock the entire loop, as that would make the queue useless. This is where AtomicQueueMBS really shines. But if you don’t want to use that, there are ways:
Lock.Enter
While StringArray.Count > 0
Var Member As String = StringArray(0)
StringArray.RemoveAt(0)
Lock.Leave
// Do things
Lock.Enter
Wend
Lock.Leave
This way you’re locking the boundary and the initial access together, while allowing manipulation of the array by other threads.
Exceptions will be your mortal enemy here:
Lock.Enter
Raise New NilObjectException
Lock.Leave
Your lock will be left open. This is just an example of course, the exception could come from anywhere. So I recommend always including a try block an having a plan for your exceptions:
Lock.Enter
Try
Raise New NilObjectException
Catch Err As RuntimeException
// Ignoring it is an option. Not a great one, but an option.
End Try
Lock.Leave
This is starting to look like a TON of boilerplate. So I’ve got one last tip for you: destructors.
Build a simple class that accepts a CriticalSection, locks it in the constructor, and unlocks it in the destructor. When the object goes out of scope for any reason, the lock is released.
Var Holder As New LockHolder(Lock)
Raise New NilObjectException
The lock still gets released because the variable will go out of scope.
Ken Tekinay has a great example of this in Xojo-ThreadPool/Common/M_ThreadPool/LockHolder.xojo_code at master · ktekinay/Xojo-ThreadPool · GitHub which is where I got the idea from.
And prepare to lose some hair. Preemptive threads are a nightmare. I’ve got an article at The ZAZ: Navigating the Perils of Preemptive Threads and Locking that repeats a lot of this, but has some additional tips and traps to be aware of.