Are you getting strange errors - Check Xojo.Core.Timer Usage

Yes, I"m starting a new thread about Xojo.Core.TImers because it’s a problem. I have a window that is used in basically a slide show fashion to show image. The user has the option of automatically stepping through images at a given interval. This stepping is controlled through a standard timer placed on the window in the IDE.

On this window, I have a canvas subclass that acts like a push button. I’ve done this so I can have “advance”, “play” and “backward” buttons that can be pushed. In the action event of the timer, I push the advance button. Now I have been getting error reports occasionally about an exception being thrown. I have never been able to really duplicate it but it happens. It’s a NOE. I’ve made it so it fails silently and has never caused any issue. But I’ve not been able to figure out why. In fact, I was using some MBS Buttons and thought maybe there was a problem in Christian’s code and so I started using my own. Same issue. So I started adding more Try/Catch blocks sending me e-mails whenever it occurs. I’ve finally been able to trace the problem.

The code below is the action event for the timer mentioned above that is placed on the window. AdvanceImage is the “button” canvas subclass instance that I put on the window in the IDE. Now, I don’t get NOEs in this event. But look towards the bottom. I make the call to Xojo.Core.Timer to fire 250 milliseconds after the button is pushed. I’ll continue after the code block:

Sub Action() Handles Action
  Static TimesFired as integer = 0
  
  TimesFired = TimesFired+1
  
  If bar.value < 99 Then
    bar.value = bar.value+1
    bar.Refresh
  else
    bar.value = bar.value+1
    bar.Refresh
    
    Try
      AdvanceImage.Value = True
    Catch
      SendErrorEmail("Happened at AdvanceImage.Value = True in SlideTimer")
    End Try
    
    Try
      AdvanceImage.Refresh
    Catch
      SendErrorEmail("Happened at AdvanceImage.Refresh in SlideTimer")
    End Try
    
    Try
      Bar.Value = 1
    Catch
      SendErrorEmail("Happened at Bar.Value = 1 in SlideTimer")
    End Try
    
    ' Call code to release the advance button in 250 Milliseconds
    Xojo.Core.Timer.CallLater(250,AddressOf ReleaseAdvanceImageButton)
    TimesFired = 0
  End If
  
End Sub

Now, the ReleaseAdvanceImageButton method is a method of the window where I am operating. This is the code for that method:

Public Sub ReleaseAdvanceImageButton()
  Try
    AdvanceImage.Value = False
  Catch
    SendErrorEmail("Happened at AdvanceImage.Value = False")
  End Try
  
  If AdvanceImage = Nil Then
    SendErrorEmail("AdvanceImage is Nil!!! - WHY?")
  End If
  
  Try
    AdvanceImage.Refresh
  Catch
    SendErrorEmail("Happened at Advance.Refresh")
  End Try
End Sub

When the error happen - this is where it is happening. Somehow, some way an object placed in the window by the IDE is suddenly NIL. Now I was trying to think how this would happen. OK, so let’s say someone decides to close the window at just exactly the right instance (the quarter second) between when the first timer pushes the button and when the CallLater method is to release it. OK, it COULD happen. However, this happens far to regularly for something that would need to be so precise to happen. It’s just not statistically possible. But I think if the Window was closed, there would be more of a problem with the timer trying to call a method of the window. So let’s scratch that and assume that the Window is staying open.

So now, it’s impossible for a control that is sitting on a window to go NIL when NOTHING calls that control to be closed. Maybe there’s a paint call going on during the time the Xojo.Core.Timer.CallLater method fires but that shouldn’t make the canvas show as NIL.

No, rather I blame something fundamentally wrong in Xojo.Core.Timer that has not been properly addressed. At times it seems things go NIL inside whatever code structure is used to handle all this. Sometimes it works fine. But at other times it does not. And Xojo needs to address it.

Bottom line: I am NOT going to use this any longer. It has caused me more grief than I can count and like a drunk, I keep going back to the bottle thinking I won’t be harmed by the next sip.

I warn everyone else not to use it either. I bet if you stop using it, and use a traditional timer or the SingleActionTimer class that Karen wrote, you might see strange errors suddenly vanish.

Beware!

I have had controls being nil when they really shouldn’t be a fair few times. I think it is events firing in unexpected times, eg when the window has not yet initialised or closing down. It is enough to send you mad looking at the stack trace to see where the error came from and seeing that there is no way it makes sense. My only solution was to check for nil controls in the effected methods, then walk away.

Also send your stack trace in the error to help debug.

What does the Paint look like for AdvanceImage and what is the timer frequency for the Action you posted above?

[quote=396261:@Graham Busch]I have had controls being nil when they really shouldn’t be a fair few times. I think it is events firing in unexpected times, eg when the window has not yet initialised or closing down. It is enough to send you mad looking at the stack trace to see where the error came from and seeing that there is no way it makes sense. My only solution was to check for nil controls in the effected methods, then walk away.

