views:

115

answers:

4

otherThread.join( time ) appears to wait time in real milliseconds. I want to wait time in actual CPU time so that I can get consistent behavior within my application.

I've done a quick look at ThreadMXBean but that doesn't quite seem to have what I wanted ( it tells me the threads actual CPU time, but offers no convenient way to wait until some time has passed ) . A busy loop around a sleep() could work, but seems grossly inefficient.

I also thought about using another thread and waiting on a Condition, but I'm not sure how that would work. The main thread would do: myCondition.await() , where another thread that would toggle myCondition when otherThread had used time actual CPU time. Again, this seems complicated and would probably still require the controlling thread to have a busy loop.

Edit: I'm doing this for a grading script. This means that I need to have a way to timeout if the student is in an infinite loop and it needs to be fair. I've been using JUnit to run tests on students, but that has the same problem with timing out: if the same (inefficient) submission is run multiple times, it could possibly get different grades depending on what other jobs are running on the machine at the time (a real problem for group work).

But this is a problem with normal unit testing, too - by using clock time instead of CPU time JUnit gets inconsistent test results?

+2  A: 

I'd suggest running a single JVM test at a time, and using time (see man time for details) to determine actual CPU time used. As for interrupting it if it takes too long... You could probably accomplish that with a shell script, or just allow the process to run to completion and base grading on the actual time taken.

Jonathan
Use a wall-clock timer to kill jobs that take way too long (60 seconds, say), then use the output of time to decide of the job finished in time. You can check the output of time for killed jobs too, and if the CPU time was under the shorter threshold (eg because the machines was heavily loaded), discard the run and repeat it.
Tom Anderson
@Tom Yup, that's about how the ephemeral shell script that I referenced would be written. :-)
Jonathan
I can't run to completion because students can have bad code ( infinite loops ). I think I'm probably just going to have to not use Java / JUnit for the tester and use #{favorite_scripting_language} instead.
Alex Reece
@Jonathan: yeah, i just rephrased what you wrote, didn't i? Consider me a chorus!
Tom Anderson
+1  A: 

Not going to happen. A timed join() is based on wall clock time, which means a hardware timer can be set to provide an asynchronous interrupt x seconds from now if the thread hasn't already been exited. Since there's no way to know a priori how much CPU a thread will use and therefore no way to schedule an interrupt when some boundary is reached. CPU time is accounted for when a process yields its time slice voluntarily or by force, so there wouldn't be any way to hit some exact figure anyway.

The closest you'd be able to get is polling the CPU utilization every so often and invoking interrupt() on threads that have gone over the limit. (Study the semantics of that carefully, because interrupting a thread will not necessarily bring the thread to an immediate stop.) If all you care about is whether or not the thread has consumed more than x seconds of CPU time, late checks that get results like kx where k > 1 aren't going to matter. They're still greater than x, and that's enough to know your candidate went over the limit.

One thing you could do if you're on a Unix-y system is to run the entire assignment as a process and use ulimit to limit the amount of CPU it's allowed to some value with a reasonable about tacked on for JVM startup and program load.

Blrfl
Yeah, I was giving partial credit depending on how much they completed before timing out ( which is why CPU time was important ). But I guess I can't do that fairly.
Alex Reece
If they all get the same data set to process, run the entire exercise to completion, divide total CPU time by the number of "things" and grade based on time per thing.
Blrfl
+1  A: 

Think about what you're asking for: you want a thread to block (not use CPU time) until it has used a specified amount of CPU time. This doesn't make sense.

I suspect what you really want if for your thread to block until another thread has used a specified amount of CPU time. This is not easily provided, probably because it is so rarely useful and can lead you down paths to poorly behaving code.

Darron
One upvote, one down. Curious. :-)
Darron
+1  A: 

I think if you combine thread.join()/interrupt() strategy (for timing out bad code (infinite loops)) and testingThread calling back cpu time you get what you want, if I´m not missing something. The solution may follow an approach like:

public class GradingThread extends Thread {
    private Thread testThread;
    private long elapsedClockTime=-1;
    public GradingThread(TestingThread testThread){
        this.testThread = testThread;
        testThread.setGradingThread(this);
    }
    public void setElapsed(long elapsedClockTime){
        this.elapsedClockTime = elapsedClockTime;
    }
    public void run(){
        System.out.println("GradingThread ID="+Thread.currentThread().getId());
        try{
        testThread.start();
        testThread.join(5000);
        testThread.interrupt();
        }catch(Exception e){e.printStackTrace();}
        if(elapsedClockTime==-1){
            System.out.println("Student program timedout (more than 5000 clock seconds)");
        }else{
            System.out.println("Student program elapsed cpu time = "+(elapsedClockTime)/1000000+"ms");
        }
    }
    public static void main(String[] args) {
        (new GradingThread(new TestingThread())).start();
    }
}
public class TestingThread extends Thread {
    private GradingThread gradingThread;
    public void setGradingThread(GradingThread thread){
        gradingThread = thread;
    }
    public void run(){
        StudentProgram sp = new StudentProgram();
        System.out.println("TestingThrad ID="+Thread.currentThread().getId());
        long scpu = getCpuTime();
            sp.takeLessThan5WallSecondsToRun(); //try calling infiniteLoop() too.
        long ecpu = getCpuTime();
        gradingThread.setElapsed(ecpu - scpu);
    }
    /** Get CPU time in nanoseconds. */
    public long getCpuTime( ) {
        ThreadMXBean bean = ManagementFactory.getThreadMXBean( );
        if ( ! bean.isCurrentThreadCpuTimeSupported())
            return -1L;
        long time = bean.getThreadCpuTime(Thread.currentThread().getId());       
        return time;
    }
}//end of TestingThread.
class StudentProgram {
    public void infiniteLoop(){
        while(true);
    }
    public int takeLessThan5WallSecondsToRun(){
        int total=0;
        while(total < Integer.MAX_VALUE) total++;
        return total;   
    }
}//end of StudentProgram.
xboard
This almost works. The grade will have to be provided by the grading thread, which can only determine whether or not the student timed out and not whether or not the students code was correct.
Alex Reece