Confused about callable

Im confused about how to do the multithreading.

My voice recognition thread takes a while to load, but so do the app states, sometimes it finished before the app states are properly setup.

I need the voice thread to only listen once the states are setup properly.
But i want to start it quite early to give it the time it needs to setup itself.

What ive tried clearly doesnt work because the thread.start returns void.
What i really want to do is wait on the main thread until its finished before voicethread starts.

		  /* This constructor creates a new executor with a core pool size of 4. */
		  executor = new ScheduledThreadPoolExecutor(4);
		  this.initialiseVoiceRecognition();
		// sleep main thread 10 seconds whilst the voice recognition gets ready
			try {
				Thread.sleep(10000);
//				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		  this.initialiseAppStates();
		  this.initSimulation();
		  this.initRouter();

                // ok once these are done its ok to listen.


	}

	private void initialiseVoiceRecognition() {
		// TODO Auto-generated method stub
		vcm = new VoiceCommandManager(app);
		voiceThread = new Thread(vcm);
		 // run as deamon to terminate this thread when app terminates.
		voiceThread.setDaemon(true);
		
	    enqueue(new Callable() {
	        public Object call() throws Exception {
	            return voiceThread.start();
	        }
	    });
		Future future = executor.submit(voiceThread.start());

//		voiceThread.start(); 
	}

Your code is very confused. For some reason it’s telling the exuector to start the voice thread but also enqueing a callable for JME to do it on its own thread. Very strange.

Start the thread… submit something to the executor or whatever.

When that has set itself up have IT enqueue a callable with JME that will start your app states that require the voice recognition.

Don’t sleep 10 seconds as that’s sloppy and buggy and hacky-ugly. It’s also wrong.

so i tried this, but voiceThread.start() is not getting called:

private void initialiseVoiceRecognition() {
	// TODO Auto-generated method stub
	vcm = new VoiceCommandManager(app); //construct and setup VM (long process)
	voiceThread = new Thread(vcm);
	 // run as deamon to terminate this thread when app terminates.
	voiceThread.setDaemon(true);
	
    Callable completedInitialisation = new Callable(){
        public Object call() throws Exception {
            return completeInit();
        }
    };
    
	Future future = executor.submit(completedInitialisation); // complete setup or states, router...
	if(future.isDone()){
		voiceThread.start(); // starts recogniser listening
	}
}

public boolean completeInit(){
	  this.initialiseAppStates();
	  this.initSimulation();
	  this.initRouter();
	  return true;
}

future.isDone does not wait until stuff is ready, it just returns false if it is not.

1 Like

@jsky, do what @pspeed suggested. Create a new Thread inside which you initialize the voice recognition. In the same thread, after your VR is done setting up, enqueue another task (i.e. loading app states). More here: http://wiki.jmonkeyengine.org/doku.php/jme3:advanced:multithreading

Before you start the thread, it would be wise to render at least some ‘Loading…’ text for the user. A some sort of ‘pattern’ for that can be found here: http://wiki.jmonkeyengine.org/doku.php/jme3:advanced:loading_screen

1 Like

And terrible and utterly not advised. It’s also very bad.

1 Like

Read about synchronization.

What you need is to tell the main thread that some part of second thread (the loading) is done. The main thread should wait on the semaphore.

java.util.concurrent.Semaphore s = new java.util.concurrent.Semaphore(0);

public setDone() {
    s.release();
}

public waitUntilDone() {
     s.aquire();
}

I’m going to unroll your code for you so you can see what is happening… I’m going to use a metaphor to explain it.

The metaphor:
-you want to send a messenger to a remote location to do some work.
-you don’t want to start telling him what to do until he gets there (by phone or whatever)

Your code with the method unrolled:

    // There are actually multiple 'messgeners' in this code so I will
    // name one Joe and the other Mary.  Joe is the one you actually want
    // to do the work and Mary is the one you didn't realize you have.

    // Start up four cars waiting for your messengers to go on their trips
    /* This constructor creates a new executor with a core pool size of 4. */
    executor = new ScheduledThreadPoolExecutor(4);
 
    // Create a new messenger "Joe" and give him the tools he will need to do his
    // job on site.  "Joe" will not be using any of the cars we created above
    // as we've given him his own car.   
    // this.initialiseVoiceRecognition(); expanded here
    // TODO Auto-generated method stub
    vcm = new VoiceCommandManager(app);
    voiceThread = new Thread(vcm);
    // run as deamon to terminate this thread when app terminates.
    voiceThread.setDaemon(true);

    // Tell JME to call the messenger with work instructions as soon as
    // we are done here.		
    enqueue(new Callable() {
        public Object call() throws Exception {
            return voiceThread.start();
        }
    });
    
    // Put "Mary" in a car and tell her to call "Joe" to drive to the job site
    Future future = executor.submit(voiceThread.start());    
 
    // We've now setup to tell "Joe" to drive to the job site two different ways.  Joe
    // is going to get confused and note that we haven't actually told Joe to go yet.
    // We've setup a thread (Mary) to do it and we've also told JME to do it right after
    // we're done here.
 
    // So now we wait 10 secons in the hopes that "Mary" was actually able to get to
    // her job site and call "Joe" to tell him to start going to his job site.  This
    // is probably true.  It's completely unknown whethere "Joe" will have a chance
    // to get to his job site in 10 seconds.  And it's silly because "Joe" could just
    // call us when he gets there.
    // Oh, and meanwhile, the entire company is completely paused just because of Joe.
    // People might get worried and call the fire department because if they looked in
    // the windows then everything is just frozen.    
    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    
    // Tell "Joe" to get started with his work.  He may or may not be at the job site
    // by now. 
    this.initialiseAppStates();
    this.initSimulation();
    this.initRouter();

Instead… let Joe call us when he gets there.

// Create the thread to initialize the VR stuff.  
Thread joe = new Thread(vcm);
joe.setDaemon(true);

// Modify the VioceCommandManager enqueue a callable when it is ready to accept commands.  This involves changing that class which we can't see.

// That callable is what initializes the app states and stuff.
// This would also be in that class we can't see.

Done.

1 Like

Thanks for walking me through that, it cleared up some misunderstandings.
I still dont totally get the Future stuff though, so ill re-read the part on that.
Im glad i didnt need the semaphore.

    public VoiceCommandManager(App app, Callable callToCompleteInit) throws Exception{
    	this.app = app;
    	this.callOnReady = callToCompleteInit; 
    	// setup configuration, acoustic model, dictionary, grammar
		jsgfRecognizer = new LiveSpeechRecognizer(configuration);
}
    public void run() {
	   this.callOnReady.call();
       // now start dialog.
	}

// In App

private void initialiseVoiceRecognition() throws Exception {
    
    		Callable callToCompleteInit = new Callable(){
    	        public Object call() throws Exception {
    	            return completeInit();
    	        }
    	    };
    	    
    		vcm = new VoiceCommandManager(app, callToCompleteInit);
    		voiceThread = new Thread(vcm);
    		voiceThread.setDaemon(true);
    		voiceThread.start(); // starts recogniser listening
    	}

HM in short, if you don’t get Future, try to avoid them as good as possible.
Nearly always another design choice (Callbacks) are better anyway if you want to create a non blocking environment.

Btw Semaphores are pretty reliable (as in you can properly debug their use ect.) , but are not often necessary as well.

1 Like