In Xojo 2024r3.1 we introduced the highly requested preemptive threads feature. This new thread type enables true independent execution of code, separate from the main thread or any other threads, allowing full utilization of all CPU cores. Let’s explore some of the improvements we’ve made since adding this capability.
Protecting Our Objects
Xojo objects are uniquely constructed and reference counted, a detail most users don’t need to think about. However, when working with preemptive threads, objects inherently require special protection. In Xojo 2024r3.1, we implemented safeguards to enable the use of objects in preemptive threads, but the approach wasn’t as efficient as it could be. Operations like passing objects as parameters or assigning them as properties incurred overhead, and those costs added up.
As uniquely as our objects are to construct, they also required a unique approach to protect them. In Xojo 2024r4 we updated how we protect our objects so that this operation happens much faster, improving the overall experience when running preemptive threads.
For an example of these efficiencies, check out this forum post where users benchmark the One Billion Row Challenge.
Uniquely Destructed Objects
As mentioned, our objects are uniquely constructed, and this uniqueness extends to their destruction as well. When a preemptive thread is running, it’s important not to wait for other objects to be destructed within the Destructor method.
Sub Object1.Destructor()
While Object2 <> Nil
// Waiting for Object2 to be destructed can lead to a deadlock,
// especially if Object2 is being destructed from a separate thread.
Wend
End Sub
Also, avoid situations where you are creating new objects while in the Destructor and referencing Self.
Sub Object1.Destructor()
// Referencing Self in a new object will lead to a
// recursive Destructor sequence causing a dead lock.
Var newObj As New Class1
newObj.NotAGoodIdea = Self
End Sub
Handling Race Conditions
In Xojo 2024r3.1, we created an example demonstrating how to synchronize shared resources using critical sections and semaphores. This example also highlights the effect of a race condition: your app will crash immediately, terminating the debugging session with no indication as to where the crash may have occurred.
These access violations were never caught by our debugger until now. Because these crashes may interfere with what can be displayed in the debugger, we’ve intentionally limited what is shown, as the access violation could leave the app in an unstable state. Although these access violations are not exclusive to crashes caused by race conditions, this new feature aims to help you identify potential race conditions in your apps when working with preemptive threads. Let’s take a look at that example and see what happens now when you run it through the debugger:
When you click the button designed to intentionally crash the app, you’ll now be returned to the debugger, where you’ll see a new crash 💣 icon. This symbol indicates that a crash has occurred, and the app cannot proceed further (which is why all debugging step commands are disabled, and the debugger values are not loaded so objects will be Nil). Your only options at this point are to stop the app or attempt to reload the debugger values.
Clicking the command button on the left refreshes or reloads the debugger values. If the app is stable enough, the debugger values will refresh; otherwise, the app may terminate automatically at this point.
In summary, these improvements aim to make working with preemptive threads more enjoyable by offering both enhanced speed and an easier process for identifying potential race conditions, which was previously much more challenging. Like the One Billion Row Challenge, we’re eager to hear about all the ways you’re making use of preemptive threads in your own apps!
William Yu grew up in Canada learning to program BASIC on a Vic-20. He is Xojo’s resident Windows and Linux engineer, among his many other skills. Some may say he has joined the dark side here in the USA, but he will always be a Canadian at heart.