8260695: The java.awt.color.ICC_Profile#getData/getData(int) are not thread safe
Reviewed-by: azvegint, aivanov
This commit is contained in:
parent
a30fb4fc68
commit
a7e2e80ff4
@ -1131,22 +1131,8 @@ public class ICC_Profile implements Serializable {
|
||||
* @see #setData(int, byte[])
|
||||
*/
|
||||
public byte[] getData() {
|
||||
int profileSize;
|
||||
byte[] profileData;
|
||||
|
||||
activate();
|
||||
|
||||
PCMM mdl = CMSManager.getModule();
|
||||
|
||||
/* get the number of bytes needed for this profile */
|
||||
profileSize = mdl.getProfileSize(cmmProfile);
|
||||
|
||||
profileData = new byte [profileSize];
|
||||
|
||||
/* get the data for the profile */
|
||||
mdl.getProfileData(cmmProfile, profileData);
|
||||
|
||||
return profileData;
|
||||
return CMSManager.getModule().getProfileData(cmmProfile);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1170,25 +1156,12 @@ public class ICC_Profile implements Serializable {
|
||||
}
|
||||
|
||||
|
||||
static byte[] getData(Profile p, int tagSignature) {
|
||||
int tagSize;
|
||||
byte[] tagData;
|
||||
|
||||
private static byte[] getData(Profile p, int tagSignature) {
|
||||
try {
|
||||
PCMM mdl = CMSManager.getModule();
|
||||
|
||||
/* get the number of bytes needed for this tag */
|
||||
tagSize = mdl.getTagSize(p, tagSignature);
|
||||
|
||||
tagData = new byte[tagSize]; /* get an array for the tag */
|
||||
|
||||
/* get the tag's data */
|
||||
mdl.getTagData(p, tagSignature, tagData);
|
||||
} catch(CMMException c) {
|
||||
tagData = null;
|
||||
return CMSManager.getModule().getTagData(p, tagSignature);
|
||||
} catch (CMMException c) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return tagData;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,33 +92,19 @@ public final class CMSManager {
|
||||
return p;
|
||||
}
|
||||
|
||||
public int getProfileSize(Profile p) {
|
||||
System.err.print(cName + ".getProfileSize(ID=" + p + ")");
|
||||
int size = tcmm.getProfileSize(p);
|
||||
System.err.println("=" + size);
|
||||
return size;
|
||||
}
|
||||
|
||||
public void getProfileData(Profile p, byte[] data) {
|
||||
public byte[] getProfileData(Profile p) {
|
||||
System.err.print(cName + ".getProfileData(ID=" + p + ") ");
|
||||
byte[] data = tcmm.getProfileData(p);
|
||||
System.err.println("requested " + data.length + " byte(s)");
|
||||
tcmm.getProfileData(p, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
public int getTagSize(Profile p, int tagSignature) {
|
||||
System.err.printf(cName + ".getTagSize(ID=%x, TagSig=%s)",
|
||||
p, signatureToString(tagSignature));
|
||||
int size = tcmm.getTagSize(p, tagSignature);
|
||||
System.err.println("=" + size);
|
||||
return size;
|
||||
}
|
||||
|
||||
public void getTagData(Profile p, int tagSignature,
|
||||
byte[] data) {
|
||||
public byte[] getTagData(Profile p, int tagSignature) {
|
||||
System.err.printf(cName + ".getTagData(ID=%x, TagSig=%s)",
|
||||
p, signatureToString(tagSignature));
|
||||
byte[] data = tcmm.getTagData(p, tagSignature);
|
||||
System.err.println(" requested " + data.length + " byte(s)");
|
||||
tcmm.getTagData(p, tagSignature, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setTagData(Profile p, int tagSignature,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2006, 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
|
||||
@ -33,10 +33,8 @@ public interface PCMM {
|
||||
|
||||
/* methods invoked from ICC_Profile */
|
||||
public Profile loadProfile(byte[] data);
|
||||
public int getProfileSize(Profile p);
|
||||
public void getProfileData(Profile p, byte[] data);
|
||||
public void getTagData(Profile p, int tagSignature, byte[] data);
|
||||
public int getTagSize(Profile p, int tagSignature);
|
||||
public byte[] getProfileData(Profile p);
|
||||
public byte[] getTagData(Profile p, int tagSignature);
|
||||
public void setTagData(Profile p, int tagSignature, byte[] data);
|
||||
|
||||
/* methods for creating ColorTransforms */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 2020, 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
|
||||
@ -58,45 +58,23 @@ public class LCMS implements PCMM {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProfileSize(final Profile p) {
|
||||
synchronized (p) {
|
||||
return getProfileSizeNative(getLcmsProfile(p).getLcmsPtr());
|
||||
public byte[] getProfileData(final Profile p) {
|
||||
LCMSProfile lcmsProfile = getLcmsProfile(p);
|
||||
synchronized (lcmsProfile) {
|
||||
return getProfileDataNative(lcmsProfile.getLcmsPtr());
|
||||
}
|
||||
}
|
||||
|
||||
private native int getProfileSizeNative(long ptr);
|
||||
|
||||
@Override
|
||||
public void getProfileData(final Profile p, byte[] data) {
|
||||
synchronized (p) {
|
||||
getProfileDataNative(getLcmsProfile(p).getLcmsPtr(), data);
|
||||
}
|
||||
}
|
||||
|
||||
private native void getProfileDataNative(long ptr, byte[] data);
|
||||
|
||||
@Override
|
||||
public int getTagSize(Profile p, int tagSignature) {
|
||||
final LCMSProfile profile = getLcmsProfile(p);
|
||||
|
||||
synchronized (profile) {
|
||||
TagData t = profile.getTag(tagSignature);
|
||||
return t == null ? 0 : t.getSize();
|
||||
}
|
||||
}
|
||||
private native byte[] getProfileDataNative(long ptr);
|
||||
|
||||
static native byte[] getTagNative(long profileID, int signature);
|
||||
|
||||
@Override
|
||||
public void getTagData(Profile p, int tagSignature, byte[] data)
|
||||
{
|
||||
final LCMSProfile profile = getLcmsProfile(p);
|
||||
|
||||
synchronized (profile) {
|
||||
TagData t = profile.getTag(tagSignature);
|
||||
if (t != null) {
|
||||
t.copyDataTo(data);
|
||||
}
|
||||
public byte[] getTagData(Profile p, int tagSignature) {
|
||||
final LCMSProfile lcmsProfile = getLcmsProfile(p);
|
||||
synchronized (lcmsProfile) {
|
||||
TagData t = lcmsProfile.getTag(tagSignature);
|
||||
return t != null ? t.getData() : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 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
|
||||
@ -25,9 +25,8 @@
|
||||
|
||||
package sun.java2d.cmm.lcms;
|
||||
|
||||
import java.awt.color.CMMException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
||||
import sun.java2d.cmm.Profile;
|
||||
|
||||
final class LCMSProfile extends Profile {
|
||||
@ -55,55 +54,40 @@ final class LCMSProfile extends Profile {
|
||||
tagCache.clear();
|
||||
}
|
||||
|
||||
static class TagCache {
|
||||
final LCMSProfile profile;
|
||||
private HashMap<Integer, TagData> tags;
|
||||
private static final class TagCache {
|
||||
private final LCMSProfile profile;
|
||||
private final HashMap<Integer, TagData> tags = new HashMap<>();
|
||||
|
||||
TagCache(LCMSProfile p) {
|
||||
private TagCache(LCMSProfile p) {
|
||||
profile = p;
|
||||
tags = new HashMap<>();
|
||||
}
|
||||
|
||||
TagData getTag(int sig) {
|
||||
private TagData getTag(int sig) {
|
||||
TagData t = tags.get(sig);
|
||||
if (t == null) {
|
||||
byte[] tagData = LCMS.getTagNative(profile.getNativePtr(), sig);
|
||||
if (tagData != null) {
|
||||
t = new TagData(sig, tagData);
|
||||
t = new TagData(tagData);
|
||||
tags.put(sig, t);
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
private void clear() {
|
||||
tags.clear();
|
||||
}
|
||||
}
|
||||
|
||||
static class TagData {
|
||||
private int signature;
|
||||
private byte[] data;
|
||||
static final class TagData {
|
||||
private final byte[] data;
|
||||
|
||||
TagData(int sig, byte[] data) {
|
||||
this.signature = sig;
|
||||
TagData(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
int getSize() {
|
||||
return data.length;
|
||||
}
|
||||
|
||||
byte[] getData() {
|
||||
return Arrays.copyOf(data, data.length);
|
||||
}
|
||||
|
||||
void copyDataTo(byte[] dst) {
|
||||
System.arraycopy(data, 0, dst, 0, data.length);
|
||||
}
|
||||
|
||||
int getSignature() {
|
||||
return signature;
|
||||
return data.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -311,68 +311,44 @@ JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_loadProfileNative
|
||||
|
||||
/*
|
||||
* Class: sun_java2d_cmm_lcms_LCMS
|
||||
* Method: getProfileSizeNative
|
||||
* Signature: (J)I
|
||||
* Method: getProfileDataNative
|
||||
* Signature: (J[B)V
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileSizeNative
|
||||
JNIEXPORT jbyteArray JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileDataNative
|
||||
(JNIEnv *env, jobject obj, jlong id)
|
||||
{
|
||||
lcmsProfile_p sProf = (lcmsProfile_p)jlong_to_ptr(id);
|
||||
cmsUInt32Number pfSize = 0;
|
||||
|
||||
if (cmsSaveProfileToMem(sProf->pf, NULL, &pfSize) && ((jint)pfSize > 0)) {
|
||||
return (jint)pfSize;
|
||||
} else {
|
||||
JNU_ThrowByName(env, "java/awt/color/CMMException",
|
||||
"Can not access specified profile.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_java2d_cmm_lcms_LCMS
|
||||
* Method: getProfileDataNative
|
||||
* Signature: (J[B)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileDataNative
|
||||
(JNIEnv *env, jobject obj, jlong id, jbyteArray data)
|
||||
{
|
||||
lcmsProfile_p sProf = (lcmsProfile_p)jlong_to_ptr(id);
|
||||
jint size;
|
||||
jbyte* dataArray;
|
||||
cmsUInt32Number pfSize = 0;
|
||||
cmsBool status;
|
||||
|
||||
// determine actual profile size
|
||||
if (!cmsSaveProfileToMem(sProf->pf, NULL, &pfSize)) {
|
||||
JNU_ThrowByName(env, "java/awt/color/CMMException",
|
||||
"Can not access specified profile.");
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// verify java buffer capacity
|
||||
size = (*env)->GetArrayLength(env, data);
|
||||
if (0 >= size || pfSize > (cmsUInt32Number)size) {
|
||||
JNU_ThrowByName(env, "java/awt/color/CMMException",
|
||||
"Insufficient buffer capacity.");
|
||||
return;
|
||||
jbyteArray data = (*env)->NewByteArray(env, pfSize);
|
||||
if (data == NULL) {
|
||||
// An exception should have already been thrown.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dataArray = (*env)->GetByteArrayElements (env, data, 0);
|
||||
jbyte* dataArray = (*env)->GetByteArrayElements(env, data, 0);
|
||||
if (dataArray == NULL) {
|
||||
// An exception should have already been thrown.
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
status = cmsSaveProfileToMem(sProf->pf, dataArray, &pfSize);
|
||||
cmsBool status = cmsSaveProfileToMem(sProf->pf, dataArray, &pfSize);
|
||||
|
||||
(*env)->ReleaseByteArrayElements (env, data, dataArray, 0);
|
||||
(*env)->ReleaseByteArrayElements(env, data, dataArray, 0);
|
||||
|
||||
if (!status) {
|
||||
JNU_ThrowByName(env, "java/awt/color/CMMException",
|
||||
"Can not access specified profile.");
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/* Get profile header info */
|
||||
|
133
test/jdk/java/awt/color/ICC_Profile/MTGetData.java
Normal file
133
test/jdk/java/awt/color/ICC_Profile/MTGetData.java
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8260695
|
||||
* @summary Verifies MT safety of ICC_Profile#getData
|
||||
*/
|
||||
public final class MTGetData {
|
||||
|
||||
static volatile long endtime;
|
||||
static volatile boolean failed;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ICC_Profile[] profiles = {
|
||||
ICC_Profile.getInstance(ColorSpace.CS_sRGB),
|
||||
ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB),
|
||||
ICC_Profile.getInstance(ColorSpace.CS_CIEXYZ),
|
||||
ICC_Profile.getInstance(ColorSpace.CS_PYCC),
|
||||
ICC_Profile.getInstance(ColorSpace.CS_GRAY)
|
||||
};
|
||||
|
||||
List<Integer> tags = new ArrayList<>();
|
||||
for (Field field : ICC_Profile.class.getDeclaredFields()) {
|
||||
if (Modifier.isStatic(field.getModifiers())
|
||||
&& Modifier.isPublic(field.getModifiers())
|
||||
&& Modifier.isFinal(field.getModifiers())
|
||||
&& field.getType() == int.class) {
|
||||
tags.add(field.getInt(null));
|
||||
}
|
||||
}
|
||||
|
||||
List<Thread> tasks = new ArrayList<>();
|
||||
for (int tag : tags) {
|
||||
for (ICC_Profile profile1 : profiles) {
|
||||
for (ICC_Profile profile2 : profiles) {
|
||||
byte[] d1 = profile1.getData(tag);
|
||||
byte[] d2 = profile2.getData(tag);
|
||||
if (d1 == null || d2 == null || d1.length == d2.length) {
|
||||
continue;
|
||||
}
|
||||
tasks.add(new Thread(() -> {
|
||||
try {
|
||||
test(profile1.getData(), d1, d2, tag);
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
failed = true;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Will run the test no more than 15 seconds
|
||||
endtime = System.nanoTime() + TimeUnit.SECONDS.toNanos(15);
|
||||
for (Thread t : tasks) {
|
||||
t.start();
|
||||
}
|
||||
for (Thread t : tasks) {
|
||||
t.join();
|
||||
}
|
||||
if (failed) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
private static void test(byte[] all, byte[] data1, byte[] data2, int tag)
|
||||
throws Exception {
|
||||
ICC_Profile icc = ICC_Profile.getInstance(all);
|
||||
|
||||
Thread swap = new Thread(() -> {
|
||||
try {
|
||||
while (!isComplete()) {
|
||||
icc.setData(tag, data1);
|
||||
icc.setData(tag, data2);
|
||||
}
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
failed = true;
|
||||
}
|
||||
});
|
||||
|
||||
Thread fetch = new Thread(() -> {
|
||||
try {
|
||||
while (!isComplete()) {
|
||||
icc.getData(tag);
|
||||
icc.getData();
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
failed = true;
|
||||
}
|
||||
});
|
||||
|
||||
swap.start();
|
||||
fetch.start();
|
||||
swap.join();
|
||||
fetch.join();
|
||||
}
|
||||
|
||||
private static boolean isComplete() {
|
||||
return endtime - System.nanoTime() < 0 || failed;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user