Link to home
Start Free TrialLog in
Avatar of benutzername
benutzername

asked on

timing out a method

hi,

I have a method, that gets a website as a String. Sometimes this method hangs, so i want to construct a timeout.
So if I call the method and it doesn't deliver the page within x seconds the program shall abort the download and continue its work.

this is a general problem, so an improved download method will help me, but won't fix the abstract problem itself...

So: how can I timeout a method-call ?

Ben Utzer

Avatar of Mick Barry
Mick Barry
Flag of Australia image

You can't really timeout a method.
Sounds like what you want to do is is instead implement a timeout on your comms. Have a look at setSoTimeout() method in Socket class.
Avatar of LexZEUS
LexZEUS

Apart from objects sugestion, maybe you can play with this code.. but I don't have idea this will work or not..

public class Something
{
...
  public void do readFromSocket()
  {
    TimerThread = new TimerThread(Thread.currentThread(),1000);
    try
      {
      mySocket.getInpuStream().read(buffer);
      }
    catch (Exception ex)
      {
      System.out.println("I/O error or unable to read from socket in 1 second");
      }
  }
...
}

public class TimerThread extends Thread
{
  Thread td;
  int waitTime;
  TimerThread(Thread t, int time)
  {
    td = t;
    waitTime = time;
    start();
  }

  public void run()
  {
  try {
      sleep(time);
      td.interrupt();
      }
  catch (Exception ex) { }
  }
}

Basically, the idea is to interrupt read(buffer) in 1000msec (1 sec).
Will this work? I dunno .. coz' I myself never try..

Alex
I don't think you can interrupt a blocked read so I don't think that'll work, but setting the socket timeout will have basically the same effect.
Avatar of benutzername

ASKER

