I have some code I am using that I got online that is a TFTP server in Xojo. I’ve brought it up to date and it’s been working pretty well but I’m having a problem right now I can’t understand.
I am getting a Framework Failed Assertion Error when trying to turn a timer off. Here’s the code:
' this method is public so listening socket can pass the first recieved packet to the port switched session socket.
Dim mb As MemoryBlock
Dim packet_length As Integer
Dim Filename, Mode, DataStr, ErrorMsg As String
Dim OP_Code, BlockNum, ErrorCode As UInt16
packet_length = Data.LenB
mb = New MemoryBlock(packet_length)
mb.LittleEndian = False
mb.StringValue(0, packet_length)= Data
Dim tt as UInt16
tt = mb.UInt16Value(0)
Select Case mb.UInt16Value(0)
Case OP_RRQ
Filename = mb.CString(2)
Mode = mb.CString(2+Filename.LenB+1)
intRetryAttempts = 0
WatchDog.Mode = Xojo.Core.Timer.Modes.Off
Recieve_Read_Request(Filename, Mode)
Case OP_WRQ
Filename = mb.CString(2)
Mode = mb.CString(2+Filename.LenB+1)
intRetryAttempts = 0
WatchDog.Mode = Xojo.Core.Timer.Modes.Off
Recieve_Write_Request(Filename, Mode)
Case OP_ACK
Dim int2 as Integer = Microseconds-me.mSec
BlockNum = mb.UInt16Value(2)
intRetryAttempts = 0
WatchDog.Mode = Xojo.Core.Timer.Modes.Off
Recieve_Acknowledge(BlockNum)
Case OP_DATA
BlockNum = mb.UInt16Value(2)
DataStr = mb.StringValue(4, packet_length-4)
intRetryAttempts = 0
WatchDog.Mode = Xojo.Core.Timer.Modes.Off
Recieve_Data_Block(BlockNum, DataStr)
Case OP_ERROR
ErrorCode = mb.UInt16Value(2)
ErrorMsg = mb.CString(4)
Recieve_Error(ErrorCode, ErrorMsg)
End Select
intTimestamp = Get_Time
The WatchDog is the timer in question. This method is called by the DataAvailable event of the socket.
I’ve tried this with both the standard old framework timer and also with the Xojo Framework Timer. Both give the same problem. I’m somewhat stumped as to why this is the case. I also thought that perhaps the problem was that he was using the ActionNotificationReceiver interface for handling the timer’s action event. I changed that to an AddHandler directive instead. No difference. Same thing…
It only creates a problem in debugging but I think a compiled version has problems too as when I ran a compiled windows version, it all worked but I kept getting numerous beeps that were like indications of errors…
So I am stuck as what might be causing this. I turn timers off in other applications with no problem…What does this assertion mean?
I’m sorry Christian, I don’t follow you. There is no SetTimer method I am using (oh wait - I see - it’s in the Xojo framework that method. No idea…). Here’s the Constructor for the TFTP session which is a UDPSocket subclass:
Public Sub Constructor(Remote_Address As String, Remote_Port As Integer, Netint as NetworkInterface = Nil, Path As FolderItem, Timeout As Integer = 4, EventLogger As EventLoggerInterface = Nil)
Dim r As New Random
SysLog = EventLogger
szRemote_Address = Remote_Address
intRemote_Port = Remote_Port
Self.RouterHops = 64
me.NetworkInterface = NetInt
If Path.Exists And Path.Directory Then
fiPath = Path
Else ' bad path given
'System.DebugLog("Access Error - Can not find root - bad file path"
Send_Error(ERROR_ACCESS, "Can not find root.")
intSessionMode = SESSION_QUIT
Return
End If
Do
Self.Port = r.InRange(49152,65535)
Self.Connect
Loop Until Self.IsConnected
WatchDog = New Xojo.Core.Timer
WatchDog.Period = 100
WatchDog.Mode = Xojo.Core.Timer.Modes.Off
AddHandler WatchDog.Action, WeakAddressOf PerformTimerAction
'WatchDog.addActionNotificationReceiver(Self)
CloseTimer = New Timer
CloseTimer.Period = 0
CloseTimer.Mode = Timer.ModeOff
AddHandler CloseTimer.Action, AddressOf DoClose
FlushThread = New Thread
AddHandler FlushThread.run, AddressOf FlushThreadAction
FlushThread.run
intTimestamp = Get_Time
intSessionMode = SESSION_UNKNOWN
intTimeout = Timeout
Write_Log("New sesion to "+Remote_Address+":"+Str(Remote_Port)+" File:"+Path.NativePath)
'System.DebugLog("New sesion to "+Remote_Address+":"+Str(Remote_Port)+" File:"+Path.NativePath)
End Sub
That’s where WatchDog and some other timers are created.
I haven’t attempt to check if the timer is off before turning it off…I can. Sounds silly to have to do that though…
You get a failed assertion, because the developer doing the xojo framework wrote an assert() check.
He expects the function SetTimer used here to always return a value which is not zero.
Now it returns zero, but the case is not handled by raising an exception (as it should), but causing your app to exit with reporting a failed assertion.
I just started using AddHandler today. Previously, the code was using the ActionNotifierReceiver interface for handling the timer. So right now, I haven’t messed with RemoveHandler, but that should not be a problem because I’m using a WeakRef in the AddHandler Statement. So whenever the timer goes out of scope, the link to the method should break anyhow. But bottom line is this was happening before the code was changed by me a short while ago.
I previously have not had this issue. So I’m not sure what changed… And the code should only create one timer per TFTP session. So I don’t think there’s a ton of timers here (this is a different Timer issue with a different application than my StackException timer issue in another thread).
What’s really odd is that this code works fine for some period of time. Then it suddenly fails. And only one timer is being created - the constructor is the only place and it’s only called once…
I’m wondering if the following scenario could be happening…
The TFTP Socket has two events where the timer is set to mode single. Those are in SendComplete and in Resend_Packet. I’m wondering if the timer gets set to mode single but before Windows is able to do it’s thing and complete it’s function of setting the timer, the Xojo framework is trying to change the mode of the time a second time. Maybe it’s all happening too quickly…
It’s gotta be some sort of conflict like that because sometimes it works. Then it doesn’t…
[quote=308415:@Christian Schmitz]Maybe the framework registers a lot of timers with Windows OS without freeing them.
[/quote]
Well, Christian, I think you hit the nail on the head with this one…
It’s only one timer. However, a LOT of calls are made to the SetTimer function… Here’s why…
When the TFTPSocket sends out a Datagram, it then starts the timer going looking for an ACK or other response from the device it is communicating with. If it receives that ACK, it turns the timer off. Due to the size of the packets and the speed at which the transfer is taking place, it is making a lot of calls to SetTimer. I put a counter in the SendComplete method for the socket and it made 9985 calls to SetTimer before the assertion started happening.
I tried everything - using CallLater, not turning the timer off but instead setting a boolean variable that would bypass the time code once the timer fired, etc. Still got the assertion.
Then I began thinking that the timer period is 100 mSeconds. That’s fairly long when you are sending 4 byte packets. The timer is being set to mode single multiple times when it’s already running! So I set it to only start the timer if the timer isn’t already running. That made things work just fine. I know doing so messes up the function of the watchdog somewhat. It should really be reset after every datagram is sent. The way I have it now, the 100 mS Wait period would only be valid for the first datagram sent. If a datagram some milliseconds later is not acknowledged, the timer will effectively fire early for that datagram.
I’m just not sure how to accomplish this without hammering the set timer function so much. It seems amazing to me that Windows is having problems after approximately 10,000 calls to SetTimer…
for i as integer = 1 to 10000
timer1.mode= 1
timer1.mode= 0
next
It died at i = 9968.
One workaround that comes to mind would be to set the timer to a shorter interval, say 20ms, make it mode multiple, and use a global/appropriately scoped variable to store the Microseconds value when you start looking for a response (instead of starting the timer) and set it to zero when you get the response (instead of stopping the timer). The timer action code would be something like
if TimeoutCounter > 0 then
if (Microseconds - TimeoutCounter) > 100000 then
// we've been waiting 100ms
end
end
[quote=308462:@Tim Hare]I got the same assertion with this code:
for i as integer = 1 to 10000
timer1.mode= 1
timer1.mode= 0
next
It died at i = 9968.
One workaround that comes to mind would be to set the timer to a shorter interval, say 20ms, make it mode multiple, and use a global/appropriately scoped variable to store the Microseconds value when you start looking for a response (instead of starting the timer) and set it to zero when you get the response (instead of stopping the timer). The timer action code would be something like
if TimeoutCounter > 0 then
if (Microseconds - TimeoutCounter) > 100000 then
// we've been waiting 100ms
end
end
[/quote]
TIm. Thank you. Good idea. I’ll look into doing something like this.
I think that not being able to run code this simple without a failure is a problem. Looks like it is a Windows problem since Xojo uses its SetTimer function. But Xojo should definitely not throw a framework assertion…
[quote=308462:@Tim Hare]
One workaround that comes to mind would be to set the timer to a shorter interval, say 20ms, make it mode multiple, and use a global/appropriately scoped variable to store the Microseconds value when you start looking for a response (instead of starting the timer) and set it to zero when you get the response (instead of stopping the timer). The timer action code would be something like
if TimeoutCounter > 0 then
if (Microseconds - TimeoutCounter) > 100000 then
// we've been waiting 100ms
end
end
[/quote]
Hey Tim,
FYI - I’ve implemented your idea here in my code and it seems to work pretty well. Thanks!