Something is bugging me about preemptive threading

I have a feeling my understanding of locking requirements is a bit off. As I understand it, any property that has a possibility to be used from another thread should be locked on both read and write. So I have lots of code that looks something like this:

Function PropValue() As String
  Self.mLock.Enter
  Var Value As String = Self.mPropValue
  Self.mLock.Leave
  Return Value
End Function
Function PropValue(Assigns Value As String)
  Self.mLock.Enter
  Self.mPropValue = Value
  Self.mLock.Leave
End Function

This way, there is absolutely zero opportunity for two threads to access the same property at the same time.

What’s bugging me is the lock itself is stored in a property. Which leads me to suspect that I don’t actually need to be locking properties and that Xojo is doing that for me. It may be that the only time I need to worry about locking is when doing multiple actions with the same property when state is important:

Self.mLock.Enter
If Self.mDict.HasKey("Key") = False Then
  Self.mDict.Value("Key") = "Value"
End If
Self.mLock.Leave

And maybe even multiple accessors on the same line?

Self.mLock.Enter
Var Prefix As String = Self.mPropValue.Left(Self.mPropValue.IndexOf(":"))
Self.mLock.Leave

Can anybody confirm which is correct?

1 Like

I remember reading something from William stating that property updates are not atomic. This would suggest that you do have to surround all access to the properties with locks.

I’ve not tried it, but if you can guarantee that access to the property will be read-only while a thread is running you might not need the locks.

1 Like

You should be okay as long as you are not creating the locks within the preemptive thread code.

I tend to create object wrappers around the properties that need to be accessed by threads which create the lock when they’re constructed and use getter/setter pairs to manage the locks.

What about creating the locks inside a constructor, even if that constructor is called within a thread? The constructor should have no opportunity of collision since it cannot be assigned to a property or variable until the constructor finishes. Shouldn’t that be safe?

A transactional lock is a bit more than just access that unit of data in a queued manner, there’s a kind of mindset, like when using Transactions in SQL. There are times you don’t want to encapsulate a property in a lock/unlock, probable most of them when private to a class, and many times when you need to act on its “public” value.

For example, if you encapsulate all data, and want to update a counter that was wrapped in a lock/release, you could end with a wrong pseudo-sequence like this:

Static counter As integer
Var temp As Integer

cs.Enter
temp = counter
cs.Leave

// Opportunity for an error

cs.Enter
counter = temp + 1
cs.Leave

And you really just need to do this directly:

cs.Enter
Counter = Counter + 1
cs.Leave

Sometimes you need several statements, but you should just use one lock/release pair:

cs.Enter
whatever1()    // No locks here
whatever2()    // Neither here
whatever3()    // Neither here
cs.Leave

I’m not used to the frameworks doing protections for me, if I turn off my transactional mindset and believe that the framework will do the necessary job (as many users used to cooperative model probably do) it surely will fail at the logic layer of things, the max it can do is avoid some crashes, not even all them. But counters and balances and states will get wrong results without crashes.

Oh yeah, definitely. I’m aware of all that already. Threading and locking is not a new concept to me. Heck, I gave a talk at XDC about it. I’m just trying to make sure I understand what preemptive threads need.

1 Like

Sorry for the ping @William_Yu, I was just hoping to get some official clarification, and figured you’d be the one to ask.

1 Like

It depends on your goal, as strings have some special locking for preemptive threading. If your main concern is avoiding crashes, you won’t need to protect your strings unless you’re dealing with a Shared string, as one user found out in this issue: https://tracker.xojo.com/xojoinc/xojo/-/issues/77525.

However, if your goal is to ensure the string value remains consistent — meaning you always retrieve the correct value without worrying about it changing during the assignment — then locking is necessary since the assignment isn’t atomic.

2 Likes

Thank you, that helps.