Android Code Snippet [B4X] Wait for multiple tasks to complete

A pattern such as this one can be used when you want to run multiple resumable subs concurrently and wait for all of them to complete before continuing:
B4X:
    Dim status(1) As Int
    Test1(status)
    Test2(status)
    Test3(status)
    Do While status(0) < 3
        Sleep(50)
    Loop
    Log("all tasks completed. Continue with your next task...")

Private Sub Test1 (Status() As Int)
    Sleep(Rnd(1, 1000))
    Log("Test1 completed")
    Status(0) = Status(0) + 1
End Sub

Private Sub Test2 (Status() As Int)
    Sleep(Rnd(1, 1000))
    Log("Test2 completed")
    Status(0) = Status(0) + 1
End Sub

Private Sub Test3 (Status() As Int)
    Sleep(Rnd(1, 1000))
    Log("Test3 completed")
    Status(0) = Status(0) + 1
End Sub
For example, you might want to download multiple resources and process them together.
Note that it doesn't need to be three different subs. You can call the same sub multiple times. Just make sure to set the counter (3 in the example) to match the number of calls.
And don't worry about this loop. It is not a busy loop. It is similar to using a timer to poll something.
 

Alessandro71

Well-Known Member
Licensed User
Longtime User
Good to know, but this is a hack šŸ˜
The resumable sub needs to be written specifically with the purpose of being used this way.
A language construct (Wait For x,y,z?) will be a more elegant solution.
Note: This is not a complain, but a constructive criticism wannabe :)
 

LucaMs

Expert
Licensed User
Longtime User
I don't agree with a single statement you made but I'm not going to argue about it
The resumable sub needs to be written specifically with the purpose of being used this way.
I assume he meant to refer to the class-level array, which must be incremented within Subs even though it is not essential to the purpose of the Sub itself.

This is the more general problem with class-level variables, however, but sometimes they are needed.
 

emexes

Expert
Licensed User
run multiple resumable subs concurrently
B4X:
Private Sub Test1 (Status() As Int)
    Status(0) = Status(0) + 1

Private Sub Test2 (Status() As Int)
    Status(0) = Status(0) + 1

Private Sub Test3 (Status() As Int)
    Status(0) = Status(0) + 1

Is there any risk of multiple Status(0) = Status(0) + 1 lines also running concurrently and one Sub overwriting another Sub's Status(0) update so that Status(0) never gets to 3?
 

Magma

Expert
Licensed User
Longtime User
Is there any risk of multiple Status(0) = Status(0) + 1 lines also running concurrently and one Sub overwriting another Sub's Status(0) update so that Status(0) never gets to 3?
Hi emexes..
I hope having risk..

Only that way we ve know something goes wrong..

We can use timer.. to time out... and msg the user for any prob..
 

aeric

Expert
Licensed User
Longtime User
I thought the value of status (lowercase) is only passed to the Test function in ONE-way to the Status (starts with capital S) but it seems it can return to the outside scope or calling code (by Reference ?) in TWO-ways
 

Magma

Expert
Licensed User
Longtime User
No. It cannot happen. The resumable subs run on the main thread like all other subs.
but if for example downloading... a file at "Test2" sub and not completed for a reason... and the "Status(0) = Status(0) + 1" is when all downloaded ok.. then will never end the loop.. right?
 

aeric

Expert
Licensed User
Longtime User
To visualize I add more Logs.

B4X:
Sub Button1_Click
    Dim status(1) As Int
    Test1(status)
    Test2(status)
    Test3(status)
    Do While status(0) < 3
        Log("sleeping...")
        Sleep(50)
    Loop
    Log("all tasks completed. Continue with your next task...")
End Sub

Private Sub Test1 (Status() As Int)
    Log("Test1 called")
    Sleep(Rnd(1, 1000))
    Log("Test1 completed")
    Log("before: " & Status(0))
    Status(0) = Status(0) + 1
    Log("after: " & Status(0))
    Log("Test1 exit")
End Sub

Private Sub Test2 (Status() As Int)
    Log("Test2 called")
    Sleep(Rnd(1, 1000))
    Log("Test2 completed")
    Log("before: " & Status(0))
    Status(0) = Status(0) + 1
    Log("after: " & Status(0))
    Log("Test2 exit")
End Sub

Private Sub Test3 (Status() As Int)
    Log("Test3 called")
    Sleep(Rnd(1, 1000))
    Log("Test3 completed")
    Log("before: " & Status(0))
    Status(0) = Status(0) + 1
    Log("after: " & Status(0))
    Log("Test3 exit")
End Sub

Results:
B4X:
*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create (first time) **
** Activity (main) Resume **
Test1 called
Test2 called
Test3 called
sleeping...
Test2 completed
before: 0
after: 1
Test2 exit
sleeping...
sleeping...
Test1 completed
before: 1
after: 2
Test1 exit
sleeping...
sleeping...
sleeping...
sleeping...
Test3 completed
before: 2
after: 3
Test3 exit
all tasks completed. Continue with your next task...
 

kimstudio

Active Member
Licensed User
Longtime User
Why array is used? What's wrong with
B4X:
Dim status as int

It is possible to
B4X:
Dim status as int
as a global variable, then modify the test sub as below to make it work:
B4X:
Private Sub Test1
    Sleep(Rnd(1, 1000))
    Log("Test1 completed")
    status  = status + 1
End Sub

It is crucial to understand the difference between pass by value and pass by reference for sub parameters.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Making it a global variable is a very bad solution. It means that any other call to these subs will break your program.
And there are exactly zero benefits for this. Don't kill an elegant solution...

And if you are worried about the creation of a single element array then simply don't. It will take exactly 0.000000000000001ms to create the array.
 

Chris2

Active Member
Licensed User
Correct me if I'm wrong, but this is similar to @Erel's "prefered timeout" (see here) which I spotted a while back and adopted :) - much more elegant then stop/starting a timer which I was doing before.

Why array is used? What's wrong with
B4X:
Dim status as int
I think it's because an array will be passed between the subs by reference, not it's value.
So by creating a new array where ever you're calling the resumable subs, that specific array will be altered in the line
Status(0) = Status(0) + 1

If you used (global)...
B4X:
Dim status as int
... then calling the resumable subs from anywhere would increase the value of this same variable. So, for example calling Test1 three times would increase status to 3 before Test2 & Test3 have completed.

Using the locally declared array means each call uses its own, specific 'status' count.
 
Top