Also send your stack trace in the error to help debug.[/quote]

Well, I understand what you are suggesting but in this case it’s impossible. The window has to be initialized in order for the user to take the action that would start the process of causing the first timer to fire.

Here is the paint event for the ButtonCanvas Subclass. I have extra lines (all the ln = ) in the code for debugging trying to see if the NOE was happening somewhere in the paint event. It’s not.

ImagedPressed is a property of type picture
ImageNormal is also a property of type picture.

DrawAndScaletoAspect is a method that scales the image to the size of the canvas keeping its aspect ratio constant.

Sub Paint(g As Graphics, areas() As REALbasic.Rect) Handles Paint
  dim ln as integer
  
  Dim p as New Picture(me.width,me.Height)
  ln =1
  
  If me.MouseDwn Then
    ln = 2
    DrawAndScaleToAspect(p,ImagePressed)
    ln = 3
  Else
    ln = 4
    If Not Toggle Then
      ln = 5
      DrawAndScaleToAspect(p,ImageNormal)
      ln = 6
    End If
    ln = 7
  End If
  ln = 8
  
  If Toggle Then
    ln = 9
    If Toggled and ImageToggle = Nil or (Toggled and MouseDwn) Then
      ln = 10
      DrawAndScaleToAspect(p,ImagePressed)
      ln = 11
    ElseIf Toggled and ImageToggle <> Nil Then
      ln = 12
      DrawAndScaleToAspect(p,ImageToggle)
      ln = 13
    ElseIf Not Toggled And me.MouseDwn and ImageToggleDone <> Nil Then
      ln = 14
      DrawAndScaleToAspect(p,ImageToggleDone)
      ln = 15
    Else
      ln = 16
      DrawAndScaleToAspect(p,ImageNormal)
      ln = 17
    End If
    ln = 18
  End If
  ln = 19
  g.DrawPicture(p,0,0)
  ln = 20
  
  Exception
    SendErrorEmail "Happened in Paint Event of Button Canvas at Line: "+str(ln)
End Sub

So there’s nothing in here except drawing a picture.

How long is the first traditional timer period? Hard to say. The user determines that but considering it is for displaying a slide show, it would need to be on the order of several seconds at least.

It can happen window’s close method is called before your code runs.

So I usually check:

If controlcount = 0 then // window closed already Return End if

So I avoid the issue with no controls being there.

And the call to:

AdvanceImage.Value = False

works fine, it never throws here? Then a line later the check:

If AdvanceImage = Nil Then

returns Nil?

Have you tried it with:

If AdvanceImage = Nil Then SendErrorEmail("AdvanceImage is Nil!!! - BEFORE DOING ANYTHING?") End If

above the:

AdvanceImage.Value = False

as well so you can see if its one or the other.

As far as I’m aware, there’s no way the code should yield out of that method in between two lines of code to possibly cause an issue mid method, timer usage or not.

[quote=396294:@Christian Schmitz]It can happen window’s close method is called before your code runs.

So I usually check:

If controlcount = 0 then // window closed already Return End if

So I avoid the issue with no controls being there.[/quote]
Christian,

You are correct. However, the odds of a window being closed < 250 mSec after the button is programmatically pushed are quite small. It is something I should code for. However, in practical application of how my program operates, closing the window while running a slide show would not be something you would really want to do. It just wouldn’t be practical. But then again, one must code for the impractical! :slight_smile:

[quote=396296:@]And the call to:

AdvanceImage.Value = False

works fine, it never throws here? Then a line later the check:

If AdvanceImage = Nil Then

returns Nil?

Have you tried it with:

If AdvanceImage = Nil Then SendErrorEmail("AdvanceImage is Nil!!! - BEFORE DOING ANYTHING?") End If

above the:

AdvanceImage.Value = False

as well so you can see if its one or the other.

As far as I’m aware, there’s no way the code should yield out of that method in between two lines of code to possibly cause an issue mid method, timer usage or not.[/quote]

Julian,

When the exception gets thrown that whole ReleaseAdvanceImage method throws the exception. So the Xojo.Core.Timer.CallLater method, executes the ReleaseAdvanceImageButton 250 mSec after it’s called. When the exception happens (and it is not always that this happens - it’s random), AdvanceImage is NIL the whole time.

Public Sub ReleaseAdvanceImageButton()
  Try
    AdvanceImage.Value = False   // Yes - It throws it here
  Catch
    SendErrorEmail("Happened at AdvanceImage.Value = False")
  End Try
  
  If AdvanceImage = Nil Then
    SendErrorEmail("AdvanceImage is Nil!!! - WHY?")   // Yes - it shows NIL here.
  End If
  
  Try
    AdvanceImage.Refresh  // And it throws it here too.
  Catch
    SendErrorEmail("Happened at Advance.Refresh")
  End Try
End Sub

So you never actually get the “AdvanceImage is Nil!!! - WHY?” email?

Yes I do. I get all three. The Try/Catch blocks keep things from crashing.