Java-Gaming.org Hi !
Featured games (91)
games approved by the League of Dukes
Games in Showcase (804)
Games in Android Showcase (239)
games submitted by our members
Games in WIP (868)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  [SOLVED] BufferedReader.ReadLine unexpectedly blocking  (Read 33476 times)
0 Members and 1 Guest are viewing this topic.
Offline moogie

JGO Ninja


Medals: 16
Projects: 6
Exp: 10 years


Java games rock!


« Posted 2016-03-04 06:02:21 »

It has been a while since I have had to ask upon the experts here, but I think i must be missing something very simple in understanding in what situation ReadLine() is blocking.

I have a controller that launches external classes (other jvm instances) and communicate via std in and out.

These external classes (processes) are not part of the project and are provided without source code.

For easy of development, i have implemented an option where, instead of an external processes, threads for each "process" are created and the Main method of each "process" is called. Since the "processes" expect communication via stdin/out I have swapped out the System.in and System.out streams with custom streams that instead uses sockets to pretend to be std in/out for each thread.

My problem is that whilst the controller can and does receive input from the child process, the child process "hangs" on the ReadLine() call indefinitely. If i do not use a BufferedReader, and instead simply use InputStream.read() then there is no blocking and the message is successfully received by the process.

*Note* that the  "threading" of the "external" processes does not have any effect... the same problem exists if the "process" is run in a seperate JVM.

Here is a cut down example to demonstrate:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.List;


public class BlockingInputStreamReaderTest {
   public static void main(String[] args) throws Exception
   {
      // use custom std streams to allow per thread stream redirection
      StandardStreamManager.stdIn=System.in;
      StandardStreamManager.stdOut=System.out;
      System.setIn(StandardStreamManager.getInputStreamRedirector());
      System.setOut(new PrintStream(StandardStreamManager.getOutputStreamRedirector()));
     
     
      final int port = 9876;
      final List<String> command = Arrays.asList("java TestPlayer".split(" "));
         Thread thread = new Thread(new Runnable()
         {
           
         @Override
         public void run() {
            ServerSocket ss=null;
            try
            {
               ss = new ServerSocket(port);
               Socket socket = ss.accept();
               StandardStreamManager.register(socket.getInputStream(),socket.getOutputStream());

               Class playerClass;
               playerClass = getClass().getClassLoader().loadClass(command.get(1));
                  String[] commands = command.subList(2, command.size()).toArray(new String[0]);
                  Method method = playerClass.getMethod("main", new String[commands.length].getClass());
                  Object[] args = new Object[1];
                  args[0] = commands;
                  method.invoke(null,args);
            } catch (Exception e)
            {
               e.printStackTrace();
            }
            finally
            {
               if (ss!=null)
               try {
                  ss.close();
               } catch (IOException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
               }
            }
         }
      });
         thread.start();
         
         PrintStream ps = null;
         BufferedReader br = null;
         int retries=0;
         while (retries++<200)
         {
            try
            {
               Socket socket = new Socket("127.0.0.1", port);
               ps = new PrintStream( socket.getOutputStream());
               br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
               break;
            } catch (IOException e)
            {
               try {
               Thread.sleep(10);
            } catch (InterruptedException e1) {
               // TODO Auto-generated catch block
               e1.printStackTrace();
            }
            }
         }
         
         System.out.println("CONTROLLER INPUT RECEIVED: "+br.readLine());
         System.out.println("CONTROLLER INPUT RECEIVED: "+br.readLine());
         
         // write to player...
         while(true)
         {
            ps.println("You can do it!");
            Thread.sleep(500);
         }
   }
}


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.HashMap;

public class StandardStreamManager
{
   static public PrintStream stdOut;
   static public InputStream stdIn;
   
   
   
   static private InputStream getInputStream()
   {
      Streams streams = registry.get(Thread.currentThread());
      if (streams == null)
      {
         return stdIn;
      }
      else
      {
         return streams.is;
      }
   }
   
   static private OutputStream getOutputStream()
   {
      Streams streams = registry.get(Thread.currentThread());
      if (streams == null)
      {
         return stdOut;
      }
      else
      {
         return streams.os;
      }
   }
   
   static private class Streams
   {
      public Streams(InputStream inputStreamRedirectWorker,
            OutputStream outputStreamRedirectWorker) {
         this.is=inputStreamRedirectWorker;
         this.os=outputStreamRedirectWorker;
      }
      InputStream is;
      OutputStream os;
   }
   static HashMap<Thread, Streams> registry = new HashMap<>();
   
