I am seeing some pretty serious crashes with preemptive threads in windows. I have multiple copies of my thread running all in different instances of the same object. Things run fine for a while but eventually I get a runtime error. It happens when I am drawing a picture in both the main thread and in running thread at the same time. But the picture objects are from different instances. Seems like there’s corruption between instances in Windows. Yes, everything is semaphored, but since it’s different instances, the semaphores don’t come into play.
It’s similar to another report with the same issue. In that issue @Rick_Araujo attached an example project showing how to “properly” handle the issue the user was describing. I have taken Rick’s example, and added 5 more instances of the same canvas and thread. In MacOS I can run everything just fine. In windows, as soon as you add the second thread, it crashes. I don’t get the runtime error, it just crashes.
William confirmed a crash. Your design is completely different of the simple code I’ve fixed, and you are playing with contents intended to be used on UI from a thread, using drawing routines from such thread, that at some extents it is forbidden, unless completely detached from any UI. I guess William found that you intended to do an off-screen drawing, maybe valid, but some framework assertion was fired as if it was targeting the UI from a background thread and it crashed. Not really a concurrency problem at this point. We really need William inspection (and maybe a fix) of your case on the R4 cycle.
Rick - There is no drawing from a thread. All I did was take your example and make multiple copies of it in the same app. I did not touch your thread code or the code in the canvas. All I did was create a control set of canvases and an array of threads. I added each thread as a property of the canvas.
I don’t think you took much time to look at what I did. I spent less than 10 minutes on this. It is absolutely drawing the exact same way as yours did. If you have a single thread running it works just fine - just like your example. It’s when you add a second thread that windows freaks out. So I think you are rushing to judgement here.
If I did as you claim, please point to where I am doing it. Back it up, please.
Uh friend, that is your code. Not mine. And yes, it’s a rectangle. You can draw into graphics contexts of pictures in threads. You can’t draw into UI controls.
I’m using YOUR code, so please don’t throw stones at me.
Sub Run() Handles Run
// Using the current design intent, Create a CriticalSection to control the access to OffscreenPicture
CritSectOffScrPicture = New CriticalSection
CritSectOffScrPicture.Type = Thread.Types.Preemptive
CritSectOffScrPicture.Enter // let's touch OffscreenPicture
// create a new picture to draw the pixels into
OffscreenPicture = New Picture(Width, Height)
CritSectOffScrPicture.Leave // Release it to anyone else
Do
CritSectOffScrPicture.Enter // let's touch OffscreenPicture
// draw a random colorered pixel into a random spot in the offscreen picture
OffscreenPicture.Graphics.DrawingColor = Color.RGB(Rnd * 255, Rnd * 255, Rnd * 255)
OffscreenPicture.Graphics.FillRectangle(Rnd * OffscreenPicture.Width, _
Rnd * OffscreenPicture.Height, Rnd * 50, Rnd * 50)
//me.sleep(1)
CritSectOffScrPicture.Leave // Release it to anyone else
Loop Until Stop // the Stop property of the thread is set by the Stop button in the window
End Sub
And as I have been saying, running a single or multiple threads in MacOS works great. No issues.
Running a single thread in Windows works great. No issues.
Attempting to run a second thread in Windows results in the crash.
I did not touch your thread code one bit. It’s exactly as you wrote it.
I don’t have much time, so it will be done gradually.
First part is: What you called “my app”, was just someone else’s sample that was crashing, and I just fixed what was broken. No enough clean ups, not exactly well done, just fixed.
So I got my take on that thread, refactored a bit, and here is my version (made right now, I hope I’ve cleaned it enough):
Your changes make the BackgroundDrawing object more elegant. But they don’t fundamentally change what the thread is doing or how it is doing it.
I have a further update as well. Given the challenges I was having with the Listbox control (you’ve been involved in that thread here on the forums as well), I wanted to see if I could create an example project to duplicate that problem. So I decided to add a listbox to this project and effectively attempt to recreate a situation similar to what my app is doing.
I’ve added a listbox that has three columns. Column 0 contains just plain text. The other two are columns that get updated by the PaintCellTextEvent. When I add a new canvas and thread, I add a row to the listbox, set the “name” in column 0 and then add the canvas as the RowTag of the listbox.
I then have a timer that creates a random number and sets it as a property of the canvas. I then call the PaintCellText event for each row of the Listbox. In the PaintCellText Event of the listbox, I reference the canvas, grab the value of the property and paint it into Column 1. I then draw a graphic in Column 3 depending on whether the number is even or odd.
On Mac this runs fine. Unfortunately, I was not able to get it to crash as I had hoped.
In WIndows, this version crashes immediately.
Again, NOTHING in what I am doing touches any of the thread code or objects in the thread. If you (or anyone else) can see where I am doing this and violating a rule - please show me.
In the link below is a movie of how things work on the Mac and how things work in Windows (tried attaching it as a ZIP file but the editor is throwing an error here - maybe too large).
Attached is the project. Again, show me where I am doing something like touching the UI portion of the control from the thread. I’m not… DrawThread.xojo_binary_project.zip (13.0 KB)
Furthermore, I just decided to see what happens in Windows if I don’t even paint the picture generated by the thread to the canvas. I’ve commented out the code in the paint event of the canvas. Still get a crash.
What is the difference other than syntactical ones? You’ve moved the critical section calls to methods in the thread. You added a constructor method.
The picture object is created in the main thread (constructor) instead of in the thread itself. The algorithm that is running in the loop in the thread is the same thing. I really fail to see how those changes make it faster or more stable.
Also, I’ve had the latest version of the app running on my Mac for two hours and it’s still running fine. If I was accessing UI objects from the thread or not properly accessing objects, it would have crashed. OS X does not like it if you attempt to manipulate the UI from the thread.
Well, William just updated my case. There is absolutely a problem in Windows with threads drawing pictures concurrently:
Additionally, we don’t currently support concurrent drawing of Pictures on separate threads on Windows (we may be able to in the future but this greatly impacts drawing speed), so you’ll need to handle locking and unlocking at the app level to ensure only one thread is drawing on its Picture at a time.
So if you want to do picture drawing in multiple threads, you will need to have an app level lock that controls this. That’s a big drawback in my mind as the whole purpose of preemptive threading is to be able to do concurrent actions…
Not meaning to Hijack the thread. Thank you for persisting on working with debugging Preemptive threads. I am watching in the background as to how successful this is.