Using Asynchronous GetXmlHttpObject() vs threads

aerohost

Member
Licensed User
Hi All, I'm having trouble with my new application because it uses the http module to received data over the net.

Because it is a synchronous protocol, my whole program freezes up while it does this and it becomes problematic, interfering with what the user may be doing on the device, especially if the internet connection is of poor quality.

What I really need is a way of putting this process into the background, so that it doesn't interfere with the user doing other things.

Is there a way to do this with threads, or by using some of the new Ajax techniques?

Thanks, Adrian
 

agraham

Expert
Licensed User
Longtime User

aerohost

Member
Licensed User
Thanks, that looks like the solution I've been hoping for, I'll definitely give it a whirl.

Just wondering, what do you think about using Ajax techniques as an alternative - is this even possible with b4ppc?

Thanks, Adrian
 

agraham

Expert
Licensed User
Longtime User
As I understand it, and I'm not too good on Webby stuff, Ajax is a web server/browser interaction that requires client side scripting. I could give a long complicated answer but the short answer is it is neither appropriate for nor possible with B4ppc.

As a matter of background B4ppc is single threaded and historically byte-code interpreted. More recently it has grown a fully compiled capability. It deliberately does not provide asynchronous methods in it's libraries as this would be complicated to implement in a byte-code machine and a complication for the programmer in it's role as a simplified rapid development environment.

My Threading library, which only actually threads on optimised compiled apps, is one of my subversive attempts to make things more complicated :) by letting the synchronous operations of B4ppc effectively become asynchronous. It allows asynchronism without getting into the complications of setting callbacks that .NET fully asynchronous operations demand and which are not supported by B4ppc.
 

aerohost

Member
Licensed User
Thanks! That's a really good answer, imho, just what I was trying to understand ... onward ho to the thread library it is ...

Regards, Adrian :sign0087:
 

aerohost

Member
Licensed User
Hi Andrew, I tested your excellent thread library with the 'ThreadTest.sbp' example you provided.

The thread that displayed the counter 0-1000 seemed to work well, so I then replaced just that section with an http page call. I then disabled my net connection and found that it behaved the same as ever and locked up the program until the timeout completed, then it just stopped, ignoring the ErrorLabel.

Any idea why that wouldn't work?

Tks, Adrian
 

aerohost

Member
Licensed User
Hi Andrew, thanks for your reply. I put the changed sub at the bottom, nothing else was altered.

So I think the clue here is your 'optimised compiled' statement. I am only testing it from the IDE, without making an exe. Is that creating problems, in other words, do I have to make an exe to test it properly?

Also, I'm confused by the 'legacy mode' message box I keep getting when run from the IDE. Does that mean I don't have all the required up-to-date stuff (I thought I had the latest versions of everything), or do I just get that because I'm running from the IDE?

Thanks, Adrian
---------------------
Sub ThreadCode
ErrorLabel(ThreadCodeErr1) ' if abort is used to stop a thread an (intended by .NET) error occurs

'try to fetch data from server
Response.New1
Request.New1("http://myURL")
' Msgbox(url)
'This line calls the server and gets the response.
Response.Value = Request.GetResponse

string = Response.GetString 'Get the Response string.
'did we get data? if we hit next line, we did
Msgbox(string)
tb1text = "ok"

' For i = 1 To 1000
' For j = 1 To 1000
' k = i + j
' Next
' tb1text = i
' Thread.FireThreadEvent ' events work for testing even if not running on a separate thread
' Next
ThreadCodeErr1: ' ignore any error
tb1text = "not ok"
'Return k ' stops unused variable error
End Sub
 

agraham

Expert
Licensed User
Longtime User
Is that creating problems, in other words, do I have to make an exe to test it properly?
The second paragraph of Overview in the help
The Thread object of this library is intended for use with the optimising compiler of Basic4PPC version 6 onwards. However it can run, after a fashion, in the IDE and in a legacy compiled application. The library protects itself, and you, if it finds itself running in a legacy mode and issues warnings that functions are not available. The purpose of allowing this is to enable debugging of thread code in the IDE because debugging a compiled application is more tricky than using the debugger in the IDE.
Threading does not work in the IDE as that is a byte-code environment (known as legacy mode to us cognoscenti :)) as is non-optimised compilation. True threading will only work in an optimised compiled app. However you can debug a threaded app (sort of) in the IDE and if you read the demo app closely it has some pointers in there as to how. To debug an optimised compiled app you can try the Watcher from my debug library which can look inside a running optimised compiled app. http://www.b4x.com/forum/additional-libraries/2328-debug-library.html

