diff --git a/jdk/src/share/classes/java/awt/color/ICC_Profile.java b/jdk/src/share/classes/java/awt/color/ICC_Profile.java index 77d40dbc04c..1089400e374 100644 --- a/jdk/src/share/classes/java/awt/color/ICC_Profile.java +++ b/jdk/src/share/classes/java/awt/color/ICC_Profile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. 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 @@ -40,9 +40,7 @@ import sun.java2d.cmm.CMSManager; import sun.java2d.cmm.Profile; import sun.java2d.cmm.ProfileDataVerifier; -import sun.java2d.cmm.ProfileDeferralMgr; import sun.java2d.cmm.ProfileDeferralInfo; -import sun.java2d.cmm.ProfileActivator; import sun.misc.IOUtils; import java.io.BufferedInputStream; @@ -57,6 +55,7 @@ import java.io.ObjectStreamException; import java.io.OutputStream; import java.io.Serializable; +import java.io.FilePermission; import java.util.StringTokenizer; @@ -97,10 +96,8 @@ public class ICC_Profile implements Serializable { private static final long serialVersionUID = -3938515861990936766L; - private transient Profile cmmProfile; - - private transient ProfileDeferralInfo deferralInfo; - private transient ProfileActivator profileActivator; + private transient volatile Profile cmmProfile; + private transient volatile ProfileDeferralInfo deferralInfo; // Registry of singleton profile objects for specific color spaces // defined in the ColorSpace class (e.g. CS_sRGB), see @@ -740,13 +737,7 @@ public class ICC_Profile implements Serializable { * The ID will be 0 until the profile is loaded. */ ICC_Profile(ProfileDeferralInfo pdi) { - this.deferralInfo = pdi; - this.profileActivator = new ProfileActivator() { - public void activate() throws ProfileDataException { - activateDeferredProfile(); - } - }; - ProfileDeferralMgr.registerDeferral(this.profileActivator); + deferralInfo = pdi; } @@ -756,8 +747,6 @@ public void activate() throws ProfileDataException { protected void finalize () { if (cmmProfile != null) { CMSManager.getModule().freeProfile(cmmProfile); - } else if (profileActivator != null) { - ProfileDeferralMgr.unregisterDeferral(profileActivator); } } @@ -775,10 +764,6 @@ public static ICC_Profile getInstance(byte[] data) { Profile p = null; - if (ProfileDeferralMgr.deferring) { - ProfileDeferralMgr.activateProfiles(); - } - ProfileDataVerifier.verify(data); try { @@ -842,11 +827,11 @@ public static ICC_Profile getInstance (int cspace) { * Enabling the appropriate access privileges is handled * at a lower level. */ - ProfileDeferralInfo pInfo = + ProfileDeferralInfo pdi = new ProfileDeferralInfo("sRGB.pf", ColorSpace.TYPE_RGB, 3, CLASS_DISPLAY); - sRGBprofile = getDeferredInstance(pInfo); + sRGBprofile = new ICC_ProfileRGB(pdi); } thisProfile = sRGBprofile; } @@ -856,11 +841,11 @@ public static ICC_Profile getInstance (int cspace) { case ColorSpace.CS_CIEXYZ: synchronized(ICC_Profile.class) { if (XYZprofile == null) { - ProfileDeferralInfo pInfo = + ProfileDeferralInfo pdi = new ProfileDeferralInfo("CIEXYZ.pf", ColorSpace.TYPE_XYZ, 3, - CLASS_DISPLAY); - XYZprofile = getDeferredInstance(pInfo); + CLASS_ABSTRACT); + XYZprofile = new ICC_Profile(pdi); } thisProfile = XYZprofile; } @@ -872,11 +857,11 @@ public static ICC_Profile getInstance (int cspace) { if (PYCCprofile == null) { if (standardProfileExists("PYCC.pf")) { - ProfileDeferralInfo pInfo = + ProfileDeferralInfo pdi = new ProfileDeferralInfo("PYCC.pf", ColorSpace.TYPE_3CLR, 3, - CLASS_DISPLAY); - PYCCprofile = getDeferredInstance(pInfo); + CLASS_COLORSPACECONVERSION); + PYCCprofile = new ICC_Profile(pdi); } else { throw new IllegalArgumentException( "Can't load standard profile: PYCC.pf"); @@ -890,11 +875,11 @@ public static ICC_Profile getInstance (int cspace) { case ColorSpace.CS_GRAY: synchronized(ICC_Profile.class) { if (GRAYprofile == null) { - ProfileDeferralInfo pInfo = + ProfileDeferralInfo pdi = new ProfileDeferralInfo("GRAY.pf", ColorSpace.TYPE_GRAY, 1, CLASS_DISPLAY); - GRAYprofile = getDeferredInstance(pInfo); + GRAYprofile = new ICC_ProfileGray(pdi); } thisProfile = GRAYprofile; } @@ -904,11 +889,11 @@ public static ICC_Profile getInstance (int cspace) { case ColorSpace.CS_LINEAR_RGB: synchronized(ICC_Profile.class) { if (LINEAR_RGBprofile == null) { - ProfileDeferralInfo pInfo = + ProfileDeferralInfo pdi = new ProfileDeferralInfo("LINEAR_RGB.pf", ColorSpace.TYPE_RGB, 3, CLASS_DISPLAY); - LINEAR_RGBprofile = getDeferredInstance(pInfo); + LINEAR_RGBprofile = new ICC_ProfileRGB(pdi); } thisProfile = LINEAR_RGBprofile; } @@ -1005,13 +990,7 @@ public static ICC_Profile getInstance(String fileName) throws IOException { * contain valid ICC Profile data. */ public static ICC_Profile getInstance(InputStream s) throws IOException { - byte profileData[]; - - if (s instanceof ProfileDeferralInfo) { - /* hack to detect profiles whose loading can be deferred */ - return getDeferredInstance((ProfileDeferralInfo) s); - } - + byte[] profileData; if ((profileData = getProfileDataFromStream(s)) == null) { throw new IllegalArgumentException("Invalid ICC Profile Data"); } @@ -1044,73 +1023,32 @@ static byte[] getProfileDataFromStream(InputStream s) throws IOException { /** - * Constructs an ICC_Profile for which the actual loading of the - * profile data from a file and the initialization of the CMM should - * be deferred as long as possible. - * Deferral is only used for standard profiles. - * If deferring is disabled, then getStandardProfile() ensures - * that all of the appropriate access privileges are granted - * when loading this profile. - * If deferring is enabled, then the deferred activation - * code will take care of access privileges. - * @see activateDeferredProfile() + * Activates the deferred standard profiles. Implementation of this method + * mimics the old behaviour when the CMMException and IOException were + * wrapped by the ProfileDataException, and the ProfileDataException itself + * was ignored during activation. */ - static ICC_Profile getDeferredInstance(ProfileDeferralInfo pdi) { - if (!ProfileDeferralMgr.deferring) { - return getStandardProfile(pdi.filename); - } - if (pdi.colorSpaceType == ColorSpace.TYPE_RGB) { - return new ICC_ProfileRGB(pdi); - } else if (pdi.colorSpaceType == ColorSpace.TYPE_GRAY) { - return new ICC_ProfileGray(pdi); - } else { - return new ICC_Profile(pdi); - } - } - - - void activateDeferredProfile() throws ProfileDataException { - byte profileData[]; - FileInputStream fis; - final String fileName = deferralInfo.filename; - - profileActivator = null; - deferralInfo = null; - PrivilegedAction pa = new PrivilegedAction() { - public FileInputStream run() { - File f = getStandardProfileFile(fileName); - if (f != null) { - try { - return new FileInputStream(f); - } catch (FileNotFoundException e) {} + private void activate() { + if (cmmProfile == null) { + synchronized (this) { + if (cmmProfile != null) { + return; + } + InputStream is = getStandardProfileInputStream(deferralInfo.filename); + if (is == null) { + return; + } + try { + byte[] data = getProfileDataFromStream(is); + if (data != null) { + cmmProfile = CMSManager.getModule().loadProfile(data); + // from now we cannot use the deferred value, drop it + deferralInfo = null; + } + is.close(); /* close the stream */ + } catch (CMMException | IOException ignore) { } - return null; } - }; - if ((fis = AccessController.doPrivileged(pa)) == null) { - throw new ProfileDataException("Cannot open file " + fileName); - } - try { - profileData = getProfileDataFromStream(fis); - fis.close(); /* close the file */ - } - catch (IOException e) { - ProfileDataException pde = new - ProfileDataException("Invalid ICC Profile Data" + fileName); - pde.initCause(e); - throw pde; - } - if (profileData == null) { - throw new ProfileDataException("Invalid ICC Profile Data" + - fileName); - } - try { - cmmProfile = CMSManager.getModule().loadProfile(profileData); - } catch (CMMException c) { - ProfileDataException pde = new - ProfileDataException("Invalid ICC Profile Data" + fileName); - pde.initCause(c); - throw pde; } } @@ -1149,11 +1087,9 @@ public int getProfileClass() { byte[] theHeader; int theClassSig, theClass; - if (deferralInfo != null) { - return deferralInfo.profileClass; /* Need to have this info for - ICC_ColorSpace without - causing a deferred profile - to be loaded */ + ProfileDeferralInfo info = deferralInfo; + if (info != null) { + return info.profileClass; } theHeader = getData(icSigHead); @@ -1209,12 +1145,11 @@ public int getProfileClass() { * ColorSpace class. */ public int getColorSpaceType() { - if (deferralInfo != null) { - return deferralInfo.colorSpaceType; /* Need to have this info for - ICC_ColorSpace without - causing a deferred profile - to be loaded */ + ProfileDeferralInfo info = deferralInfo; + if (info != null) { + return info.colorSpaceType; } + activate(); return getColorSpaceType(cmmProfile); } @@ -1241,9 +1176,7 @@ static int getColorSpaceType(Profile p) { * ColorSpace class. */ public int getPCSType() { - if (ProfileDeferralMgr.deferring) { - ProfileDeferralMgr.activateProfiles(); - } + activate(); return getPCSType(cmmProfile); } @@ -1305,9 +1238,7 @@ public byte[] getData() { int profileSize; byte[] profileData; - if (ProfileDeferralMgr.deferring) { - ProfileDeferralMgr.activateProfiles(); - } + activate(); PCMM mdl = CMSManager.getModule(); @@ -1340,9 +1271,7 @@ public byte[] getData() { */ public byte[] getData(int tagSignature) { - if (ProfileDeferralMgr.deferring) { - ProfileDeferralMgr.activateProfiles(); - } + activate(); return getData(cmmProfile, tagSignature); } @@ -1388,9 +1317,7 @@ static byte[] getData(Profile p, int tagSignature) { */ public void setData(int tagSignature, byte[] tagData) { - if (ProfileDeferralMgr.deferring) { - ProfileDeferralMgr.activateProfiles(); - } + activate(); CMSManager.getModule().setTagData(cmmProfile, tagSignature, tagData); } @@ -1448,11 +1375,9 @@ public int getNumComponents() { byte[] theHeader; int theColorSpaceSig, theNumComponents; - if (deferralInfo != null) { - return deferralInfo.numComponents; /* Need to have this info for - ICC_ColorSpace without - causing a deferred profile - to be loaded */ + ProfileDeferralInfo info = deferralInfo; + if (info != null) { + return info.numComponents; } theHeader = getData(icSigHead); @@ -1536,6 +1461,24 @@ public int getNumComponents() { return theNumComponents; } + /** + * Returns a stream corresponding to a built-in profile + * specified by fileName. + * If there is no built-in profile with such name, then the method + * returns null. + */ + private static InputStream getStandardProfileInputStream(String fileName) { + return AccessController.doPrivileged( + new PrivilegedAction() { + public InputStream run () { + try { + return + new FileInputStream(getStandardProfileFile(fileName)); + } catch(IOException ex) {return null;} + } + }); + } + /** * Returns a float array of length 3 containing the X, Y, and Z diff --git a/jdk/src/share/classes/java/awt/image/ColorConvertOp.java b/jdk/src/share/classes/java/awt/image/ColorConvertOp.java index 7c188871cfc..28da9b6354c 100644 --- a/jdk/src/share/classes/java/awt/image/ColorConvertOp.java +++ b/jdk/src/share/classes/java/awt/image/ColorConvertOp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. 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 @@ -36,16 +36,18 @@ package java.awt.image; -import java.awt.Point; import java.awt.Graphics2D; -import java.awt.color.*; -import sun.java2d.cmm.ColorTransform; +import java.awt.Point; +import java.awt.RenderingHints; +import java.awt.color.ColorSpace; +import java.awt.color.ICC_ColorSpace; +import java.awt.color.ICC_Profile; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + import sun.java2d.cmm.CMSManager; -import sun.java2d.cmm.ProfileDeferralMgr; +import sun.java2d.cmm.ColorTransform; import sun.java2d.cmm.PCMM; -import java.awt.geom.Rectangle2D; -import java.awt.geom.Point2D; -import java.awt.RenderingHints; /** * This class performs a pixel-by-pixel color conversion of the data in @@ -77,13 +79,6 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp { boolean gotProfiles; float[] srcMinVals, srcMaxVals, dstMinVals, dstMaxVals; - /* the class initializer */ - static { - if (ProfileDeferralMgr.deferring) { - ProfileDeferralMgr.activateProfiles(); - } - } - /** * Constructs a new ColorConvertOp which will convert * from a source color space to a destination color space. diff --git a/jdk/src/share/classes/sun/java2d/cmm/ProfileDeferralInfo.java b/jdk/src/share/classes/sun/java2d/cmm/ProfileDeferralInfo.java index d4667fbc0d1..c36b4e9aff4 100644 --- a/jdk/src/share/classes/sun/java2d/cmm/ProfileDeferralInfo.java +++ b/jdk/src/share/classes/sun/java2d/cmm/ProfileDeferralInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. 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 @@ -25,37 +25,28 @@ package sun.java2d.cmm; -import java.io.InputStream; -import java.io.IOException; - - /** - * A class to pass information about a profile to be loaded from - * a file to the static getInstance(InputStream) method of - * ICC_Profile. Loading of the profile data and initialization - * of the CMM is to be deferred as long as possible. + * A class to pass information about a profile to be loaded from a file to the + * static getInstance(int cspace) method of ICC_Profile. Loading of the profile + * data and initialization of the CMM is to be deferred as long as possible. */ -public class ProfileDeferralInfo extends InputStream { +public final class ProfileDeferralInfo { + + /** + * Need to have this info for ICC_ColorSpace without causing a deferred + * profile to be loaded. + */ + public final int colorSpaceType, numComponents, profileClass; - public int colorSpaceType, numComponents, profileClass; - public String filename; + /** + * The profile file name, such as "CIEXYZ.pf", "sRGB.pf", etc. + */ + public final String filename; public ProfileDeferralInfo(String fn, int type, int ncomp, int pclass) { - - super(); filename = fn; colorSpaceType = type; numComponents = ncomp; profileClass = pclass; } - - - /** - * Implements the abstract read() method of InputStream. - */ - public int read() throws IOException { - - return 0; - } - } diff --git a/jdk/src/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java b/jdk/src/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java index 4d5ae1fa0ee..1b5222ba228 100644 --- a/jdk/src/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java +++ b/jdk/src/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. 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 @@ -35,13 +35,11 @@ package sun.java2d.cmm.lcms; -import java.awt.color.ICC_Profile; import java.awt.color.ProfileDataException; import java.awt.color.CMMException; import java.awt.color.ColorSpace; +import java.awt.color.ICC_Profile; import java.awt.image.BufferedImage; -import java.awt.image.Raster; -import java.awt.image.WritableRaster; import java.awt.image.ColorModel; import java.awt.image.DirectColorModel; import java.awt.image.ComponentColorModel; @@ -52,7 +50,12 @@ import sun.java2d.cmm.*; import sun.java2d.cmm.lcms.*; import static sun.java2d.cmm.lcms.LCMSImageLayout.ImageLayoutException; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; +import sun.java2d.cmm.ColorTransform; + +import static sun.java2d.cmm.lcms.LCMSImageLayout.ImageLayoutException; public class LCMSTransform implements ColorTransform { long ID; @@ -71,13 +74,6 @@ public class LCMSTransform implements ColorTransform { private Object disposerReferent = new Object(); - /* the class initializer */ - static { - if (ProfileDeferralMgr.deferring) { - ProfileDeferralMgr.activateProfiles(); - } - } - public LCMSTransform(ICC_Profile profile, int renderType, int transformType) { diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/LCMS.c b/jdk/src/share/native/sun/java2d/cmm/lcms/LCMS.c index 6b0ab331d9a..086294ce03a 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/LCMS.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/LCMS.c @@ -611,13 +611,28 @@ JNIEXPORT jobject JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileID jclass clsLcmsProfile; jobject cmmProfile; jfieldID fid; + jclass pcls; + jmethodID mid; if (pf == NULL) { return NULL; } - fid = (*env)->GetFieldID (env, - (*env)->GetObjectClass(env, pf), - "cmmProfile", "Lsun/java2d/cmm/Profile;"); + + pcls = (*env)->GetObjectClass(env, pf); + if (pcls == NULL) { + return NULL; + } + mid = (*env)->GetMethodID(env, pcls, "activate", "()V"); + if (mid == NULL) { + return NULL; + } + (*env)->CallVoidMethod(env, pf, mid); + if ((*env)->ExceptionOccurred(env)) { + return NULL; + } + + fid = (*env)->GetFieldID(env, pcls, "cmmProfile", + "Lsun/java2d/cmm/Profile;"); if (fid == NULL) { return NULL; } diff --git a/jdk/test/java/awt/color/ICC_ProfileRGB/MTMatrixAccess.java b/jdk/test/java/awt/color/ICC_ProfileRGB/MTMatrixAccess.java new file mode 100644 index 00000000000..ea3722dbd33 --- /dev/null +++ b/jdk/test/java/awt/color/ICC_ProfileRGB/MTMatrixAccess.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.color.ColorSpace; +import java.awt.color.ICC_Profile; +import java.awt.color.ICC_ProfileRGB; +import java.util.concurrent.CountDownLatch; + +/** + * @test + * @bug 6986863 + * @summary Verifies MT safety of ICC_ProfileRGB#getMatrix method + */ +public final class MTMatrixAccess { + + private static volatile boolean failed; + + public static void main(String[] args) throws Exception { + test((ICC_ProfileRGB) ICC_Profile.getInstance(ColorSpace.CS_sRGB)); + test((ICC_ProfileRGB) ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB)); + } + + private static void test(ICC_ProfileRGB rgb) throws InterruptedException { + Thread[] threads = new Thread[100]; + CountDownLatch go = new CountDownLatch(1); + for (int i = 0; i < threads.length; i++) { + threads[i] = new Thread(() -> { + try { + go.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + try { + rgb.getMatrix(); + } catch (Throwable t) { + t.printStackTrace(); + failed = true; + } + }); + } + for (Thread thread : threads) { + thread.start(); + } + go.countDown(); + for (Thread thread : threads) { + thread.join(); + } + if (failed) { + throw new RuntimeException(); + } + } +} diff --git a/jdk/test/java/awt/color/ProfileActivationDuringPropertyAccess.java b/jdk/test/java/awt/color/ProfileActivationDuringPropertyAccess.java new file mode 100644 index 00000000000..20a3cba41cb --- /dev/null +++ b/jdk/test/java/awt/color/ProfileActivationDuringPropertyAccess.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.color.ColorSpace; +import java.awt.color.ICC_Profile; +import java.util.concurrent.CountDownLatch; + +/** + * @test + * @bug 6986863 + * @summary Verifies MT safety of profile activation while a profile is accessed + */ +public final class ProfileActivationDuringPropertyAccess { + + private static volatile boolean failed; + private static volatile boolean end; + + public static void main(String[] args) throws Exception { + test(ICC_Profile.getInstance(ColorSpace.CS_sRGB)); + test(ICC_Profile.getInstance(ColorSpace.CS_GRAY)); + test(ICC_Profile.getInstance(ColorSpace.CS_CIEXYZ)); + test(ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB)); + test(ICC_Profile.getInstance(ColorSpace.CS_PYCC)); + } + + private static void test(ICC_Profile profile) throws Exception { + Thread[] ts = new Thread[100]; + CountDownLatch latch = new CountDownLatch(ts.length); + for (int i = 0; i < ts.length; i++) { + ts[i] = new Thread(() -> { + latch.countDown(); + try { + latch.await(); + } catch (InterruptedException ex) { + } + try { + while (!end) { + profile.getColorSpaceType(); // try use deferred info + } + } catch (Throwable t) { + t.printStackTrace(); + failed = true; + } + }); + } + for (Thread t : ts) { + t.start(); + } + Thread.sleep(1500); + profile.getPCSType(); // activate profile + end = true; + for (Thread t : ts) { + t.join(); + } + if (failed) { + throw new RuntimeException(); + } + } +}