JDK-8150076 : Print jobs do not finish when using a page range
  • Type: Bug
  • Component: javafx
  • Sub-Component: graphics
  • Affected Version: 8u72,9
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_7
  • CPU: x86_64
  • Submitted: 2016-02-17
  • Updated: 2016-05-31
  • Resolved: 2016-05-31
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9
8u112Fixed 9Fixed
Description
J2SE Version (please include all output from java -version flag):
java version "1.8.0_75"
Java(TM) SE Runtime Environment (build 1.8.0_75-b07)
Java HotSpot(TM) Client VM (build 25.75-b07, mixed mode)

Does this problem occur on J2SE 6ux or 7ux or 8ux?  Yes / No (pick one)
  8U72, maybe more

Operating System Configuration Information (be specific):
  HP EliteBook 8760W
  Windows 7 Professional 64-bit (SP1)
  8G Ram

Bug Description:
  Having a trouble to have the print jobs finish when used a page range.  It 
might be coming from a thread race condition.

  The printer queue shows them as being completely spooled and ready to 
print, but the java thread that initiated the print is stuck.

---------------------------------------------------
com.sun.prism.j2d.print.J2DPrinterJob

The only place that pageDone is set to "true" is in waitForNextPage(int 
pageIndex)

If pJob2D.print() ever completes, and sets jobDone to true, waitForNextPage() may never be called. 
If this occurs, than the wait loop that the initiating thread is stuck in will never return.
It seems like the wait loops should always be checking on error and termination conditions.

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

                pJob2D.print(printReqAttrSet);
                jobDone = true;

        public int print(Graphics g, PageFormat pf, int pageIndex) {
            if (jobError || jobDone || !getPage(pageIndex)) {   *** If the 
jobDone is true, getPage() will not be called ***
                return Printable.NO_SUCH_PAGE;
            }

                while (!pageDone) { *** Not checking to see if an error 
occurred or if the job is done. ***
                    synchronized (monitor) {
                        try {
                            monitor.wait(1000);
                        } catch (InterruptedException e) {
                        }
                    }
                }

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

On a side note, these 1 second wait loops are way too long.  My pages print almost instantaneously.  These waits make these jobs take an order of 
magnitude longer to spool.  At the very least, they should be cut in half (or 
a tenth) which, in the greater scheme of things, would likely not put any 
significant burden on platforms currently capable of printing.

The real solution would actually be to "notify()" the "monitor" object when a 
page was completed so that it wouldn't have to always incur that 1 second wait to begin with.

Steps to Reproduce (be specific):

In the real application, this time slice hits every time.  In my test program, it does not and not sure why. (Threads...)

still working an creating a test program that will force the race condition 
to occur.
    


Comments
Changeset: 1f34c05c1dbe Author: mcherkas Date: 2016-05-31 14:30 -0700 URL: http://hg.openjdk.java.net/openjfx/9-dev/rt/rev/1f34c05c1dbe 8150076: Print jobs do not finish when using a page range Reviewed-by: prr ! modules/graphics/src/main/java/com/sun/prism/j2d/print/J2DPrinterJob.java + tests/manual/printing/PrintTest.java
31-05-2016

Approved to backport to 8u-dev (8u112). Btw, Phil can set the "user" field to "mcherkas" as a preferred alternative to adding a "Contributed-by" field.
16-05-2016

[~prr] if fix looks ok, could you please push the fix to fx9 and fx8 repositories? Unfortunately, I don't have commiter status to do this myself. Please don't forget to put "Contributed-by: mcherkas" field into commit decription. Thanks, Mikhail.
16-05-2016

Hi Phil, if a page range IS NOT specified, then com.sun.prism.j2d.print.J2DPrinterJob.J2DPageable#print method will be called as many times as we return something for printing, if all pages are printed then the last call for "print" will return NO_SUCH_PAGE and invoke com.sun.prism.j2d.print.J2DPrinterJob.J2DPageable#getPage which will call com.sun.prism.j2d.print.J2DPrinterJob.J2DPageable#waitForNextPage and waitForNextPage will set pageDone = true. But in case when a page range IS specified com.sun.prism.j2d.print.J2DPrinterJob.J2DPageable#print will be called as many times as needed, so there won't be extra call that will set pageDone = true for us. So we can finish printing job without setting pageDone for last page and for this case I added checks for jobDone or jobError.
05-05-2016

Looks OK. I am not sure why but your test was passing for me on current JDK 9 builds -and then stopped passing. But your fix will cure it. This bug report should really have an evaluation in it. What is happening is that "pageDone" is set only when we go to get the next page. This won't happen if a PageRange is passed to the "2D" PrinterJob since it won't call back to us again. It might seem like we should not pass the Range to the underlying PrinterJob but it is needed so it populates the user dialog properly.
29-04-2016

Hi Phil, I wrote manul test for this issue and crate webrev against jdk9: http://cr.openjdk.java.net/~mcherkas/8150076/9/webrev.00/ To reproduce the issue you need to print on other thread than FX application thread, this is allowed by doc: https://docs.oracle.com/javase/8/javafx/api/javafx/print/PrinterJob.html "There is no requirement to do printing on the FX application thread. A node may be prepared for printing on any thread, the job may be invoked on any thread." Thanks, Mikhail.
19-04-2016

Review request was sent here : http://mail.openjdk.java.net/pipermail/openjfx-dev/2016-April/018944.html My response .. I have an old PageRange printing test where I set settings.setPageRanges(new PageRange(1,3)); and it prints fine on JDK 9 on Win 7 x64. In fact it also prints fine with 8u74 Also a thread dump doesn't show any problem with the print thread not terminating. So I assume the test that shows the bug must be doing something I am not. You say [9] above but I see your webrev suggests 8u ! "/cygdrive/c/ws/javafx/8u-dev/rt" Can you please (a) prepare a webrev against 9 instead of 8u. (b) include (and therefore share) the test you are using that illustrates the problem. -phil.
13-04-2016

Additional comment from the submitter and a fairly simple user level workaround: ================ The JavaFX print model is not a callback model, but one where nodes are explicitly printed one page at a time. Because of this, I don���t think there is a reason to have the javax.print attributes internal to the prism libraries care about the page range. If this is true, a possible solution may be to simply not copy PageRange values from the JobSettings to the attribute set. A simple user-level workaround of this issue is to clear the ranges in the JobSettings before the print occurs. PageRange[] ranges = job.getJobSettings().getPageRanges(); job.getJobSettings().setPageRanges((PageRange[])null); //JDK-8150076 for (PageRange range : ranges) { for (int page = range.getStartPage(); page <= range.getEndPage(); page++) { job.printPage( <Node> ) } } I think the way threads are handled in the prism API is not great (JDK-8150181), but in this case I think the issue can be circumvented entirely by just internally ignoring the page range (since it is not relevant). In fact, it seems entirely possible that a scenario could be created where setting the page range in the attribute set could produce other types of unexpected results as it interacts with the user loop. That being said, I have not investigated how things like WebEngine uses the print job and if, in these custom cases, the internal page range is valid. J2DPrinterJob private void syncSettingsToAttributes() { syncJobName(); syncCopies(); *** syncPageRanges(); *** syncSides(); syncCollation(); syncPageLayout(); syncPaperSource(); syncColor(); syncPrintQuality(); syncPrintResolution(); } ================
02-03-2016