Also, I'm confused by the 'legacy mode' message box I keep getting when run from the IDE. Does that mean I don't have all the required up-to-date stuff (I thought I had the latest versions of everything), or do I just get that because I'm running from the IDE?
That's the library warning you that threading is not available in the legacy environment.
 
Last edited:

aerohost

Member
Licensed User
Hi Andrew, ok, thanks very much for your feedback. I'm using your 'ThreadTest.sbp' example to try and understand how this works.

In the 'Sub ThreadCode' you have the loop that counts to 1,000 you have this code:

For i = 1 To 1000
For j = 1 To 1000
k = i + j
Next
tb1text = i
Thread.FireThreadEvent
Next

I'm presuming this is the actual background task you are running in a thread to demonstrate that the user can still do other things on the form while the text box displays the updated value. This works fine for me - it's a good way to demonstrate events running in a background thread.

So, I'm trying to substitute that code with a simple http request, as follows:

Response.New1
Request.New1("myURL")

'This line calls the server and gets the response.
Response.Value = Request.GetResponse
Thread.FireThreadEvent

If I'm connected to the net, it works fine, but when I disconnect from the net, the whole form is stalled and you can't do anything else. I have put a drop-down listbox on the form as an example of doing something on the form while the internet is being accessed, and it is frozen while the thread attempts to connect to the non-existent internet connection.

This is the essence of my problem: I need the form activity to continue, when the http request is made, even if the internet connection is broken.

I'm wondering if I have the 'Thread.FireThreadEvent' statement in the wrong place?

I haven't changed anything else, except in the initialization sub:

#Region Create and start a thread

Thread.New1(B4PObject(1))
If Optimising Then
Thread.Start("ThreadCode") ' returns true if started, false if not
Else
'ThreadCode
End If

#End Region

I commented out the ThreadCode line so nothing would happen until I pressed the start button because the legacy message was getting in the way, and I needed to be able to clear that first to test it properly?

So can you see any reason why the http request jams up everything?

Thanks, Adrian
 

agraham

Expert
Licensed User
Longtime User
I commented out the ThreadCode line so nothing would happen until I pressed the start button because the legacy message was getting in the way, and I needed to be able to clear that first to test it properly?
I don't understand this. Optimising is a global variable and in the demo it is set true, so unless you have set it false that commenting out should do nothing. You shouldn't be getting a legacy message anyway if you are running an optimised compiled app.
So can you see any reason why the http request jams up everything?
You are optimising compiling this to run it aren't you? I can't easily dump my network connection but I've run the following code as you have described, adding a line to see the response and a duff URL that causes a couple of seconds delay before GetResponse returns. Pressing the Join button reports that the thread is running showing that my user interface is alive during this time as I would expect.
B4X:
Response.New1
Request.New1("http://www.dac")
'This line calls the server and gets the response.
Response.Value = Request.GetResponse
tb1text = Response.GetString ' added to see the response
Thread.FireThreadEvent
 

aerohost

Member
Licensed User
ok, so maybe I'm not understanding this correctly. When you say

"You are optimising compiling this to run it aren't you?"

do you mean I have to compile it into an exe for it to work properly? I'm confused because your 1000 loop example seems to run fine in the IDE as a background task when I test it.

Tks, AB
 

agraham

Expert
Licensed User
Longtime User
do you mean I have to compile it into an exe for it to work properly?
Exactly! As I noted previously threading is not possible in the byte-coded legacy environments of the IDE and non-optimised compiled exes. That's why I implemented the legacy warnings.

I'm confused because your 1000 loop example seems to run fine in the IDE as a background task when I test it.
It runs, but not as a background task and not without modifying my demo code. Set the global variable Optimising to false and it runs under the IDE. This demo is structured to show how code could be tested in the IDE without threading and then run compiled as a threaded app.

Please read through the demo app and the help file line by line. My technical writing tends to be terse and succinct and every word is normally there for a purpose so you can't just skim my stuff and expect to understand what's going on.
 

