« Picking picture on ma… | Home | MBS Xojo Plugins, ver… »

Preemptive vs. cooperative threads in Xojo

When Xojo adds preemptive threading in one of the next releases, a few people will jump into this and play with it. But you need to understand when to use preemptive and when cooperative threads are better. As far as we know, you can switch this anytime by setting a property to basically opt-out the thread from the cooperative thread scheduler and run the thread preemptive.

On demand switching

A chance where to switch explicitly to the preemptive thread is when calling a lengthly functions like for example JSON parsing. Let's show a bit of pseudo-code:

Sub TestJSON() // some JSON you got a text Var Text As String = ... // switch to preemptive Thread.Current.Type = Thread.Types.Preemptive // now parse without blocking main thread Dim json As New JSONItem(Text) // and maybe switch back Thread.Current.Type = Thread.Types.Cooperative // do something with JSON End Sub

Since parsing JSON may take a while and would block the main thread for a multi megabyte big JSON text, running it on a preemptive thread makes sense and allows GUI to continue. Especially in the web project, this may help to speed up the handing of requests.

The above code doesn't show what happens on an exception. It may be good to have a thread switch to cooperative automatically in case of an exception. This would help since destructors running automatically may not be made for preemptive threads.

No longer safe

Let's take a code like this:

Sub AddJob(job as job) JobsArray.Add job End Sub
Sub ProcessJobs() Do If JobsArray.Count > 0 Then Dim job As job = JobsArray.Pop job.run Else Thread.Current.Sleep(20) End If Loop Until Quitting End Sub

If you run a couple of worker threads like this in Xojo with cooperative threads, everything is fine. Somewhere entries are added to the jobs array. And each thread checks for the count and picks something from the array.

Now you switch to preemptive threading and this will crash. Multiple threads access the same array to read and write. In cooperative threading the switch happens as part of the loop statement on the end. In preemptive threads, switching happens anytime and two threads may run at the same time on different cores. So one thread could check count, then other thread makes the pop while the first thread also calls pop. Two threads going into pop method at the same time on the same array will cause trouble.

Add mutex

To fix this, we add a mutex called JobsArrayMutex. We enter it when we want to do something and leave it when we are done. Adding a job now looks like this:

Sub AddJobSafe(job as job) // only one thread can enter at a time. Others wait JobsArrayMutex.Enter // modify array JobsArray.Add job // make sure you leave mutex JobsArrayMutex.Leave End Sub

And to process them, we limit the mutex enter to leave lines to a minimum, so the processing of the job is outside the mutex. Otherwise the threads would basically fall back to be cooperative.

Sub ProcessJobsSafe() Do Dim job As job // only one thread can enter at a time. Others wait JobsArrayMutex.Enter // modify array If JobsArray.Count > 0 Then job = JobsArray.Pop End If JobsArrayMutex.Leave if job <> nil then job.run Else Thread.Current.Sleep(20) End If Loop Until Quitting End Sub

You could use the same code for cooperative, but since Xojo only switches on loop boundaries, you don't need them and that makes Xojo's cooperative threads much easier for the average user. Be careful when doing coding for preemptive threads. We don't want Xojo to change Dictionary, Arrays, Variants and other containers to be thread safe as this would slow them down in regular use.

Once the next beta of Xojo shows up to testers, please try the new feature and learn how to use it.
And by the way, Xojo already has preemptive threads on Android, so watch out!

07 05 24 - 08:52