Non-nil thread gives NilObjectException

I’m trying to download some images from the internet. When there is an error the user is asked if he wants to continue or pause downloading. For a long time I have used the code below to show a dialog:

[code]theMessage = hasMessage
Button = hasButton

dim currentThread as Thread = App.CurrentThread
if currentThread = nil then
HandleAction(Nil)
return
end if

'create the timer
me.ShowDialog = new DelegatingTimer
me.ShowDialog.Mode = 1
me.ShowDialog.Period = 100
me.ShowDialog.Action = AddressOf me.HandleAction

theThread = currentThread
theThread.suspend[/code]

where the dialog is shown in HandleAction. However, today I’m totally astonished to get a NilObjectException in the last line. The debugger shows me that theThread is not Nil. The assignment is only made when currentThread isn’t Nil. So how can there be a NOE?

The download is an additional feature of a parser which can continue without downloading. So if there are network problem I’d like to ask if the use want to continue or not.

Does anyone have an idea what might be going on here?

Xojo 2018r3, High Sierra.

Any ideas? The location of the crash seems accidental. If I take out the error I get the NOEs all over the place. The NOEs are related to the way I do an array of sockets:

[code]'make some sockets
for currentSocket as Integer = 0 to Min(UBound(theSecureSocket), UBound(theLinks))
theSecureSocket(currentSocket) = new HTTPSecureSocket
'get the events
AddHandler theSecureSocket(currentSocket).PageReceived, WeakAddressOf PageReceived_Handler
AddHandler theSecureSocket(currentSocket).Error, WeakAddressOf Error_Handler
next

'start downloading
for currentSocket as Integer = 0 to Min(UBound(theSecureSocket), UBound(theLinks))
dim nextLink as String = theLinks.Pop
if Left(nextLink, 5) = “https” then theSecureSocket(currentSocket).Secure = True
theSecureSocket(currentSocket).Get(nextLink)
Next

'check if we are finished
dim Timeout as integer = Ticks + 60 * 10
while not isFinished and Ticks < Timeout
if Globals.StopArchiving then Return
app.SleepCurrentThread(10)
wend[/code]

Error_Handler terminates processing, PageReceived_Handler continues processing:

'download the next link dim nextLink as String = theLinks.Pop if Left(nextLink, 5) = "https" then Sender.Secure = True Sender.Get(nextLink)

Is this something stupid?

To me, it looks like the App object which can become nil on quitting before everything has been executed.

I would recommend to check App and App.currentThread against nil everytime you access them

The app isn’t quitting. After adding a check on App.currentThread I STILL get an NOE. The NOEs are related to my socket array. I must be doing something stupid there.

CurrentThread returns Nil if it’s the main thread

Ahem… have a look at the start of my code:

dim currentThread as Thread = App.CurrentThread if currentThread = nil then HandleAction(Nil) return end if

Again: it’s my sockets that make the mess. I just don’t understand why and how to fix the problem.

Are we sure that AddHandler (and related methods) are truly compatible with Threads?

Maybe instead on relying on AddHandler you could subclass your socket class so it executes itself the code of PageReceived_Handler and Error_Handler. At least just to see if you still get a NOE

@Stephane Mons: good idea. I’ll try the subclassing tomorrow.

Try switching to a Namespaced Framework socket and using 2018r2 (I’m not seeing any good news about URLConnection) while testing that theory.

I have a wrapper class that does some Text and Xojo.Core.Memoryblock to String conversion so that the returned contents in PageReceived are a String. It’s not super complete so I don’t share it openly, but I know you’ll fix whatever you need if you need to, so if you’d like it just drop me a PM or email. :slight_smile:

Are you keeping the threads somewhere as a reference or could there be a chance that the thread finishes between you checking it for being nil and then handling it?
(Yes, I see it as a rather tiny chance, but it would be the only explanation for me why this could be happening without suspecting a Xojo bug: The thread, being a self-referencing object, finishes, gets out of scope and so becomes nil.)

Nope. Main thread and one single additional thread. The problem shows only up with the socket array. For one single socket everything is okay - just too slow.

Maybe you get a Nil Thread still cause the context switches back to the mainthread which is nil?

You request a reference from app.currentthread so you may end up getting it Nil at any point.

You may want to run trough the debugger with break on exceptions on, the find the reference of ththread and see if it’s nil at that line ending for some unknown reason
This sounds very impossible i know, but still you give info that sounds impossible so i’d be searching trough the debugger.

Good point! @Beatrix, what would happen if you move the suspend call to an else extension of the If clause above?

BTDT! I chased a shell that would throw an NOE and it turned out to be exactly that - in some runs, the context switched just prior to my assigning my local thread reference. That ended up assigning my local reference to Nil because App.CurrentThread was the main loop.

You call App.CurrentThread from the maintrhead and expect it to be another thread, then it can happen if you call App.CurrentThread and it’s even expected to be Nil (mainthread).

you may be better off doing this only within the thread and just keep it running forever or with a flag that may end the thread.

It must be something with context switches. However, the error dialog is only supposed to show when the socket has an error and has finished processing.

Gr… today the error is not reproducible. Which I so totally love.

Then I did more testing and got a website with HTTP 1.1. Tried to use the newer socket. But on the help page it says:

Which is about as clear as mud.

@Beatrix that one means in the PageReceived event do not re-use the socket itself. Something happens and it’s bugged for the second request. Creating a new socket object is safe to do. I usually build a queue system and add sockets to an array, removing them after they have completed.