Platforms to show: All Mac Windows Linux Cross-Platform

/Util/Preemptive Threading/Mutlithreading test


You find this example project in your Plugins Download as a Xojo project file within the examples folder: /Util/Preemptive Threading/Mutlithreading test

This example is the version from Wed, 28th Feb 2023.

Project "Mutlithreading test.xojo_binary_project"
Class App Inherits DesktopApplication
Const kEditClear = "&Delete"
Const kFileQuit = "&Quit"
Const kFileQuitShortcut = ""
EventHandler Sub Opening() #If Target32Bit Then #Pragma error "Needs 64-bit!" #EndIf RegisterPlugins // test declares, so Xojo preloads the pointers #If TargetWindows Declare Function GetCurrentThreadId Lib "kernel32" () As UInt32 Dim n As Integer = GetCurrentThreadId #Else Declare Function pthread_self Lib "/usr/lib/libpthread.dylib" () As UInt64 Dim n As Integer = pthread_self #EndIf End EventHandler
End Class
Class MainWindow Inherits DesktopWindow
Control StartButton Inherits DesktopButton
ControlInstance StartButton Inherits DesktopButton
EventHandler Sub Pressed() CreateJobs End EventHandler
End Control
Control List Inherits DesktopListBox
ControlInstance List Inherits DesktopListBox
End Control
EventHandler Sub Opening() CreateJobs End EventHandler
Sub CreateJobs() Dim FillJobs() As Job Dim CopyJobs() As Job #If DebugBuild Then Dim TestSize As Integer = 1024 * 1024 * 16 // 16 MB #Else Dim TestSize As Integer = 1024 * 1024 * 1024 // 1 GB #EndIf // make some jobs to fill memory For i As Integer = 0 To 3 Dim j As New job j.Func = AddressOf FillJob j.label = "FillJob"+i.ToString j.Output = New MemoryBlock(TestSize) j.FinishedHandler = AddressOf JobFinished j.StartedHandler = AddressOf JobStarted Job.addJob j FillJobs.Add j Next // and a copy job to waste some CPU For i As Integer = 0 To 3 Dim j As New job j.Func = AddressOf CopyJob j.label = "CopyJob"+i.ToString // we use memoryblock from FillJob as input here j.Input = FillJobs(i).Output j.Output = New MemoryBlock(TestSize) j.FinishedHandler = AddressOf JobFinished j.StartedHandler = AddressOf JobStarted j.AfterJob = FillJobs(i) // <- wait until FillJob is done job.addJob j CopyJobs.add j Next // and sum the memory For i As Integer = 0 To 3 Dim j As New job j.Func = AddressOf SumJob j.label = "SumJob"+i.ToString // we use memoryblock from CopyJob as input here j.Input = CopyJobs(i).Output j.Output = New MemoryBlock(8) // output j.FinishedHandler = AddressOf JobFinished j.StartedHandler = AddressOf JobStarted j.AfterJob = CopyJobs(i) // <- wait until CopyJob is done job.addJob j Next // start them Job.RunJobs End Sub
Sub JobFinished(j as job) Log "Job "+j.ID.ToString+": "+j.label+" finished on thread: "+j.ThreadID.ToString If j.Output.Size = 8 Then Log "Sum: "+j.Output.UInt64Value(0).ToString End If End Sub
Sub JobStarted(j as job) Log "Job "+j.ID.ToString+": "+j.label+" started on thread." End Sub
Private Sub Log(s as string) System.DebugLog s List.AddRow s End Sub
End Class
MenuBar MainMenuBar
MenuItem FileMenu = "&File"
MenuItem FileQuit = "#App.kFileQuit"
MenuItem EditMenu = "&Edit"
MenuItem EditUndo = "&Undo"
MenuItem EditSeparator1 = "-"
MenuItem EditCut = "Cu&t"
MenuItem EditCopy = "&Copy"
MenuItem EditPaste = "&Paste"
MenuItem EditClear = "#App.kEditClear"
MenuItem EditSeparator2 = "-"
MenuItem EditSelectAll = "Select &All"
MenuItem HelpMenu = "&Help"
End MenuBar
Class Job
Delegate Sub JobFinished(j as job)
Shared Sub AddJob(j as job) jobs.Add j End Sub
Function CanRun() As Boolean If Started Then return false ElseIf AfterJob = Nil Then Return True Else Return AfterJob.Finished End If End Function
Sub Constructor() // assign unique ID IDCounter = IDCounter + 1 ID = IDCounter End Sub
Private Shared Sub JobTimerAction(t as timer) Dim launch As Boolean = false For Each j As job In jobs If j.Started And Not j.Finished Then If j.Data.UInt32Value(8) <> 0 then j.ThreadID = j.data.UInt64Value(48) j.Running = false j.Finished = True If j.FinishedHandler <> Nil Then j.FinishedHandler.Invoke(j) End If launch = True jobs.RemoveAt jobs.IndexOf(j) exit // come back later to run more End If End If Next If launch Then RunJobs End If End Sub
Sub Run() StartTimer data = New MemoryBlock(100) data.UInt64Value(0) = ID data.UInt32Value(8) = 0 // done flag if Input <> nil then data.Ptr(16) = Input data.UInt64Value(24) = Input.size End If If Output <> Nil Then data.Ptr(32) = Output data.UInt64Value(40) = output.size End If If DebugBuild Then Started = True func.Invoke(data) Return End If If StartedHandler <> Nil Then StartedHandler.Invoke(Self) End If If CallDelegateOnPreemptiveThreadMBS(func, data) Then Running = true Started = True Else Break End If End Sub
Shared Sub RunJobs() // start all jobs in queue StartTimer Dim RunningCounter As Integer For Each j As job In jobs If j.Running then RunningCounter = RunningCounter + 1 Continue End If If j.CanRun Then j.run RunningCounter = RunningCounter + 1 End If If RunningCounter >= 4 Then // 4 in paralell is enough for us Exit end if Next End Sub
Private Shared Sub StartTimer() If JobTimer = Nil Then JobTimer = New timer AddHandler JobTimer.Action, AddressOf JobTimerAction JobTimer.Period = 50 JobTimer.RunMode = timer.RunModes.Multiple End If End Sub
Property AfterJob As Job
Property Private Data As MemoryBlock
Property Finished As Boolean
Property FinishedHandler As JobFInished
Property Func As _delegatePtrMBS
Property ID As Integer
Property Shared IDCounter As Integer
Property Input As MemoryBlock
Property Private Shared JobTimer As Timer
Property Shared Jobs() As job
Property Label As string
Property Output As MemoryBlock
Property Running As Boolean
Property Started As Boolean
Property StartedHandler As JobFInished
Property ThreadID As Integer
End Class
Module JobModule
Sub CopyJob(data as Ptr) #Pragma BackgroundTasks False #Pragma BreakOnExceptions False #Pragma StackOverflowChecking False #Pragma NilObjectChecking False // get input parameters from job data Dim ID As UInt64 = data.UInt64(0) Dim InputData As Ptr = data.Ptr(16) Dim InputSize As UInt64 = data.UInt64(24) Dim OutputData As Ptr = data.Ptr(32) Dim OutputSize As UInt64 = data.UInt64(40) // do work Dim u As Integer = InputSize-7 For i As Integer = 0 To u Step 8 OutputData.UInt64(i) = InputData.UInt64(i) Next // we like to know which thread got the job #if TargetWindows Declare Function GetCurrentThreadId Lib "kernel32" () As UInt32 data.UInt64(48) = GetCurrentThreadId #Else Declare Function pthread_self Lib "/usr/lib/libpthread.dylib" () As UInt64 data.UInt64(48) = pthread_self #EndIf // mark job done data.UInt32(8) = 1 // done flag End Sub
Sub FillJob(data as ptr) #Pragma BackgroundTasks False #Pragma BreakOnExceptions False #Pragma StackOverflowChecking False #Pragma NilObjectChecking False // get input parameters from job data Dim ID As UInt64 = data.UInt64(0) Dim InputData As Ptr = data.Ptr(16) Dim InputSize As UInt64 = data.UInt64(24) Dim OutputData As Ptr = data.Ptr(32) Dim OutputSize As UInt64 = data.UInt64(40) // do work Dim u As Integer = OutputSize-7 For i As Integer = 0 To u Step 8 OutputData.UInt64(i) = i Next // we like to know which thread got the job #If TargetWindows Declare Function GetCurrentThreadId Lib "kernel32" () As UInt32 data.UInt64(48) = GetCurrentThreadId #Else Declare Function pthread_self Lib "/usr/lib/libpthread.dylib" () As UInt64 data.UInt64(48) = pthread_self #EndIf // mark job done data.UInt32(8) = 1 // done flag End Sub
Sub SumJob(data as Ptr) #Pragma BackgroundTasks False #Pragma BreakOnExceptions False #Pragma StackOverflowChecking False #Pragma NilObjectChecking False // get input parameters from job data Dim ID As UInt64 = data.UInt64(0) Dim InputData As Ptr = data.Ptr(16) Dim InputSize As UInt64 = data.UInt64(24) Dim OutputData As Ptr = data.Ptr(32) Dim OutputSize As UInt64 = data.UInt64(40) // do work Dim Sum As UInt64 Dim u As Integer = InputSize-7 For i As Integer = 0 To u Step 8 Sum = Sum + InputData.UInt64(i) Next // store result OutputData.UInt64(0) = sum // we like to know which thread got the job #If TargetWindows Declare Function GetCurrentThreadId Lib "kernel32" () As UInt32 data.UInt64(48) = GetCurrentThreadId #Else Declare Function pthread_self Lib "/usr/lib/libpthread.dylib" () As UInt64 data.UInt64(48) = pthread_self #EndIf // mark job done data.UInt32(8) = 1 // done flag End Sub
Note "Warning"
Experimental function to play with preemptive-threads in Xojo apps. Some rules: * No objects, no strings, no arrays * Use Ptr, not Memoryblocks * Crashes in debugger, so only use in built app * Use #Pragma BackgroundTasks False * Use #Pragma BreakOnExceptions False * Use #Pragma StackOverflowChecking False * Use #Pragma NilObjectChecking False * No exceptions * Delegate must be stored in global variable to avoid it getting freed early. * You can pass up to one ptr as data parameter. uses thread pool on iOS, macOS and Windows. Otherwise regular threads.
End Module
Module MBSPluginRegistration
Sub RegisterChartDirector() // License code registration could be done here End Sub
Sub RegisterDynaPDF() // License code registration could be done here End Sub
Sub RegisterPluginMakeDoku() // License code registration could be done here End Sub
Sub RegisterPlugins() // License code registration could be done here End Sub
Sub RegisterSQL() // License code registration could be done here End Sub
End Module
End Project

See also:

The items on this page are in the following plugins: MBS Util Plugin.


The biggest plugin in space...