2

Say I'm using a HTTP requests library for downloading files. This library uses threads inside. Now, I want to wait on the main thread until other threads complete their execution.

All the other solutions that I found by googling only work if I have access to the Thread variables that were used in the library. But these are not accessible to me.

Here's what i'm using currently:

package smartzero.eightnoteight.testfirebase;

import com.firebase.client.AuthData;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.print("email: ");
        String email = in.nextLine();
        System.out.print("password: ");
        String password = in.nextLine();
        Firebase fb = new Firebase("https://nullform.firebaseio.com");
        fb.authWithPassword(email, password, new AuthResultHandler());
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static class AuthResultHandler implements Firebase.AuthResultHandler {
        @Override
        public void onAuthenticated(AuthData authData) {
            System.out.println("authentication successful");
            String uid = authData.getUid();
            new RunTests(uid);
        }

        @Override
        public void onAuthenticationError(FirebaseError firebaseError) {
            System.out.println("authentication failed.");
        }
    }
}

PS: i'm testing firebase using firebase-client-jvm on my pc.

bdhar
  • 21,619
  • 17
  • 70
  • 86
eightnoteight
  • 234
  • 2
  • 11
  • Is there a specific reason you cannot use events to control your program flow? I dont know Firebase, but it looks event driven. – Mario Dekena Mar 30 '16 at 12:30
  • i'm also using the events. but my problem is, how should i wait on those events to complete in the main thread. – eightnoteight Mar 30 '16 at 12:44
  • You really dont have to wait in the main thread. Once you start with authWithPassword, you dont need your main method anymore. You can control your program through events. – Mario Dekena Mar 30 '16 at 12:51
  • If you think you really have to wait, can you give us a bigger picture of what you are trying to do? Right now there is no point in waiting in the main. – Mario Dekena Mar 30 '16 at 12:55
  • Where are your threads created? – Raedwald Mar 30 '16 at 13:07
  • You really do need to handle this yourself since the Firebase client won't. I wrote more about it on the answer. – Caio Iglesias May 20 '16 at 12:11

3 Answers3

3

You should use the events provided by Firebase:

fb.authWithPassword(email, password, new AuthResultHandler(){
    @Override
    public void onAuthenticated(AuthData authData) {
        //do something if authentication successful
    }

    @Override
    public void onAuthenticationError(FirebaseError error) {
        //handle error
    }
});

You could, if you really want to wait in the main do this:

void main(String[] args) {
    boolean finished = false;
    fb.authWithPassword(email, password, new AuthResultHandler(){
        @Override
        public void onAuthenticated(AuthData authData) {
            finished = true;
        }
    });
    while (!finished){
        Thread.sleep(1);
    }
}

Thats more of a pseudocode. It doesnt catch the interrupted exception and blocks forever if there is an error (onAuthenticationError). Also i would not recommend this. Busy waiting is almost never a good idea.

Mario Dekena
  • 843
  • 6
  • 20
  • i don't understand on how i'm not using event provided by the Firebase, my AuthResultHandler class implements Firebase.AuthResultHandler. anyway i used your finished variable approach and it is working fine and thanks. but how would one solve this kind of problem generally if there are more threads involved in the libraries that we are using. – eightnoteight Mar 30 '16 at 12:59
  • Am i right that you want to do something after the authentication is finished? You can write your code, directly into the event function. If you are new to programming it might be that you are really focused on your main method. But every other method (like onAuthenticated) is fine as well. – Mario Dekena Mar 30 '16 at 13:02
  • @mr.eightnoteight It would help to know what you want to write in your main method after youve waited. I could give a more specific example on how one would do it the right way. Even though my seccond approach works for you, i can only insist that you dont use it. – Mario Dekena Mar 30 '16 at 13:13
  • after i wait i will exit. most of my code runs in RunTests class, currently it just has a print statement, but if i have to use more of the Firebase methods this approach of keeping a finished variable is not working. – eightnoteight Mar 30 '16 at 13:19
  • You dont have to wait for all threads to finish. Your program keeps running until all threads are terminated. Even if your main method finished way earlier. – Mario Dekena Mar 30 '16 at 13:24
3

On non-Android runtimes the Firebase Java client uses daemon threads, which will not prevent a process from exiting. You must handle this using a CountdownLatch or a Semaphore.

CountdownLatch

A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

Your code on it:

package smartzero.eightnoteight.testfirebase;

import com.firebase.client.AuthData;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;

import java.util.Scanner;
import java.util.concurrent.CountDownLatch;


public class Main {

    public static void main(String[] args) throws InterruptedException {
        Scanner in = new Scanner(System.in);
        System.out.print("email: ");
        String email = in.nextLine();
        System.out.print("password: ");
        String password = in.nextLine();
        in.close();
        Firebase fb = new Firebase("https://nullform.firebaseio.com");
        CountDownLatch done = new CountDownLatch(1);
        fb.authWithPassword(email, password, new Firebase.AuthResultHandler(){

            @Override
            public void onAuthenticated(AuthData authData) {
                System.out.println("authentication successful");
                done.countDown();
            }

            @Override
            public void onAuthenticationError(FirebaseError error) {
                System.out.println("authentication failed.");
                done.countDown();
            }

        });
        done.await();
    }

}

Semaphore

It is used to control the number of concurrent threads that are using a resource. You could think of it as tickets to use a resource. You set the number of tickets available when you create it, and when acquire() is called with no tickets left, your process will wait for one to become available (on a release() call). On your code it is being created with zero "tickets" available:

package smartzero.eightnoteight.testfirebase;

import com.firebase.client.AuthData;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;

import java.util.Scanner;
import java.util.concurrent.Semaphore;


public class Main {

    public static void main(String[] args) throws InterruptedException {
        Scanner in = new Scanner(System.in);
        System.out.print("email: ");
        String email = in.nextLine();
        System.out.print("password: ");
        String password = in.nextLine();
        in.close();
        Firebase fb = new Firebase("https://nullform.firebaseio.com");
        Semaphore semaphore = new Semaphore(0);
        fb.authWithPassword(email, password, new Firebase.AuthResultHandler(){

            @Override
            public void onAuthenticated(AuthData authData) {
                System.out.println("authentication successful");
                semaphore.release();
            }

            @Override
            public void onAuthenticationError(FirebaseError error) {
                System.out.println("authentication failed.");
                semaphore.release();
            }

        });
        semaphore.acquire();
    }

}
Community
  • 1
  • 1
Caio Iglesias
  • 594
  • 9
  • 24
0

A CountdownLatch is really good for having one thread wait for one or more threads to complete one or more tasks before proceeding.

First, create the countdown latch with count of n, where n is the number events you want to wait on. Next, give the latch to the thread or threads doing the work. After that, the thread that should wait calls await() on the latch, and simultaneously the other threads begin working. When each of the worker threads is done, they call countdown() on the latch. When the latch counter hits zero, the waiting thread (or possibly threads) will unblock.

BillRobertson42
  • 12,602
  • 4
  • 40
  • 57