< prev index next >

src/java.base/share/classes/java/io/PrintStream.java

Print this page
*** 28,10 ***
--- 28,13 ---
  import java.util.Formatter;
  import java.util.Locale;
  import java.nio.charset.Charset;
  import java.nio.charset.IllegalCharsetNameException;
  import java.nio.charset.UnsupportedCharsetException;
+ import jdk.internal.access.JavaIOPrintStreamAccess;
+ import jdk.internal.access.SharedSecrets;
+ import jdk.internal.misc.InternalLock;
  
  /**
   * A {@code PrintStream} adds functionality to another output stream,
   * namely the ability to print representations of various data values
   * conveniently.  Two other features are provided as well.  Unlike other output

*** 62,10 ***
--- 65,12 ---
   */
  
  public class PrintStream extends FilterOutputStream
      implements Appendable, Closeable
  {
+     // initialized to null when PrintStream is sub-classed
+     private final InternalLock lock;
  
      private final boolean autoFlush;
      private boolean trouble = false;
      private Formatter formatter;
  

*** 108,10 ***
--- 113,17 ---
      private PrintStream(boolean autoFlush, OutputStream out) {
          super(out);
          this.autoFlush = autoFlush;
          this.charOut = new OutputStreamWriter(this);
          this.textOut = new BufferedWriter(charOut);
+ 
+         // use monitors when PrintStream is sub-classed
+         if (getClass() == PrintStream.class) {
+             lock = new InternalLock();
+         } else {
+             lock = null;
+         }
      }
  
      /* Variant of the private constructor so that the given charset name
       * can be verified before evaluating the OutputStream argument. Used
       * by constructors creating a FileOutputStream that also take a

*** 199,10 ***
--- 211,17 ---
      public PrintStream(OutputStream out, boolean autoFlush, Charset charset) {
          super(out);
          this.autoFlush = autoFlush;
          this.charOut = new OutputStreamWriter(this, charset);
          this.textOut = new BufferedWriter(charOut);
+ 
+         // use monitors when PrintStream is sub-classed
+         if (getClass() == PrintStream.class) {
+             lock = new InternalLock();
+         } else {
+             lock = null;
+         }
      }
  
      /**
       * Creates a new print stream, without automatic line flushing, with the
       * specified file name.  This convenience constructor creates

*** 418,20 ***
       *
       * @see        java.io.OutputStream#flush()
       */
      @Override
      public void flush() {
!         synchronized (this) {
              try {
!                 ensureOpen();
!                 out.flush();
              }
!             catch (IOException x) {
!                 trouble = true;
              }
          }
      }
  
      private boolean closing = false; /* To avoid recursive closing */
  
      /**
       * Closes the stream.  This is done by flushing the stream and then closing
--- 437,33 ---
       *
       * @see        java.io.OutputStream#flush()
       */
      @Override
      public void flush() {
!         if (lock != null) {
+             lock.lock();
              try {
!                 lockedFlush();
!             } finally {
+                 lock.unlock();
              }
!         } else {
!             synchronized (this) {
+                 lockedFlush();
              }
          }
      }
+     
+     private void lockedFlush() {
+         try {
+             ensureOpen();
+             out.flush();
+         }
+         catch (IOException x) {
+             trouble = true;
+         }
+     }
  
      private boolean closing = false; /* To avoid recursive closing */
  
      /**
       * Closes the stream.  This is done by flushing the stream and then closing

*** 439,24 ***
       *
       * @see        java.io.OutputStream#close()
       */
      @Override
      public void close() {
!         synchronized (this) {
!             if (! closing) {
!                 closing = true;
!                 try {
!                     textOut.close();
!                     out.close();
!                 }
!                 catch (IOException x) {
!                     trouble = true;
!                 }
!                 textOut = null;
!                 charOut = null;
!                 out = null;
              }
          }
      }
  
      /**
       * Flushes the stream and checks its error state. The internal error state
--- 471,37 ---
       *
       * @see        java.io.OutputStream#close()
       */
      @Override
      public void close() {
!         if (lock != null) {
!             lock.lock();
!             try {
!                 lockedClose();
!             } finally {
!                 lock.unlock();
!             }
!         } else {
!             synchronized (this) {
!                 lockedClose();
!             }
!         }
!     }
+     
+     private void lockedClose() {
+         if (!closing) {
+             closing = true;
+             try {
+                 textOut.close();
+                 out.close();
+             }
+             catch (IOException x) {
+                 trouble = true;
              }
+             textOut = null;
+             charOut = null;
+             out = null;
          }
      }
  
      /**
       * Flushes the stream and checks its error state. The internal error state

*** 530,24 ***
       * @see #println(char)
       */
      @Override
      public void write(int b) {
          try {
!             synchronized (this) {
!                 ensureOpen();
!                 out.write(b);
!                 if ((b == '\n') && autoFlush)
!                     out.flush();
              }
          }
          catch (InterruptedIOException x) {
              Thread.currentThread().interrupt();
          }
          catch (IOException x) {
              trouble = true;
          }
      }
  
      /**
       * Writes {@code len} bytes from the specified byte array starting at
       * offset {@code off} to this stream.  If automatic flushing is
       * enabled then the {@code flush} method will be invoked on the underlying
--- 575,37 ---
       * @see #println(char)
       */
      @Override
      public void write(int b) {
          try {
!             if (lock != null) {
!                 lock.lock();
!                 try {
!                     lockedWrite(b);
!                 } finally {
+                     lock.unlock();
+                 }
+             } else {
+                 synchronized (this) {
+                     lockedWrite(b);
+                 }
              }
          }
          catch (InterruptedIOException x) {
              Thread.currentThread().interrupt();
          }
          catch (IOException x) {
              trouble = true;
          }
      }
+     
+     private void lockedWrite(int b) throws IOException {
+         ensureOpen();
+         out.write(b);
+         if ((b == '\n') && autoFlush)
+             out.flush();
+     }
  
      /**
       * Writes {@code len} bytes from the specified byte array starting at
       * offset {@code off} to this stream.  If automatic flushing is
       * enabled then the {@code flush} method will be invoked on the underlying

*** 562,25 ***
       * @param  len   Number of bytes to write
       */
      @Override
      public void write(byte[] buf, int off, int len) {
          try {
!             synchronized (this) {
!                 ensureOpen();
!                 out.write(buf, off, len);
!                 if (autoFlush)
!                     out.flush();
              }
          }
          catch (InterruptedIOException x) {
              Thread.currentThread().interrupt();
          }
          catch (IOException x) {
              trouble = true;
          }
      }
  
      /**
       * Writes all bytes from the specified byte array to this stream. If
       * automatic flushing is enabled then the {@code flush} method will be
       * invoked on the underlying output stream.
       *
--- 620,39 ---
       * @param  len   Number of bytes to write
       */
      @Override
      public void write(byte[] buf, int off, int len) {
          try {
!             if (lock != null) {
!                 lock.lock();
!                 try {
!                     lockedWrite(buf, off, len);
!                 } finally {
+                     lock.unlock();
+                 }
+             } else {
+                 synchronized (this) {
+                     lockedWrite(buf, off, len);
+                 }
              }
          }
          catch (InterruptedIOException x) {
              Thread.currentThread().interrupt();
          }
          catch (IOException x) {
              trouble = true;
          }
      }
  
+     private void lockedWrite(byte[] buf, int off, int len) throws IOException {
+         ensureOpen();
+         out.write(buf, off, len);
+         if (autoFlush)
+             out.flush();
+     }
+     
+ 
      /**
       * Writes all bytes from the specified byte array to this stream. If
       * automatic flushing is enabled then the {@code flush} method will be
       * invoked on the underlying output stream.
       *

*** 643,116 ***
       * stream occur as promptly as with the original PrintStream.
       */
  
      private void write(char[] buf) {
          try {
!             synchronized (this) {
!                 ensureOpen();
!                 textOut.write(buf);
!                 textOut.flushBuffer();
!                 charOut.flushBuffer();
!                 if (autoFlush) {
!                     for (int i = 0; i < buf.length; i++)
!                         if (buf[i] == '\n') {
!                             out.flush();
!                             break;
-                         }
                  }
              }
          } catch (InterruptedIOException x) {
              Thread.currentThread().interrupt();
          } catch (IOException x) {
              trouble = true;
          }
      }
  
      // Used to optimize away back-to-back flushing and synchronization when
      // using println, but since subclasses could exist which depend on
      // observing a call to print followed by newLine() we only use this if
      // getClass() == PrintStream.class to avoid compatibility issues.
      private void writeln(char[] buf) {
          try {
!             synchronized (this) {
!                 ensureOpen();
!                 textOut.write(buf);
!                 textOut.newLine();
!                 textOut.flushBuffer();
!                 charOut.flushBuffer();
!                 if (autoFlush)
!                     out.flush();
              }
          }
          catch (InterruptedIOException x) {
              Thread.currentThread().interrupt();
          }
          catch (IOException x) {
              trouble = true;
          }
      }
  
      private void write(String s) {
          try {
!             synchronized (this) {
!                 ensureOpen();
!                 textOut.write(s);
!                 textOut.flushBuffer();
!                 charOut.flushBuffer();
!                 if (autoFlush && (s.indexOf('\n') >= 0))
!                     out.flush();
              }
          }
          catch (InterruptedIOException x) {
              Thread.currentThread().interrupt();
          }
          catch (IOException x) {
              trouble = true;
          }
      }
  
      // Used to optimize away back-to-back flushing and synchronization when
      // using println, but since subclasses could exist which depend on
      // observing a call to print followed by newLine we only use this if
      // getClass() == PrintStream.class to avoid compatibility issues.
      private void writeln(String s) {
          try {
!             synchronized (this) {
!                 ensureOpen();
!                 textOut.write(s);
!                 textOut.newLine();
!                 textOut.flushBuffer();
!                 charOut.flushBuffer();
!                 if (autoFlush)
!                     out.flush();
              }
          }
          catch (InterruptedIOException x) {
              Thread.currentThread().interrupt();
          }
          catch (IOException x) {
              trouble = true;
          }
      }
  
      private void newLine() {
          try {
!             synchronized (this) {
!                 ensureOpen();
!                 textOut.newLine();
!                 textOut.flushBuffer();
!                 charOut.flushBuffer();
!                 if (autoFlush)
!                     out.flush();
              }
          }
          catch (InterruptedIOException x) {
              Thread.currentThread().interrupt();
          }
          catch (IOException x) {
              trouble = true;
          }
      }
  
      /* Methods that do not terminate lines */
  
      /**
       * Prints a boolean value.  The string produced by {@link
       * java.lang.String#valueOf(boolean)} is translated into bytes
--- 715,181 ---
       * stream occur as promptly as with the original PrintStream.
       */
  
      private void write(char[] buf) {
          try {
!             if (lock != null) {
!                 lock.lock();
!                 try {
!                     lockedWrite(buf);
!                 } finally {
!                     lock.unlock();
!                 }
!             } else {
!                 synchronized (this) {
!                     lockedWrite(buf);
                  }
              }
          } catch (InterruptedIOException x) {
              Thread.currentThread().interrupt();
          } catch (IOException x) {
              trouble = true;
          }
      }
  
+     private void lockedWrite(char[] buf) throws IOException {
+         ensureOpen();
+         textOut.write(buf);
+         textOut.flushBuffer();
+         charOut.flushBuffer();
+         if (autoFlush) {
+             for (int i = 0; i < buf.length; i++)
+                 if (buf[i] == '\n') {
+                     out.flush();
+                     break;
+                 }
+         }
+     }
+ 
      // Used to optimize away back-to-back flushing and synchronization when
      // using println, but since subclasses could exist which depend on
      // observing a call to print followed by newLine() we only use this if
      // getClass() == PrintStream.class to avoid compatibility issues.
      private void writeln(char[] buf) {
          try {
!             if (lock != null) {
!                 lock.lock();
!                 try {
!                     lockedWriteln(buf);
!                 } finally {
!                     lock.unlock();
!                 }
!             } else {
+                 synchronized (this) {
+                     lockedWriteln(buf);
+                 }
              }
          }
          catch (InterruptedIOException x) {
              Thread.currentThread().interrupt();
          }
          catch (IOException x) {
              trouble = true;
          }
      }
  
+     private void lockedWriteln(char[] buf) throws IOException {
+         ensureOpen();
+         textOut.write(buf);
+         textOut.newLine();
+         textOut.flushBuffer();
+         charOut.flushBuffer();
+         if (autoFlush)
+             out.flush();
+     }
+ 
      private void write(String s) {
          try {
!             if (lock != null) {
!                 lock.lock();
!                 try {
!                     lockedWrite(s);
!                 } finally {
!                     lock.unlock();
!                 }
+             } else {
+                 synchronized (this) {
+                     lockedWrite(s);
+                 }
              }
          }
          catch (InterruptedIOException x) {
              Thread.currentThread().interrupt();
          }
          catch (IOException x) {
              trouble = true;
          }
      }
  
+     private void lockedWrite(String s) throws IOException {
+         ensureOpen();
+         textOut.write(s);
+         textOut.flushBuffer();
+         charOut.flushBuffer();
+         if (autoFlush && (s.indexOf('\n') >= 0))
+             out.flush();
+     }
+ 
      // Used to optimize away back-to-back flushing and synchronization when
      // using println, but since subclasses could exist which depend on
      // observing a call to print followed by newLine we only use this if
      // getClass() == PrintStream.class to avoid compatibility issues.
      private void writeln(String s) {
          try {
!             if (lock != null) {
!                 lock.lock();
!                 try {
!                     lockedWriteln(s);
!                 } finally {
!                     lock.unlock();
!                 }
!             } else {
+                 synchronized (this) {
+                     lockedWriteln(s);
+                 }
              }
          }
          catch (InterruptedIOException x) {
              Thread.currentThread().interrupt();
          }
          catch (IOException x) {
              trouble = true;
          }
      }
  
+     private void lockedWriteln(String s) throws IOException {
+         ensureOpen();
+         textOut.write(s);
+         textOut.newLine();
+         textOut.flushBuffer();
+         charOut.flushBuffer();
+         if (autoFlush)
+             out.flush();
+     }
+ 
      private void newLine() {
          try {
!             if (lock != null) {
!                 lock.lock();
!                 try {
!                     lockedNewLine();
!                 } finally {
!                     lock.unlock();
!                 }
+             } else {
+                 synchronized (this) {
+                     lockedNewLine();
+                 }
              }
          }
          catch (InterruptedIOException x) {
              Thread.currentThread().interrupt();
          }
          catch (IOException x) {
              trouble = true;
          }
      }
  
+     private void lockedNewLine() throws IOException {
+         ensureOpen();
+         textOut.newLine();
+         textOut.flushBuffer();
+         charOut.flushBuffer();
+         if (autoFlush)
+             out.flush();
+     }
+ 
      /* Methods that do not terminate lines */
  
      /**
       * Prints a boolean value.  The string produced by {@link
       * java.lang.String#valueOf(boolean)} is translated into bytes

*** 1206,27 ***
       *
       * @since  1.5
       */
      public PrintStream format(String format, Object ... args) {
          try {
!             synchronized (this) {
!                 ensureOpen();
!                 if ((formatter == null)
!                     || (formatter.locale() !=
!                         Locale.getDefault(Locale.Category.FORMAT)))
!                     formatter = new Formatter((Appendable) this);
!                 formatter.format(Locale.getDefault(Locale.Category.FORMAT),
!                                  format, args);
              }
          } catch (InterruptedIOException x) {
              Thread.currentThread().interrupt();
          } catch (IOException x) {
              trouble = true;
          }
          return this;
      }
  
      /**
       * Writes a formatted string to this output stream using the specified
       * format string and arguments.
       *
       * @param  l
--- 1343,37 ---
       *
       * @since  1.5
       */
      public PrintStream format(String format, Object ... args) {
          try {
!             if (lock != null) {
!                 lock.lock();
!                 try {
!                     lockedFormat(format, args);
!                 } finally {
!                     lock.unlock();
!                 }
!             } else {
+                 synchronized (this) {
+                     lockedFormat(format, args);
+                 }
              }
          } catch (InterruptedIOException x) {
              Thread.currentThread().interrupt();
          } catch (IOException x) {
              trouble = true;
          }
          return this;
      }
  
+     private void lockedFormat(String format, Object ... args) throws IOException {
+         ensureOpen();
+         if ((formatter == null) || (formatter.locale() != Locale.getDefault(Locale.Category.FORMAT)))
+             formatter = new Formatter((Appendable) this);
+         formatter.format(Locale.getDefault(Locale.Category.FORMAT), format, args);
+     }
+ 
      /**
       * Writes a formatted string to this output stream using the specified
       * format string and arguments.
       *
       * @param  l

*** 1265,25 ***
       *
       * @since  1.5
       */
      public PrintStream format(Locale l, String format, Object ... args) {
          try {
!             synchronized (this) {
!                 ensureOpen();
!                 if ((formatter == null)
!                     || (formatter.locale() != l))
!                     formatter = new Formatter(this, l);
!                 formatter.format(l, format, args);
              }
          } catch (InterruptedIOException x) {
              Thread.currentThread().interrupt();
          } catch (IOException x) {
              trouble = true;
          }
          return this;
      }
  
      /**
       * Appends the specified character sequence to this output stream.
       *
       * <p> An invocation of this method of the form {@code out.append(csq)}
       * behaves in exactly the same way as the invocation
--- 1412,37 ---
       *
       * @since  1.5
       */
      public PrintStream format(Locale l, String format, Object ... args) {
          try {
!             if (lock != null) {
!                 lock.lock();
!                 try {
!                     lockedFormat(l, format, args);
!                 } finally {
!                     lock.unlock();
+                 }
+             } else {
+                 synchronized (this) {
+                     lockedFormat(format, args);
+                 }
              }
          } catch (InterruptedIOException x) {
              Thread.currentThread().interrupt();
          } catch (IOException x) {
              trouble = true;
          }
          return this;
      }
  
+     private void lockedFormat(Locale l, String format, Object ... args) throws IOException {
+         ensureOpen();
+         if ((formatter == null) || (formatter.locale() != l))
+             formatter = new Formatter(this, l);
+         formatter.format(l, format, args);
+     }
+ 
      /**
       * Appends the specified character sequence to this output stream.
       *
       * <p> An invocation of this method of the form {@code out.append(csq)}
       * behaves in exactly the same way as the invocation

*** 1372,6 ***
--- 1531,14 ---
      public PrintStream append(char c) {
          print(c);
          return this;
      }
  
+     static {
+         SharedSecrets.setJavaIOCPrintStreamAccess(new JavaIOPrintStreamAccess() {
+             public Object lock(PrintStream ps) {
+                 Object lock = ps.lock;
+                 return (lock != null) ? lock : ps;
+             }
+         });
+     }
  }
< prev index next >