   static public void register(InputStream is, OutputStream os)
   {
      registry.put(Thread.currentThread(), new Streams(is,os));
   }

   public static InputStream getInputStreamRedirector() {

      return new InputStream() {
         
         @Override
         public int read() throws IOException {
            InputStream is = getInputStream();
            return is.read();
         }
      };
   }

   public static OutputStream getOutputStreamRedirector() {

      return new OutputStream() {
         
         @Override
         public void write(int data) throws IOException {
            getOutputStream().write(data);
         }
      };
   }
   
   
}


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

public class TestPlayer {

   public static void main(String[] args) throws Exception
   {
      Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
         
         @Override
         public void run() {
            System.err.println("TEST PLAYER SHUTDOWN!");
         }
      }));
     
      System.err.println("TEST PLAYER START!");

      InputStream in = System.in;

      // send something to the controller
      System.out.println("12345,E\n45678,F");
     
      BufferedReader br = new BufferedReader(new InputStreamReader(in));
      while (true)
      {

         String line = br.readLine();
         System.err.println("TestPlayer INPUT RECEIVED: "+line);
      }

//      String line="";
//      while (true)
//      {
//         line+=(char) in.read();
//         if (line.endsWith("\n"))
//         {
//            System.err.print("TestPlayer INPUT RECEIVED: "+line);
//            line="";
//         }
//      }
   }

}


To test that the messages are being sent correctly, uncomment the commented section in TestPlayer and comment the code block above it using the BufferedReader.

Can someone give some suggestions on why BufferedReader would be blocking? Thanks

Java4k RIP 2014
Offline basil_

« JGO Bitwise Duke »


Medals: 418
Exp: 13 years



« Reply #1 - Posted 2016-03-04 07:19:26 »

System.in
.read()
itself is blocking, not the bufferedReader.

one way to get around it is to use a dedicated thread, as you do already - and utilize
available()
:

only issue
in.read()
if
in.available() > 0
otherwise wait and try again.

Offline moogie

JGO Ninja


Medals: 16
Projects: 6
Exp: 10 years


Java games rock!


« Reply #2 - Posted 2016-03-04 09:16:19 »

System.in
.read()
itself is blocking, not the bufferedReader.

one way to get around it is to use a dedicated thread, as you do already - and utilize
available()
:

only issue
in.read()
if
in.available() > 0
otherwise wait and try again.



I am not sure that is the complete problem... I am happy (want) the blocking to occur until new line character is read from the underlying inputstream used by the BufferedReader. Its just that it seems like it never detects it on the one side. The other side of the socket (see line 83-84 of BlockingInputStreamReaderTest code above) happily reads a line from a BufferedReader encapsulated InputStream.

Java4k RIP 2014
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline moogie

JGO Ninja


Medals: 16
Projects: 6
Exp: 10 years


Java games rock!


« Reply #3 - Posted 2016-03-04 09:55:19 »

Ok, I have investigated further by looking at the source code of the JRE and have found my problem: The InputStreamReader uses StreamDecoder internally... the implementation of which populates a buffer of either 32 bytes or 8192 bytes from the InputStream before return results back to the InputStreamReader and ultimately to the BufferedReader. It seems that in one direction the 32 byte buffer option is triggered and thus I see the results on that side. For the other side it must trigger the 8192 byte buffer and it blocks until that buffer is full. At which that side outputs the messages. (I found this out after accidentally leaving the test running for a while and coming back to see that it had worked!)

That is annoying for my purposes, but at least I now understand why it was behaving the way it did... I have no idea why it chose the smaller buffer on one side but the larger buffer on the otherside tho. Ah well... I will just have to role my own ReadLine routine.

Thanks for all that pondered on my question!

Java4k RIP 2014
Offline Hydroque

JGO Coder


Medals: 25
Exp: 5 years


I'm always inspiring a good time.


« Reply #4 - Posted 2016-03-04 15:58:54 »

Taking a quick look at this, you instantaenously call

Quote
ps = new PrintStream( socket.getOutputStream());
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));

When you create multiple objects trying to read from the stream, you get problems. Only ever create one printStream object for each connection (as you only need to output data one way), but as for reading, if you are reading images or bytes or strings, you need to switch between and you should only read when you are looking for input, not processing.

You think I haven't been monitoring the chat? http://pastebin.java-gaming.org/c47d35366491fHere is a compilation <3
Offline moogie

JGO Ninja


Medals: 16
Projects: 6
Exp: 10 years


Java games rock!


« Reply #5 - Posted 2016-03-04 21:15:22 »

Thanks for spotting that issue.