aerohost

Member
Licensed User
Hi Andrew, thanks I'll give it a try. Honestly, I read and re-read your instructions, but I was confused because it appeared to run in the IDE, so I thought I was mis-interpreting them.

What I didn't understand was that although it is running, it isn't running as a thread until the program is compiled. So, my http request was running, but not as a thread either, and it was jamming everything up as usual.

Thanks for taking the time to clarify this because, frankly, if I can't get this to work, my project is dead, and I will have wasted hundreds of hours. If I can get this working, your threading will save the day! I'll let you know ...

Regards, Adrian
 

aerohost

Member
Licensed User
Hi Andrew, that's good news from Erel about adding asynchronous communication support in the next release. But, I'd still like to get this working as I can see threads are very useful, not just for this.

I compiled it as we discussed, and it appears to be working. But, when I disable the network connection, the thread appears to just stop after it timesout, and it doesn't display the 'not ok' message in the textbox. In other words, it doesn't seem to get to the error routine - any idea on that?

Tks, Adrian (code below)

Sub ThreadCode
ErrorLabel(ThreadCodeErr1)

'try to fetch data from server
Response.New1
Request.New1("myURL")

'This line calls the server and gets the response.
Response.Value = Request.GetResponse
Thread.FireThreadEvent

'did we get data? if we hit next line, we did
Msgbox(Response.GetString)
tb1text = "ok"
Return

'program flow should go here if there's no internet found or other error
ThreadCodeErr1:
tb1text = "not ok"
Return

End Sub
 

agraham

Expert
Licensed User
Longtime User
I'm surpised it does display the OK message in the testbox! Does it? If you are still using the structure of the demo then the textbox is updated by the ThreadEvent and your FireThreadEvent is in the wrong place. You also have a superfluous Return in there that is doing no harm. It should look like this.
B4X:
Sub ThreadCode
  ErrorLabel(ThreadCodeErr1)

  'try to fetch data from server
  Response.New1
  Request.New1("myURL")

  'This line calls the server and gets the response.
  Response.Value = Request.GetResponse

  'did we get data? if we hit next line, we did
  tb1text = "ok"
  Thread.FireThreadEvent ' update textbox
  Msgbox(Response.GetString)
  Return

  'program flow should go here if there's no internet found or other error
ThreadCodeErr1:
  tb1text = "not ok"
  Thread.FireThreadEvent ' update text box

End Sub
The ONLY GUI thing you can do within a thread is to display a messagebox. Anything else must be done by a ThreadEvent or DebugEvent.

Hi Andrew, that's good news from Erel about adding asynchronous communication support in the next release.
Note he said after not in -possibly big difference.
 

aerohost

Member
Licensed User
Hi Andrew, ok, that works perfectly now.

You said: "I'm surprised it does display the OK message in the testbox! Does it?"

The 'ok' seemed to work, but the 'not ok' didn't, but it's all fixed now.

Just so you have the bigger picture, this problem of 'no internet, program jams up' didn't appear during my lab testing because it was always connecting.

It wasn't until I started driving around in a marginal area with my mobile moving in and out of 'no service' that I realized my program wouldn't work because of the halt on any other lookup activities (with data already on the phone) during timeouts.

It's the mobile front end for a larger database application:

Access Database Demonstration Online - SQL Alternative

One final question, if you will: I don't understand what these routines are about:

Demonstrate locking a resource
Resource locking routines

I'm sure I will over time, but for now I want to distill this down to the minimal amount of code I actually need for this particular purpose. So, do I actually need those items above?

Thanks again for all your patience, and for saving the day with your threads!

Regards, Adrian
 

agraham

Expert
Licensed User
Longtime User
I don't understand what these routines are about
They are useful when two threads need to update or use the same resource such as a global variable or a Serial Port or some other resource that only supports a single method of access. The "Thread safety" section in the "Thread pitfalls" topic of the help tries to explain this.

You probably don't need them for the time being but if you get, for example, variables set to unexpected values or apparently not being set then see if that variable is being used in both the main program and a thread, or in two threads. If it is then consider the result of both threads trying to use it at once (one reading, one writing is usually but not always OK). In that case you may need to lock the variable before using it and unlock it after.
 
Top