Preemptive Thread Issues on Windows - Multiple Threads

Happy my efforts are helping people! :slight_smile:

2 Likes

If you have two threads each with its own picture that would cause a crash, even if the interface isnā€™t working with that same picture?

If it is simply a matter of the interface must not access the picture while the thread is drawing to it. If so, you could try having a critical section and duplicating the the completed drawing for use by the interface. To put it another way Give the thread its own picture to draw into. Once complete duplicate it for use my the interface. Protect the duplication and each thread and the interface have their own image?

Looking at the sample code William uploaded, it appears that you canā€™t have multiple threads drawing to the SAME picture at the same time without a semaphore, which makes intuitive sense; eventually, the ā€œfixā€ in the framework will likely be to move the semaphore into the framework so you donā€™t have to worry about it.

This suggests that you could still have multiple threads drawing to their own pictures - still a huge advantage.

No, in the example, each thread instance is drawing to its own picture which is a property of the thread.

Furthermore, it looks like you have to lockout/control drawing between all main thread drawings and threads.

I just added an app based Critical Section to my app and called it before doing any drawing in the thread and before making a copy of my drawing in the main thread. Iā€™m still getting the same crash. So it seems like you canā€™t be drawing to any picture in the main thread and to any picture in any threads at the same time. To any called to Graphics.DrawPicture need to be locked before and unlocked after.

I tried your example and in some way I got a solution (strange but it works, waiting for real solution)
On your canvas object I added
a property backPic as Picture
a property cs as CriticalSection
a method picCall that accept a picture and draw it in the backPic
a method Start that initialize cs and backPic, create the thread (if it is nil) and start the thread (your start method)
a method Stop that if thread is not nil call your thread stop method

added to your thread a delegate with the picCall signature and a property cbPic for this delegate (creating the thread set the callback)

in your run method after the drawing (after unlockPicture) :
For MacOS: if cbPic<>nil then lockPicture, invocke cbPic with your offScreenPictur and unlock
For Windows: call AddUserInterfaceUpdate ā€œpicā€:ā€œā€
then a little For cycle (100) with some simple calculation

in the UserInterfaceEvent (thatā€™s used only on win) for each dictionary, for each dictionary entry if the key is ā€œpicā€ call the same sequence used for MacOs in the Run event

In the PaintEvent (Canvas on the window) simply check if backPic and cs are not nil then if cs.tryEnter then draw the backPic and leave the cs (cs.leave)

In my test it works without problem, maybe you have to find the right value for the loop and the right operation:I use Var d As Double=Sqrt(i^i)
On Mac you can have a for with 0 loop, on win I found 100 is the minimal value
In preemptive thread timing and function availability are really important

Itā€™s simpler than that! :smiley:

William updated the example in the bug case. A critical section is added to the app object. Then before any call to the Graphics object, you make a call to enter the critical section. Then leave it after the draw.

Problem is I think you have to do this for EVERY drawing operation as it appears that in Windows the Graphics object cannot be touched by more than one thread at a time (main thread included). Now, I donā€™t know why Xojo canā€™t put a lock into the framework. That would certainly make it easier for all of us.

Presumably one could have it cover a set of operations, such as enter and leave at the start and end of the paint event.

Constantly locking and unlocking has a detrimental effect on performance and would slow down all graphics code even for users who arenā€™t using preemptive threads.

I am surprised that there are problems if thread ā€˜aā€™ touches picture ā€˜aā€™ and thread ā€˜bā€™ touches picture ā€˜bā€™ at the same time.

1 Like

Iā€™d agree. Itā€™s not an issue with Mac. Just with Windows.

Here is your problem working.
I just hope you can transfer the knowledge there to whatever you want. :wink:

JonProblem.zip (11.3 KB)

@Rick_Araujo
That code does not work on Windows. It eventually crashes if left running. As William pointed out updating more than one picture at same time on Windows causes crashes.

Location: DrawableD2D.cpp(534)
Message: CreateBitmapFromWicBitmap failed: 88982f0d

As far as I remember Iā€™ve protected that. Will double check.
How long until a crash? I couldnā€™t replicate with few minutes.

About a minute. You seem to only have a CriticalSection for each thread / image. Windows will not allow more than one picture to be updated at the same time. William currently suggests a single CriticalSection to protect any Windows Picture drawing globally across the whole app. Obviously, thatā€™s a major speed issue.

I suspect that such rule applies even for off-screen contents.
That kills the entire process. Basically it will end the entire parallelism.
So, we will need to wait for R4 and the same sample will run as expected once William fix the framework.
I could change the sample to make it follow such rule, but it should be kind of equivalent of using a cooperative modelā€¦

Iā€™ve made a fast change. Fusing all locks. Check if it stabilizes what you saw.
JonProblem2.zip (21.3 KB)

The blue one:
image

Williamā€™s example in the bug report works. And it doesnā€™t slow it down too much.

But if you have a lot of threads and a lot of graphics draws, this could bring something to its knees really quickly. Only one operation can touch the graphics object in Windows at a time. William is working on a fix and so far he says the results are goodā€¦

Mine above seems to work too. For the current workload no human perceivable change too.

But interestingly, my previous one is not crashing here too.

Mine was tested with Windows 11 ARM, 64bit. Under UTM.

Hmmm, interesting mix. Maybe associated. Mine is W11 Intel native DirectX GPU accelerated. Few minutes, no issues.

If we get more people trying the sample we could notice some pattern.

Is it still crashing using the second version with global lock?

Away from computer for the moment.