Merge
authorlana
Wed, 18 Nov 2009 17:16:27 -0800
changeset 4253 c9f997bcd7e2
parent 4149 4c48c068c995 (current diff)
parent 4252 0e645080f74b (diff)
child 4268 505b39f182af
Merge
--- a/jdk/src/share/classes/javax/print/attribute/standard/PrinterStateReasons.java	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/share/classes/javax/print/attribute/standard/PrinterStateReasons.java	Wed Nov 18 17:16:27 2009 -0800
@@ -65,7 +65,7 @@
  * PrinterStateReason PrinterStateReason} objects to an existing
  * PrinterStateReasons object and remove them again. However, like class
  *  {@link java.util.HashMap java.util.HashMap}, class PrinterStateReasons is
- * bot multiple thread safe. If a PrinterStateReasons object will be used by
+ * not multiple thread safe. If a PrinterStateReasons object will be used by
  * multiple threads, be sure to synchronize its operations (e.g., using a
  * synchronized map view obtained from class {@link java.util.Collections
  * java.util.Collections}).
--- a/jdk/src/share/classes/sun/font/Font2D.java	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/share/classes/sun/font/Font2D.java	Wed Nov 18 17:16:27 2009 -0800
@@ -320,21 +320,6 @@
                     lastFontStrike = new SoftReference(strike);
                     StrikeCache.refStrike(strike);
                     return strike;
-                } else {
-                    /* We have found a cleared reference that has not yet
-                     * been removed by the disposer.
-                     * If we make this reference unreachable by removing it
-                     * from the map,or overwriting it with a new reference to
-                     * a new strike, then it is possible it may never be
-                     * enqueued for disposal. That is the implication of
-                     * the docs for java.lang.ref. So on finding a cleared
-                     * reference, we need to dispose the native resources,
-                     * if they haven't already been freed.
-                     * The reference object needs to have a reference to
-                     * the disposer instance for this to occur.
-                     */
-                  ((StrikeCache.DisposableStrike)strikeRef)
-                      .getDisposer().dispose();
                 }
             }
             /* When we create a new FontStrike instance, we *must*
--- a/jdk/src/share/classes/sun/font/FontDesignMetrics.java	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/share/classes/sun/font/FontDesignMetrics.java	Wed Nov 18 17:16:27 2009 -0800
@@ -171,7 +171,7 @@
      * out we can clear the keys from the table.
      */
     private static class KeyReference extends SoftReference
