B4J Question B4J program crash in Debug mode, but not in Release

knutf

Active Member
Licensed User
Longtime User
This program crash in Debug mode, but not in Release mode. Is it something to do with this?

B4X:
'Non-UI application (console / server application)
#Region Project Attributes
    #CommandLineArgs:
    #MergeLibraries: True
#End Region

Sub Process_Globals
End Sub

Sub AppStart (Args() As String)
    ResumableSub
    Log("Continues after calling ResumableSub")
    StartMessageLoop
End Sub

'Return true to allow the default exceptions handler to handle the uncaught exception.
Sub Application_Error (Error As Exception, StackTrace As String) As Boolean
    Return True
End Sub

Sub ResumableSub()
    Dim myEventGerator As Eventgenerator
    myEventGerator.Initialize(Me,"Genarator ResumableSub")
    Log("ResumableSub Waiting")
    Wait For (myEventGerator) TheEvent(origin As String)
    Log("The event happend in ResumableSub, raised by " & origin)
End Sub

B4X:
'Class module, class name is Eventgenerator
Sub Class_Globals
    Private pTargetModule As Object
    Private MyTimer As Timer   
    Private pID As String
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(TargetModule As Object, ID As String)
    pTargetModule = TargetModule
    MyTimer.Initialize("MyTimer",1000)
    MyTimer.Enabled = True
    pID = ID
End Sub

Public Sub MyTimer_Tick   
    CallSub2(pTargetModule,"TheEvent", pID)
    MyTimer.Enabled = False
End Sub

Log in Release mode
B4X:
ResumableSub Waiting
Continues after calling ResumableSub
The event happend in ResumableSub, raised by Genarator ResumableSub

Log in Debug mode
B4X:
Waiting for debugger to connect...
Program started.
ResumableSub Waiting
Continues after calling ResumableSub
Feil oppstod på linje: 17 (Eventgenerator)
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at anywheresoftware.b4a.keywords.Common.CallSubDebug2(Common.java:460)
    at b4j.example.eventgenerator._mytimer_tick(eventgenerator.java:89)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:628)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:237)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:168)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:90)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:98)
    at anywheresoftware.b4a.objects.Timer$TickTack$1.run(Timer.java:118)
    at anywheresoftware.b4a.keywords.SimpleMessageLoop.runMessageLoop(SimpleMessageLoop.java:30)
    at anywheresoftware.b4a.StandardBA.startMessageLoop(StandardBA.java:26)
    at anywheresoftware.b4a.ShellBA.startMessageLoop(ShellBA.java:119)
    at anywheresoftware.b4a.keywords.Common.StartMessageLoop(Common.java:153)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:308)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:168)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:90)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:98)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:77)
    at b4j.example.main.main(main.java:29)
Caused by: java.lang.RuntimeException: java.lang.ArrayIndexOutOfBoundsException: 2
    at anywheresoftware.b4a.debug.Debug.CallSub4(Debug.java:131)
    at anywheresoftware.b4a.debug.Debug.CallSubNew2(Debug.java:78)
    ... 34 more
Caused by: java.lang.ArrayIndexOutOfBoundsException: 2
    at anywheresoftware.b4a.shell.DebugResumableSub$DelegatableResumableSub.resume(DebugResumableSub.java:41)
    at anywheresoftware.b4a.BA.checkAndRunWaitForEvent(BA.java:135)
    at anywheresoftware.b4a.debug.Debug.CallSub4(Debug.java:107)
    ... 35 more
 

Attachments

  • ProgramCrashingInDebugMode.zip
    1.3 KB · Views: 157

Daestrum

Expert
Licensed User
Longtime User
Seems to work if you change
B4X:
CallSub2(pTargetModule,"TheEvent", pID)
to
B4X:
CallSubDelayed2(pTargetModule,"TheEvent", pID)
 
Upvote 0

knutf

Active Member
Licensed User
Longtime User
Ok, but in my actual program it is important that "TheEvent" sub is executed imidiatly. If it is put last in the message que some variables will be changed by other code not shown in my first post
 
Upvote 0

knutf

Active Member
Licensed User
Longtime User
The test project that illustrate the problem is uploded in my first post. I think it is difficult to do testing in my original project because it depends on external hardware.
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
My guess: Since Debug mode is slower, your timer tick goes off before you get to
B4X:
Wait For (myEventGerator) TheEvent(origin As String)
or before that code line has finished doing what it needs to do and then your MyTimer_tick event issues the CallSub2 without the target being available or properly set up, creating the error that you see in the logs.

Edit: BTW, you should probably set pID before enabling the timer. I know that in this case the code is executed fast enough that it does not matter, but any data that is required by your Timer's tick event should be set up before enabling the Timer.

Edit2: Change the time from the Timer initialization routine from 1000 to something like 10000. If that clears the error, my guess may be correct, otherwise it is something else.
 
Upvote 0

knutf