That is not what caused the original problem (that side of the socket correctly worked Smiley ) but could be an issue in the future

Java4k RIP 2014
Offline HeroesGraveDev

JGO Kernel


Medals: 383
Projects: 11
Exp: 4 years


┬─┬ノ(ಠ_ಠノ)(╯°□°)╯︵ ┻━┻


« Reply #6 - Posted 2016-03-05 07:32:27 »

Taking a quick look at this, you instantaenously call

Quote
ps = new PrintStream( socket.getOutputStream());
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));

When you create multiple objects trying to read from the stream, you get problems. Only ever create one printStream object for each connection (as you only need to output data one way), but as for reading, if you are reading images or bytes or strings, you need to switch between and you should only read when you are looking for input, not processing.

Er... unless you're talking about something else that I missed, one of those is an input stream and the other is an output. Only one object will be reading.

Offline Hydroque

JGO Coder


Medals: 25
Exp: 5 years


I'm always inspiring a good time.


« Reply #7 - Posted 2016-03-05 23:47:01 »

You picked and chose what you read from my post.


You think I haven't been monitoring the chat? http://pastebin.java-gaming.org/c47d35366491fHere is a compilation <3
Offline ra4king

JGO Kernel


Medals: 508
Projects: 3
Exp: 5 years


I'm the King!


« Reply #8 - Posted 2016-03-06 04:52:53 »

No HeroesGraveDev is correct, your advice is incorrect. He created a single object that reads from the InputStream and a single object that writes to the OutputStream. He is only every creating on stream wrapper per Input/Output stream.

Offline ShadedVertex
« Reply #9 - Posted 2016-03-06 05:01:15 »

Before you call the readLine() method, you should first check if the BufferedReader is ready.

1  
2  
3  
4  
5  
if(buff_reader.ready()){

    String input = buff_reader.readLine();

}


Just call the ready() method before you read the input.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Hydroque

JGO Coder


Medals: 25
Exp: 5 years


I'm always inspiring a good time.


« Reply #10 - Posted 2016-03-06 05:03:55 »

No HeroesGraveDev is correct, your advice is incorrect. He created a single object that reads from the InputStream and a single object that writes to the OutputStream. He is only every creating on stream wrapper per Input/Output stream.

I said that  because he statically made ONE stream for ONE purpose and no way to disconnect and change them. He is going to run into a problem with this method, esp because he only reads strings. If he wants to read images via ImageIO or something, he will have trouble.

You think I haven't been monitoring the chat? http://pastebin.java-gaming.org/c47d35366491fHere is a compilation <3
Offline ShadedVertex
« Reply #11 - Posted 2016-03-06 05:05:37 »

No HeroesGraveDev is correct, your advice is incorrect. He created a single object that reads from the InputStream and a single object that writes to the OutputStream. He is only every creating on stream wrapper per Input/Output stream.

I said that  because he statically made ONE stream for ONE purpose and no way to disconnect and change them. He is going to run into a problem with this method, esp because he only reads strings. If he wants to read images via ImageIO or something, he will have trouble.

If he wants to read objects in general, he could just use ObjectInputStream. But for that, the objects have to implement Serializable.
Offline moogie

JGO Ninja


Medals: 16
Projects: 6
Exp: 10 years


Java games rock!


« Reply #12 - Posted 2016-03-06 05:26:42 »

Thanks for the best practises advice. It always helps to get a reminder Smiley

These classes listed here are purely for the purposes of testing my original issue of (in my point of view) unexpected blocking on ReadLine() call and are not reflective of the quality of the final code Smiley.

Ultimately this will be a submission to create a King-of-the-Hill code golf question (similar to that of AlienWar ) where people will submit entries ( bots ) that will play a Tower-Defence game in a tournament to find the strongest bot.

Since submissions can be from any language, all communication will be performed via std in/out using only the lower 7bits of ASCII with integers and foating point numbers sent as text strings. Thus i will not need to be sending any binary (e.g. images etc)

Thanks for all your interest in my issue.

Java4k RIP 2014
Offline Hydroque

JGO Coder


Medals: 25
Exp: 5 years


I'm always inspiring a good time.


« Reply #13 - Posted 2016-03-06 05:37:58 »

1  
2  
3  
4  
5  
6  
7  
      BufferedReader br = new BufferedReader(new InputStreamReader(in));
      while (true)
      {

         String line = br.readLine();
         System.err.println("TestPlayer INPUT RECEIVED: "+line);
      }