-        implements DisposerRecord {
+        implements DisposerRecord, Disposer.PollDisposable {
 
         static ReferenceQueue queue = Disposer.getQueue();
 
--- a/jdk/src/share/classes/sun/font/FontStrikeDisposer.java	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/share/classes/sun/font/FontStrikeDisposer.java	Wed Nov 18 17:16:27 2009 -0800
@@ -25,6 +25,7 @@
 
 package sun.font;
 
+import sun.java2d.Disposer;
 import sun.java2d.DisposerRecord;
 
 /*
@@ -49,7 +50,8 @@
  * entries would be removed much more promptly than we need.
  */
 
-class FontStrikeDisposer implements DisposerRecord {
+class FontStrikeDisposer
+    implements DisposerRecord, Disposer.PollDisposable {
 
     Font2D font2D;
     FontStrikeDesc desc;
--- a/jdk/src/share/classes/sun/font/StrikeCache.java	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/share/classes/sun/font/StrikeCache.java	Wed Nov 18 17:16:27 2009 -0800
@@ -254,9 +254,20 @@
         // because they may be accessed on that thread at the time of the
         // disposal (for example, when the accel. cache is invalidated)
 
-        // REMIND: this look a bit heavyweight, but should be ok
-        // because strike disposal is a relatively infrequent operation,
-        // more worrisome is the necessity of getting a GC here.
+        // Whilst this is a bit heavyweight, in most applications
+        // strike disposal is a relatively infrequent operation, so it
+        // doesn't matter. But in some tests that use vast numbers
+        // of strikes, the switching back and forth is measurable.
+        // So the "pollRemove" call is added to batch up the work.
+        // If we are polling we know we've already been called back
+        // and can directly dispose the record.
+        // Also worrisome is the necessity of getting a GC here.
+
+        if (Disposer.pollingQueue) {
+            doDispose(disposer);
+            return;
+        }
+
         RenderQueue rq = null;
         GraphicsEnvironment ge =
             GraphicsEnvironment.getLocalGraphicsEnvironment();
@@ -277,6 +288,7 @@
                 rq.flushAndInvokeNow(new Runnable() {
                     public void run() {
                         doDispose(disposer);
+                        Disposer.pollRemove();
                     }
                 });
             } finally {
--- a/jdk/src/share/classes/sun/java2d/Disposer.java	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/share/classes/sun/java2d/Disposer.java	Wed Nov 18 17:16:27 2009 -0800
@@ -29,6 +29,7 @@
 import java.lang.ref.ReferenceQueue;
 import java.lang.ref.PhantomReference;
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
 import java.util.Hashtable;
 
 /**
@@ -146,6 +147,7 @@
                 rec.dispose();
                 obj = null;
                 rec = null;
+                clearDeferredRecords();
             } catch (Exception e) {
                 System.out.println("Exception while removing reference: " + e);
                 e.printStackTrace();
@@ -153,6 +155,85 @@
         }
     }
 
+    /*
+     * This is a marker interface that, if implemented, means it
+     * doesn't acquire any special locks, and is safe to
+     * be disposed in the poll loop on whatever thread
+     * which happens to be the Toolkit thread, is in use.
+     */
+    public static interface PollDisposable {
+    };
+
+    private static ArrayList<DisposerRecord> deferredRecords = null;
+
+    private static void clearDeferredRecords() {
+        if (deferredRecords == null || deferredRecords.isEmpty()) {
+            return;
+        }
+        for (int i=0;i<deferredRecords.size(); i++) {
+            try {
+                DisposerRecord rec = deferredRecords.get(i);
+                rec.dispose();
+            } catch (Exception e) {
+                System.out.println("Exception while disposing deferred rec.");
+                e.printStackTrace();
+            }
+        }
+        deferredRecords.clear();
+    }
+
+    /*
+     * Set to indicate the queue is presently being polled.
+     */
+    public static volatile boolean pollingQueue = false;
+
+    /*
+     * The pollRemove() method is called back from a dispose method
+     * that is running on the toolkit thread and wants to
+     * dispose any pending refs that are safe to be disposed
+     * on that thread.
+     */
+    public static void pollRemove() {
+
+        /* This should never be called recursively, so this check
+         * is just a safeguard against the unexpected.
+         */
+        if (pollingQueue) {
+            return;
+        }
+        Object obj;
+        pollingQueue = true;
+        int freed = 0;
+        int deferred = 0;
+        try {
+            while ((obj = queue.poll()) != null
+                   && freed < 10000 && deferred < 100) {
+                freed++;
+                ((Reference)obj).clear();
+                DisposerRecord rec = (DisposerRecord)records.remove(obj);
+                if (rec instanceof PollDisposable) {
+                    rec.dispose();
+                    obj = null;
+                    rec = null;
+                } else {
+                    if (rec == null) { // shouldn't happen, but just in case.
+                        continue;
+                    }
+                    deferred++;
+                    if (deferredRecords == null) {
+                      deferredRecords = new ArrayList<DisposerRecord>(5);
+                    }
+                    deferredRecords.add(rec);
+                }
+            }
+        } catch (Exception e) {
+            System.out.println("Exception while removing reference: " + e);
+            e.printStackTrace();
+        } finally {
+            pollingQueue = false;
+        }
+    }
+
     private static native void initIDs();
 
     /*
--- a/jdk/src/share/classes/sun/java2d/SunGraphics2D.java	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/share/classes/sun/java2d/SunGraphics2D.java	Wed Nov 18 17:16:27 2009 -0800
@@ -257,7 +257,6 @@
             font = defaultFont;
         }
 
-        loops = sd.getRenderLoops(this);
         setDevClip(sd.getBounds());
         invalidatePipe();
     }
@@ -367,6 +366,7 @@
         shapepipe = invalidpipe;
         textpipe = invalidpipe;
         imagepipe = invalidpipe;
+        loops = null;
     }
 
     public void validatePipe() {
--- a/jdk/src/share/classes/sun/java2d/SurfaceData.java	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/share/classes/sun/java2d/SurfaceData.java	Wed Nov 18 17:16:27 2009 -0800
@@ -69,6 +69,7 @@
 import sun.java2d.pipe.DrawImage;
 import sun.awt.SunHints;
 import sun.awt.image.SurfaceManager;
+import sun.java2d.pipe.LoopBasedPipe;
 
 /**
  * This class provides various pieces of information relevant to a
@@ -506,7 +507,6 @@
                     sg2d.textpipe = solidTextRenderer;
                 }
                 sg2d.shapepipe = colorPrimitives;
-                sg2d.loops = getRenderLoops(sg2d);
                 // assert(sg2d.surfaceData == this);
             }
         } else if (sg2d.compositeState == sg2d.COMP_CUSTOM) {
@@ -603,8 +603,17 @@
 
             sg2d.textpipe = getTextPipe(sg2d, false /* AA==OFF */);
             sg2d.shapepipe = colorPrimitives;
+            // assert(sg2d.surfaceData == this);
+        }
+
+        // check for loops
+        if (sg2d.textpipe  instanceof LoopBasedPipe ||
+            sg2d.shapepipe instanceof LoopBasedPipe ||
+            sg2d.fillpipe  instanceof LoopBasedPipe ||
+            sg2d.drawpipe  instanceof LoopBasedPipe ||
+            sg2d.imagepipe instanceof LoopBasedPipe)
+        {
             sg2d.loops = getRenderLoops(sg2d);
-            // assert(sg2d.surfaceData == this);
         }
     }
 
--- a/jdk/src/share/classes/sun/java2d/pipe/AATextRenderer.java	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/share/classes/sun/java2d/pipe/AATextRenderer.java	Wed Nov 18 17:16:27 2009 -0800
@@ -34,8 +34,9 @@
  * a solid source colour to an opaque destination.
  */
 
-public class AATextRenderer extends GlyphListLoopPipe {
-
+public class AATextRenderer extends GlyphListLoopPipe
+    implements LoopBasedPipe
+{
    protected void drawGlyphList(SunGraphics2D sg2d, GlyphList gl) {
        sg2d.loops.drawGlyphListAALoop.DrawGlyphListAA(sg2d, sg2d.surfaceData,
                                                       gl);
--- a/jdk/src/share/classes/sun/java2d/pipe/GlyphListLoopPipe.java	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/share/classes/sun/java2d/pipe/GlyphListLoopPipe.java	Wed Nov 18 17:16:27 2009 -0800
@@ -36,8 +36,9 @@
  * the installed loop may not match the glyphvector.
  */
 
-public abstract class GlyphListLoopPipe extends GlyphListPipe {
-
+public abstract class GlyphListLoopPipe extends GlyphListPipe
+    implements LoopBasedPipe
+{
     protected void drawGlyphList(SunGraphics2D sg2d, GlyphList gl,
                                  int aaHint) {
         switch (aaHint) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/java2d/pipe/LoopBasedPipe.java	Wed Nov 18 17:16:27 2009 -0800
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.java2d.pipe;
+
+/**
+ * This is a marker interface used by Pipes that need RenderLoops.
+ * RenderLoops are validated in SurfaceData when a pipe is recognised to
+ * implement this interface.
+ *
+ * @author Mario Torre <neugens@aicas.com>
+ */
+public interface LoopBasedPipe {
+
+}
--- a/jdk/src/share/classes/sun/java2d/pipe/LoopPipe.java	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/share/classes/sun/java2d/pipe/LoopPipe.java	Wed Nov 18 17:16:27 2009 -0800
@@ -46,7 +46,8 @@
 public class LoopPipe
     implements PixelDrawPipe,
                PixelFillPipe,
-               ShapeDrawPipe
+               ShapeDrawPipe,
+               LoopBasedPipe
 {
     final static RenderingEngine RenderEngine = RenderingEngine.getInstance();
 
--- a/jdk/src/share/classes/sun/java2d/pipe/SolidTextRenderer.java	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/share/classes/sun/java2d/pipe/SolidTextRenderer.java	Wed Nov 18 17:16:27 2009 -0800
@@ -35,8 +35,9 @@
  * a solid source colour to an opaque destination.
  */
 
-public class SolidTextRenderer extends GlyphListLoopPipe {
-
+public class SolidTextRenderer extends GlyphListLoopPipe
+    implements LoopBasedPipe
+{
     protected void drawGlyphList(SunGraphics2D sg2d, GlyphList gl) {
         sg2d.loops.drawGlyphListLoop.DrawGlyphList(sg2d, sg2d.surfaceData, gl);
     }
--- a/jdk/src/share/classes/sun/java2d/pipe/SpanShapeRenderer.java	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/share/classes/sun/java2d/pipe/SpanShapeRenderer.java	Wed Nov 18 17:16:27 2009 -0800
@@ -65,7 +65,9 @@
         }
     }
 
-    public static class Simple extends SpanShapeRenderer {
+    public static class Simple extends SpanShapeRenderer
+        implements  LoopBasedPipe
+    {
         public Object startSequence(SunGraphics2D sg, Shape s,
                                     Rectangle devR, int[] bbox) {
             return sg;
--- a/jdk/src/share/classes/sun/java2d/pisces/Renderer.java	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/share/classes/sun/java2d/pisces/Renderer.java	Wed Nov 18 17:16:27 2009 -0800
@@ -775,10 +775,12 @@
 
     // Free sorting arrays if larger than maximum size
     private void crossingListFinished() {
-        if (crossings.length > DEFAULT_CROSSINGS_SIZE) {
+        if (crossings != null && crossings.length > DEFAULT_CROSSINGS_SIZE) {
             crossings = new int[DEFAULT_CROSSINGS_SIZE];
         }
-        if (crossingIndices.length > DEFAULT_INDICES_SIZE) {
+        if (crossingIndices != null &&
+            crossingIndices.length > DEFAULT_INDICES_SIZE)
+        {
             crossingIndices = new int[DEFAULT_INDICES_SIZE];
         }
     }
--- a/jdk/src/share/native/sun/awt/image/jpeg/imageioJPEG.c	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/share/native/sun/awt/image/jpeg/imageioJPEG.c	Wed Nov 18 17:16:27 2009 -0800
@@ -1437,6 +1437,7 @@
         JNU_ThrowByName( env,
                          "java/lang/OutOfMemoryError",
                          "Initializing Reader");
+        free(cinfo);
         return 0;
     }
 
@@ -1473,6 +1474,7 @@
         JNU_ThrowByName(env,
                         "java/lang/OutOfMemoryError",
                         "Initializing Reader");
+        imageio_dispose((j_common_ptr)cinfo);
         return 0;
     }
     cinfo->src->bytes_in_buffer = 0;
@@ -1489,6 +1491,7 @@
         JNU_ThrowByName( env,
                          "java/lang/OutOfMemoryError",
                          "Initializing Reader");
+        imageio_dispose((j_common_ptr)cinfo);
         return 0;
     }
     return (jlong) ret;
@@ -2420,8 +2423,7 @@
         JNU_ThrowByName( env,
                          "java/lang/OutOfMemoryError",
                          "Initializing Writer");
-        free(cinfo);
-        free(jerr);
+        imageio_dispose((j_common_ptr)cinfo);
         return 0;
     }
 
@@ -2439,8 +2441,7 @@
         JNU_ThrowByName( env,
                          "java/lang/OutOfMemoryError",
                          "Initializing Writer");
-        free(cinfo);
-        free(jerr);
+        imageio_dispose((j_common_ptr)cinfo);
         return 0;
     }
     return (jlong) ret;
--- a/jdk/src/share/native/sun/awt/medialib/awt_ImagingLib.c	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/share/native/sun/awt/medialib/awt_ImagingLib.c	Wed Nov 18 17:16:27 2009 -0800
@@ -960,21 +960,15 @@
     mlib_filter filter;
     unsigned int *dP;
 
-    if ((srcRasterP = (RasterS_t *) calloc(1, sizeof(RasterS_t))) == NULL) {
-        JNU_ThrowOutOfMemoryError(env, "Out of memory");
-        return -1;
-    }
-
-    if ((dstRasterP = (RasterS_t *) calloc(1, sizeof(RasterS_t))) == NULL) {
-        JNU_ThrowOutOfMemoryError(env, "Out of memory");
-        free(srcRasterP);
-        return -1;
-    }
-
     /* This function requires a lot of local refs ??? Is 64 enough ??? */
     if ((*env)->EnsureLocalCapacity(env, 64) < 0)
         return 0;
 
+    if (s_nomlib) return 0;
+    if (s_timeIt) {
+        (*start_timer)(3600);
+    }
+
     switch(interpType) {
     case java_awt_image_AffineTransformOp_TYPE_BILINEAR:
         filter = MLIB_BILINEAR;
@@ -990,9 +984,15 @@
         return -1;
     }
 
-    if (s_nomlib) return 0;
-    if (s_timeIt) {
-        (*start_timer)(3600);
+    if ((srcRasterP = (RasterS_t *) calloc(1, sizeof(RasterS_t))) == NULL) {
+        JNU_ThrowOutOfMemoryError(env, "Out of memory");
+        return -1;
+    }
+
+    if ((dstRasterP = (RasterS_t *) calloc(1, sizeof(RasterS_t))) == NULL) {
+        JNU_ThrowOutOfMemoryError(env, "Out of memory");
+        free(srcRasterP);
+        return -1;
     }
 
     if ((*env)->GetArrayLength(env, jmatrix) < 6) {
@@ -1215,6 +1215,9 @@
     }
 
     if (tbl == NULL || table == NULL || jtable == NULL) {
+        if (tbl != NULL) free(tbl);
+        if (table != NULL) free(table);
+        if (jtable != NULL) free(jtable);
         awt_freeParsedImage(srcImageP, TRUE);
         awt_freeParsedImage(dstImageP, TRUE);
         JNU_ThrowNullPointerException(env, "NULL LUT");
@@ -1224,6 +1227,11 @@
     for (i=0; i < jlen; i++) {
         jtable[i] = (*env)->GetObjectArrayElement(env, jtableArrays, i);
         if (jtable[i] == NULL) {
+            free(tbl);
+            free(table);
+            free(jtable);
+            awt_freeParsedImage(srcImageP, TRUE);
+            awt_freeParsedImage(dstImageP, TRUE);
             return 0;
         }
     }
@@ -1232,6 +1240,9 @@
                         FALSE, &hint);
     if (nbands < 1) {
         /* Can't handle any custom images */
+        free(tbl);
+        free(table);
+        free(jtable);
         awt_freeParsedImage(srcImageP, TRUE);
         awt_freeParsedImage(dstImageP, TRUE);
         return 0;
@@ -1240,12 +1251,18 @@
     /* Allocate the arrays */
     if (allocateArray(env, srcImageP, &src, &sdata, TRUE, FALSE, FALSE) < 0) {
         /* Must be some problem */
+        free(tbl);
+        free(table);
+        free(jtable);
         awt_freeParsedImage(srcImageP, TRUE);
         awt_freeParsedImage(dstImageP, TRUE);
         return 0;
     }
     if (allocateArray(env, dstImageP, &dst, &ddata, FALSE, FALSE, FALSE) < 0) {
         /* Must be some problem */
+        free(tbl);
+        free(table);
+        free(jtable);
         freeArray(env, srcImageP, src, sdata, NULL, NULL, NULL);
         awt_freeParsedImage(srcImageP, TRUE);
         awt_freeParsedImage(dstImageP, TRUE);
@@ -1284,6 +1301,9 @@
                                                       (jbyte *) table[j],
                                                       JNI_ABORT);
             }
+            free(tbl);
+            free(table);
+            free(jtable);
             freeArray(env, srcImageP, src, sdata, NULL, NULL, NULL);
             awt_freeParsedImage(srcImageP, TRUE);
             awt_freeParsedImage(dstImageP, TRUE);
@@ -1413,12 +1433,15 @@
 
     /* Parse the source raster - reject custom images */
     if ((status = awt_parseRaster(env, jsrc, srcRasterP)) <= 0) {
+        free(srcRasterP);
+        free(dstRasterP);
         return 0;
     }
 
     /* Parse the destination image - reject custom images */
     if ((status = awt_parseRaster(env, jdst, dstRasterP)) <= 0) {
         awt_freeParsedRaster(srcRasterP, TRUE);
+        free(dstRasterP);
         return 0;
     }
 
--- a/jdk/src/share/native/sun/font/freetypeScaler.c	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/share/native/sun/font/freetypeScaler.c	Wed Nov 18 17:16:27 2009 -0800
@@ -102,9 +102,21 @@
 }
 
 static void freeNativeResources(JNIEnv *env, FTScalerInfo* scalerInfo) {
+    void *stream;
+
     if (scalerInfo == NULL)
         return;
 
+    //apparently Done_Face will only close the stream
+    // but will not relase the memory of stream structure.
+    // We need to free it explicitly to avoid leak.
+    //Direct access to the stream field might be not ideal solution as
+    // it is considred to be "private".
+    //Alternatively we could have stored pointer to the structure
+    // in the scalerInfo but this will increase size of the structure
+    // for no good reason
+    stream = scalerInfo->face->stream;
+
     FT_Done_Face(scalerInfo->face);
     FT_Done_FreeType(scalerInfo->library);
 
@@ -116,6 +128,10 @@
         free(scalerInfo->fontData);
     }
 
+   if (stream != NULL) {
+        free(stream);
+   }
+
     free(scalerInfo);
 }
 
--- a/jdk/src/solaris/classes/sun/java2d/x11/X11SurfaceData.java	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/solaris/classes/sun/java2d/x11/X11SurfaceData.java	Wed Nov 18 17:16:27 2009 -0800
@@ -388,7 +388,10 @@
             // if a GlyphVector overrides the AA setting.
             // We use getRenderLoops() rather than setting solidloops
             // directly so that we get the appropriate loops in XOR mode.
-            sg2d.loops = getRenderLoops(sg2d);
+            if (sg2d.loops == null) {
+                // assert(some pipe will always be a LoopBasedPipe)
+                sg2d.loops = getRenderLoops(sg2d);
+            }
         } else {
             super.validatePipe(sg2d);
         }
--- a/jdk/src/solaris/native/sun/awt/awt_GraphicsEnv.h	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/solaris/native/sun/awt/awt_GraphicsEnv.h	Wed Nov 18 17:16:27 2009 -0800
@@ -42,6 +42,10 @@
 #include <sys/ipc.h>
 #include <sys/shm.h>
 #include <X11/extensions/XShm.h>
+#ifndef X_ShmAttach
+#include <X11/Xmd.h>
+#include <X11/extensions/shmproto.h>
+#endif
 
 extern int XShmQueryExtension();
 
--- a/jdk/src/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java	Wed Nov 18 17:16:27 2009 -0800
@@ -210,7 +210,10 @@
             // if a GlyphVector overrides the AA setting.
             // We use getRenderLoops() rather than setting solidloops
             // directly so that we get the appropriate loops in XOR mode.
-            sg2d.loops = getRenderLoops(sg2d);
+            if (sg2d.loops == null) {
+                // assert(some pipe will always be a LoopBasedPipe)
+                sg2d.loops = getRenderLoops(sg2d);
+            }
         } else {
             super.validatePipe(sg2d);
         }
--- a/jdk/src/windows/native/sun/windows/awt_PrintControl.cpp	Thu Nov 12 15:35:52 2009 -0800
+++ b/jdk/src/windows/native/sun/windows/awt_PrintControl.cpp	Wed Nov 18 17:16:27 2009 -0800
@@ -687,7 +687,7 @@
     // Now, set-up the struct for the real calls to ::PrintDlg and ::CreateDC
 
     pd.hwndOwner = hwndOwner;
-    pd.Flags = PD_ENABLEPRINTHOOK | PD_RETURNDC;
+    pd.Flags = PD_ENABLEPRINTHOOK | PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE;
     pd.lpfnPrintHook = (LPPRINTHOOKPROC)PrintDlgHook;
 
     if (env->CallBooleanMethod(printCtrl, AwtPrintControl::getCollateID)) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/java2d/pisces/Renderer/TestNPE.java	Wed Nov 18 17:16:27 2009 -0800
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/**
+ * @test
+ * @bug     6887494
+ *
+ * @summary Verifies that no NullPointerException is thrown in Pisces Renderer
+ *          under certain circumstances.
+ *
+ * @run     main TestNPE
+ */
+
+import java.awt.*;
+import java.awt.geom.*;
+import java.awt.image.BufferedImage;
+
+public class TestNPE {
+
+    private static void paint(Graphics g) {
+        Graphics2D g2d = (Graphics2D) g;
+        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                             RenderingHints.VALUE_ANTIALIAS_ON);
+        g2d.setClip(0, 0, 0, 0);
+        g2d.setTransform(
+               new AffineTransform(4.0f, 0.0f, 0.0f, 4.0f, -1248.0f, -744.0f));
+        g2d.draw(new Line2D.Float(131.21428571428572f, 33.0f,
+                                  131.21428571428572f, 201.0f));
+    }
+
+    public static void main(String[] args) {
+        BufferedImage im = new BufferedImage(100, 100,
+                                             BufferedImage.TYPE_INT_ARGB);
+
+        // Trigger exception in main thread.
+        Graphics g = im.getGraphics();
+        paint(g);
+    }
+}