Active Member
Licensed User
Longtime User
OliverA! I set the timer interval to 10000 and also moved the setting of pID to before MyTimer initialization as you suggested, the new code is below. I get the same crash in Debug mode, but it works in Release mode.

B4X:
'Class module
Sub Class_Globals
    Private pTargetModule As Object
    Private MyTimer As Timer   
    Private pID As String
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(TargetModule As Object, ID As String)
    pTargetModule = TargetModule
    pID = ID
    MyTimer.Initialize("MyTimer",10000)
    MyTimer.Enabled = True
End Sub

Public Sub MyTimer_Tick   
    CallSub2(pTargetModule,"TheEvent", pID)
    MyTimer.Enabled = False
End Sub

I have also tried changing

B4X:
CallSub2(pTargetModule,"TheEvent", pID)

to

B4X:
CallSub2(pTargetModule,"TheEvent", "my ID")

with same result: crash in Debug mode
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
One more thing to try, disable the timer before using CallSub2.
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
It may be a bug in the debugger, but I still think that enabling the Timer in Initialize and then setting up a Wait For is a bug waiting to happen. Please note that this is just my opinion (I'm not an expert). If your timer event fires and executes the CallSub before you set up the Wait For, the Wait For will just sit there. This could cause sporadic issues that would be hard to debug. A workaround that works in both Release and Debug mode is as follows:
B4X:
'Non-UI application (console / server application)
#Region Project Attributes
   #CommandLineArgs:
   #MergeLibraries: True
#End Region

Sub Process_Globals
   
End Sub

Sub AppStart (Args() As String)
   ResumableSub
   Log("Continues after calling ResumableSub")
   StartMessageLoop
End Sub

'Return true to allow the default exceptions handler to handle the uncaught exception.
Sub Application_Error (Error As Exception, StackTrace As String) As Boolean
   Return True
End Sub

Sub ResumableSub()
   Dim myEventGerator As Eventgenerator
   myEventGerator.Initialize("Genarator ResumableSub")
   Log("ResumableSub Waiting")
   Wait For (myEventGerator.WaitOnTimer) complete (origin As String)
   Log("The event happend in ResumableSub, raised by " & origin)
   StopMessageLoop
End Sub

Class module code:
B4X:
'Class module, class name is Eventgenerator
Sub Class_Globals
   Private MyTimer As Timer
   Private waitTime As Int
   Private pID As String
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(ID As String)
   waitTime = 1000
   MyTimer.Initialize("MyTimer",waitTime)
   pID = ID

End Sub

Public Sub WaitOnTimer As ResumableSub
   MyTimer.Enabled = True
   Do While MyTimer.Enabled
       Sleep(waitTime)
   Loop
   Return pID
End Sub

Private Sub MyTimer_Tick
   MyTimer.Enabled = False
End Sub

Please note I posted something totally wrong previously, but deleted it (it was that bad - I'm blaming Monday, again). If timing is of the importance, then I think you need to set up your timer in the main class and use the timer's event then to call some function of your class module, without using wait for.
 
Upvote 0

knutf

Active Member
Licensed User
Longtime User
OliverA!
Added after the first submit: -Thank you for your suggestions, they feed my thinking with ideas.-

In my actual app it's not a timer that initiates the sub that call the Callsub2, it is a AsyncStreams NewData event that after some decoding of the data calls the sub that do Callsub2

If your timer event fires and executes the CallSub before you set up the Wait For, the Wait For will just sit there.
How can this happen when everything is run on the main thread? I think the Timer_Tick allways will be called after the Wait For are set up. It is the Wait For that cause the thread to temporary leave the sub so that messages in the message queue can be handled. I think the timer tick is queued in the message queue

Prefer CallSubDelayed over CallSub.
Do you mean this as a general rule? Why?
This is especially true when you want to intercept the event with Wait For.
Is this becouse a possible bug in the debugger, or could it also lead to problems using Callsub in combination with Wait For in the Release version of the code?
 
Last edited:
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Do you mean this as a general rule?
Yes.
Otherwise you can encounter cases where the event is raised before the Wait For line is executed.

For example:
B4X:
DoSomething
Wait For Result_Complete

Sub DoSomething
 If x > 0 Then
   'send http request
   wait for JobDone
 End If
 CallSub(Me, "Result_Complete")
End Sub
If x = 0 then the event will be lost.
If you use CallSubDelayed then it can never happen.
 
Upvote 0

knutf

Active Member
Licensed User
Longtime User
If x = 0 then the event will be lost.
I understand that an event will be lost in your example. In my example the Callsub2 is raised in the MyTimer_Tick. I think when the timer periode is elapsed, a message is placed in the message queue. The MyTimer_Tick event will be raised after The execution of the Wait For line. It is the execution of the Wait For line that cause the main thread to leave the Resumable Sub and handle messages in the message queue. Is that true?

In that case, is it still risky to use CallSub2 in combination with Wait For? Why?
 
Upvote 0
Top