interrupt only sets the interrupt flag - you can read this flag with isInterrupted() - so it doesn`t really interrupt anything. the socket timeout will help me for the special problem of the connection, but the abstract problem still exists: how to kill a thread ?

imagine, you have a multithreading program, that has to run for weeks without crashing. you want to build a supervisor that scans all running threads from time to time. if one of the threads crashed, the supervisor will delete the thread and start a new thread to replace the crashed one - thats what i have to work out.

it seems, that sometimes the garbage collection doesn't work like it should, because sometimes a thread is kicked out by java, because of low memory, even if there would be enough memory if java just did some more garbage collection :(
I found a soloution myself in between:

public class Test implements Runnable {

   public long lastActionPerformed;

   public Test() {
       start();
   }

   public void run() {
       while(true) {
           lastActionPerformed = System.currentMillis();
           // now do something
           // ....
       }
   }  
}


The thread t will be supervised by another thread. The other thread will check lastActionPerformed from time to time - if last action is too long ago (over 5000ms), it will delete the thread through Thread.join(whenToJoin) and construct a new one as replacement.

public class Supervisor implements Runnable {

   private Thread t;

   public static void main (String args[]) {
       Thread s = new Thread(new Supervisor());
   }

   public Supervisor() {
       t = new Thread(new Test());
       start();
   }

   public void run() {
       while(true) {          
           if ( System.currentMillis() - t.lastActionPerformed > 5000 ) {
                 t.join(1);
                 t = new Thread(new Test());      
           }
       }
   }  
}

...hope this will work, didn't check the syntax in java - but the concept will work.
> interrupt only sets the interrupt flag

In the general case, interrupt() will cause the thread to throw an InterruptedException so it can be used to break a thread.

> if one of the threads crashed

If a thread is crashed then it is no longer running.
You just need to start a new one.
Just call isAlive() if you want to know if a thread is still running.
Your example won't work. join() doesn't delete the thread, it just waits for it to finish.
The only way I can think of to 'timeout' a method is thru the use of interrupt().
API about join(long milliseconds): "Waits at most millis milliseconds for this thread to die."

What happens, if the thread doesn't die within the timerange of "milliseconds" ?

isAlive won't help me either - the thread is not "crashed" in the sense of "passed off" but "crashed" in the sense of "hanging in some point without continueing its work".

I find hanging threads by checking their lastActionPerformed variable.

It's a pitty, that sun didn't implement Thread.destroy() yet...
I will try it with a combination of interrupt() and setSoTimeout()...
> What happens, if the thread doesn't die within the timerange of "milliseconds" ?

Then the join method simply returns.

> "hanging in some point without continueing its work".

Threads hang for a variety of well defined reasons such as blocking I/O, sleep, wait etc.
Most of these can be awoken by an interrupt().

> I find hanging threads by checking their lastActionPerformed variable.

Yes, but then how do you stop them.

> It's a pitty, that sun didn't implement Thread.destroy() yet...

You can stop a thread, but it's prone to deadlocks and is not recomended.
>In the general case, interrupt() will cause the thread to throw an InterruptedException so it can be
used to break a thread.

Where do I have to put the try-catch to catch the InterruptedException ? I tried it everywhere, but I don't find out, where to catch it :(


best regards,

Ben Utzer
In the thread you are interrupting.

public void run()
{
   try
   {
      dostuff();
   }
   catch (InterruptedException ex)
   {
      // Someones interrupted me
   }
}
I thought interrupt() only interrupt when a thread in sleep() or wait() condition, it doesn't interrupt a thread which is not in one of above condition?
That is correct. I thought we were dealing with blocked threads.
If the thread is executing there is no (safe) way to stop it. If this is required then the thread should provide a mechanism for halting itself.
yes I agree.. The thread itself should implements some mechanism to exit from run() method for garbage collection.

....
public boolean forcedToStop = false;
public void run()
{
  while(!forcedToStop)
    {
    if (!forcedToStop) doSomethingPartOne();
    if (!forcedToStop) doSomethingPartTwo();
    if (!forcedToStop) doSomethingPartThree();
    if (!forcedToStop) doSomethingPartAndSoOn();

    // give another thread chance to execute
    if (!forcedToStop)
       try {sleep(100);} catch (Exception ex) { }
    }  
}
....

To stop the thread:

if (myThread.isAlive())
    {
    myThread.forcedToStop = true;
    myThread.interrupt();
    myThread=null; // for further garbage collection
    }

The only way to stop thread instantly (but not recommended) is using _yourThread_.stop() method...
i have no luck :( it seems that the exception is already catched within suns code, so it doesn't get down to my code and a can't catch and process it.

My classes are "Download" and "DownloadThread". Download starts a DownloadThread and gives it an URL. If DownloadThread hasn't completed after x seconds, it will be interrupted. But the exception is caught by something called "AWT blocker activation" ... whatever that is:

AWT blocker activation interrupted:
java.lang.InterruptedException
        at java.lang.Object.wait(Native Method)
        at java.lang.Object.wait(Object.java:426)
        at sun.awt.AWTAutoShutdown.activateBlockerThread(AWTAutoShutdown.java:309)
        at sun.awt.AWTAutoShutdown.notifyThreadBusy(AWTAutoShutdown.java:146)
        at java.awt.EventQueue.initDispatchThread(EventQueue.java:654)
        at java.awt.EventQueue.postEvent(EventQueue.java:209)
        at java.awt.EventQueue.postEventPrivate(EventQueue.java:190)
        at java.awt.EventQueue.postEvent(EventQueue.java:164)
        at java.awt.EventQueue.invokeLater(EventQueue.java:757)
        at javax.swing.SwingUtilities.invokeLater(SwingUtilities.java:1142)
        at javax.swing.text.StyleContext.reclaim(StyleContext.java:431)
        at javax.swing.text.StyleContext.addAttribute(StyleContext.java:274)
        at javax.swing.text.html.StyleSheet.addAttribute(StyleSheet.java:541)
        at javax.swing.text.StyleContext$NamedStyle.addAttribute(StyleContext.java:1490)
        at javax.swing.text.StyleContext.addStyle(StyleContext.java:89)
        at javax.swing.text.html.StyleSheet.addRule(StyleSheet.java:978)
        at javax.swing.text.html.StyleSheet$CssParser.endRule(StyleSheet.java:3097)
        at javax.swing.text.html.CSSParser.parseRuleSet(CSSParser.java:256)
        at javax.swing.text.html.CSSParser.getNextStatement(CSSParser.java:161)
        at javax.swing.text.html.CSSParser.parse(CSSParser.java:136)
        at javax.swing.text.html.StyleSheet$CssParser.parse(StyleSheet.java:3005)
        at javax.swing.text.html.StyleSheet.loadRules(StyleSheet.java:303)
        at javax.swing.text.html.HTMLEditorKit.getStyleSheet(HTMLEditorKit.java:358)
        at javax.swing.text.html.HTMLEditorKit.createDefaultDocument(HTMLEditorKit.java:185)
        at javax.swing.JEditorPane.setEditorKit(JEditorPane.java:945)
        at javax.swing.JEditorPane.setContentType(JEditorPane.java:871)
        at javax.swing.JEditorPane.getStream(JEditorPane.java:696)
        at nkUtil.www.DownloadThread.downloadWWWPage(DownloadThread.java:73)
        at nkUtil.www.DownloadThread.run(DownloadThread.java:46)
        at java.lang.Thread.run(Thread.java:536)
You might wonder, what JEditorPane has to to with my DownloadThread - it's like that:

I tried many different code samples for retrieving a web page over http, but most of them were refused by some servers, because they didn't know how to identify correctly.

JEditorPane is able to deliver an InputStream to download a HTML-Document and is not refused by any server, whoever.

Ben Utzer
This is the source, I'm struggling with:

import java.net.URL;

public class Download {

    /** Creates new Download */
    public Download() {
    }
   
    //--------------------------------------------------------------------------
    // Interface
   
    public String downloadToString(URL url, long timeout) {
        try {
            long startTime = System.currentTimeMillis();
            DownloadThread d = new DownloadThread(url);
            Thread t = new Thread(d);
            t.start();
            while(d.getResult() == null) {
                if ((System.currentTimeMillis() - startTime) > timeout) {
                    t.interrupt();
                    break;
                } else {
                    try{ wait(100);} catch(Exception e) {}                
                }
            }
            return d.getResult();
        } catch (Exception e) {
            System.out.println("downloadToStringException: " + e);
            return null;
        }
    }

}


---------------------------------------------------------

import java.net.*;
import java.io.*;
import javax.swing.JEditorPane;

class DownloadThread extends JEditorPane implements Runnable
{
   
    //--------------------------------------------------------------------------
    // Felder
   
    // read buffer size
    private static int READ_BLOCK_SIZE = 128;
    private char[] inputBuffer = new char[READ_BLOCK_SIZE];
    private int charsRead;    
   
    //
    private URL url;
    private String result = null;
   
    //--------------------------------------------------------------------------
    // Konstruktor
   
    public DownloadThread(URL url) {    
        this.url = url;
    }

    //--------------------------------------------------------------------------
    // runtime
   
    public void run() {
        result = downloadWWWPage(url);
    }    
   
    //--------------------------------------------------------------------------
    // Interface
   
    public String getResult() {
        return result;
    }

   
    //--------------------------------------------------------------------------
    // Download
   
    private String downloadWWWPage(URL url) {
        StringBuffer result = new StringBuffer();
        InputStreamReader in = new InputStreamReader(getStream(url));          
        while(true) {
            charsRead = in.read(inputBuffer, 0, READ_BLOCK_SIZE);
            if (charsRead == -1) {                    
                break;
            } else {
                result.append(inputBuffer, 0, charsRead);
            }                
        }
        // result
        return result.toString();
    }    
}

-----------------------------------------------------------

It's the best working web-document download routine I found yet, but it still has this interruption problem.

I'm building a spider, so I will need the best web-document download routine available, I gotta work hard on that issue.

Ben Utzer
ooopps, there was an error in the downloadWWWPage() - here is an update:
---------------------

    private String downloadWWWPage(URL url) {
        try {
            StringBuffer result = new StringBuffer();
            InputStreamReader in = new InputStreamReader(getStream(url));          
            while(true) {
                charsRead = in.read(inputBuffer, 0, READ_BLOCK_SIZE);
                if (charsRead == -1) {                    
                    break;
                } else {
                    result.append(inputBuffer, 0, charsRead);
                }                
            }
            // result
            return result.toString();
        } catch (Exception e) {
            return "";
        }            
    }    
The actual connection to the server appears to be being handled in a seperate thread ie. not the one your interrupting. I'd say the thread you are interrupting is in fact just waiting on the connection to complete.
Also worth mentioning that you cannot interrupt blocked I/O.
Hi benutzername,


Your problem is lying inside downloadwwwpage(url).


pass ur timeout value to this method. inside ur while validate ur timeout time. if u exceed the specified time come out of while loop, then ur download gets aborted.



Regards,
K.J.S.
sorry, that doesn't work, because sometimes it takes hours to receive one block (to finish one loop) - in what case I would like to interrupt and cancel the transfer.
hi benutzername,

now I'm totally lost.
when u r not sure of the length of the process why do u want to give timeout.

Pls., clear me where do u need timeout here and for what purpose. So, that I may help u out.


Regards,
K.J.S.
ok, I will describe my situation in detail:

I'm programming a webcrawler. I start with a given URL (let's say "http://directory.google.com/"), download the HTML-Document, search the HTML-Source for any hyperlinks and store all the links into a database.

Then I get the next URL from the database, download it, write all links into the database and so on...

It happens, that a URL doesn't work correctly, in which case my webcrawler "hangs". For example, some servers just stop sending data, so that the line

charsRead = in.read(inputBuffer, 0, READ_BLOCK_SIZE);
               
will take forever.

I need a VERY robust download algorhithm, that doesn't hang on bad servers. It should time out after let's say 30 seconds, just forget the current download and continue with the next URL.

If anyone needs my webcrawler code, I have no problem with sharing it, its an noncommercial research project. The crawler works very good and is quite elaborate now (except for the download thread...). So if somebody else is working on a crawler, too, we could exchange our code.
ASKER CERTIFIED SOLUTION
Avatar of Mick Barry
Mick Barry
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
that's what i needed ! thanks a lot for that tip :)