1  
2  
3  
4  
5  
6  
      BufferedReader br = new BufferedReader(new InputStreamReader(in));
      String line;
      while ((line = in.readLine()) != null)
      {
         System.err.println("TestPlayer INPUT RECEIVED: "+line);
      }


Does switching to this style somehow fix your issue. I am unable to troubleshoot your issue really.

You think I haven't been monitoring the chat? http://pastebin.java-gaming.org/c47d35366491fHere is a compilation <3
Offline moogie

JGO Ninja


Medals: 16
Projects: 6
Exp: 10 years


Java games rock!


« Reply #14 - Posted 2016-03-06 06:00:26 »

Does switching to this style somehow fix your issue. I am unable to troubleshoot your issue really.

I have identified my problem (see earlier post is due the the underlying implementation of classes used by InputStreamReader. )

So unfortunately, your suggested change has no effect as it does not change the use of InputStreamReader.

I have accepted that I will have to roll-my-own Readline to achieve immediate response to messages, it is not difficult or risky to do so for my stated purpose and will most likely be as simple as below:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
public String readLine()
{
   StringBuilder sb = new StringBuilder();
   int readByte = in.read();
   while (readByte>-1 && readByte!= '\n')
   {
      sb.append((char) readByte);
      readByte = in.read();
   }
   return sb.length()==0?null:sb.toString();
}


Java4k RIP 2014
Offline Riven
Administrator

« JGO Overlord »


Medals: 1371
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #15 - Posted 2016-03-06 11:00:42 »

Your latest impl interprets empty lines exactly the same as EOF. You might want to change that Pointing

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings!
Offline ags1

JGO Kernel


Medals: 367
Projects: 7


Make code not war!


« Reply #16 - Posted 2016-03-06 19:51:36 »

Riven, it's like you were bitten by a radioactive debugger in a comp sci lab!

Offline moogie

JGO Ninja


Medals: 16
Projects: 6
Exp: 10 years


Java games rock!


« Reply #17 - Posted 2016-03-06 22:13:57 »

Your latest impl interprets empty lines exactly the same as EOF. You might want to change that Pointing

heh thats what comes from just typing into a post with out really testing or thinking too long on the response. cheers for the heads up

Java4k RIP 2014
Offline Hydroque

JGO Coder


Medals: 25
Exp: 5 years


I'm always inspiring a good time.


« Reply #18 - Posted 2016-03-06 23:13:35 »

Spiderriven spiderriven! Does whatever a spiderriven does.

Does closing your program after you sent data print any data out? Because it could of been a flush issue. But like I said I haven't popped it into eclipse and actually had a go at it.

You think I haven't been monitoring the chat? http://pastebin.java-gaming.org/c47d35366491fHere is a compilation <3
Offline moogie

JGO Ninja


Medals: 16
Projects: 6
Exp: 10 years


Java games rock!


« Reply #19 - Posted 2016-03-06 23:51:34 »

Does closing your program after you sent data print any data out? Because it could of been a flush issue. But like I said I haven't popped it into eclipse and actually had a go at it.

While i have not performed that particular test, I would imagine it would not have any effect (unless i close the socket) as the StreamDecoder is still waiting to fill its buffer before continuing. But as i have said, the problem is now understood and I have a solution.

Thanks

Java4k RIP 2014
Pages: [1]
  ignore  |  Print  
 
 

 
Riven (579 views)
2019-09-04 15:33:17

hadezbladez (5501 views)
2018-11-16 13:46:03

hadezbladez (2398 views)
2018-11-16 13:41:33

hadezbladez (5761 views)
2018-11-16 13:35:35

hadezbladez (1220 views)
2018-11-16 13:32:03

EgonOlsen (4660 views)
2018-06-10 19:43:48

EgonOlsen (5681 views)
2018-06-10 19:43:44

EgonOlsen (3196 views)
2018-06-10 19:43:20

DesertCoockie (4094 views)
2018-05-13 18:23:11

nelsongames (5113 views)
2018-04-24 18:15:36
A NON-ideal modular configuration for Eclipse with JavaFX
by philfrei
2019-12-19 19:35:12

Java Gaming Resources
by philfrei
2019-05-14 16:15:13

Deployment and Packaging
by philfrei
2019-05-08 15:15:36

Deployment and Packaging
by philfrei
2019-05-08 15:13:34

Deployment and Packaging
by philfrei
2019-02-17 20:25:53

Deployment and Packaging
by mudlee
2018-08-22 18:09:50

Java Gaming Resources
by gouessej
2018-08-22 08:19:41

Deployment and Packaging
by gouessej
2018-08-22 08:04:08
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!