diff options
Diffstat (limited to 'src/main/java/com')
2 files changed, 63 insertions, 21 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/strings/FastStringCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/strings/FastStringCodec.java index e763f70b50..f4b44c1568 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/strings/FastStringCodec.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/strings/FastStringCodec.java @@ -14,6 +14,7 @@ package com.google.devtools.build.lib.skyframe.serialization.strings; +import com.google.common.base.Preconditions; import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec; import com.google.protobuf.CodedInputStream; import com.google.protobuf.CodedOutputStream; @@ -29,9 +30,14 @@ import sun.misc.Unsafe; * Similar to {@link StringCodec}, except with deserialization optimized for ascii data. It can * still handle UTF-8, though less efficiently than {@link StringCodec}. Should be used when the * majority of the data passing through will be ascii. + * + * <p>Users <b>MUST</b> check if this class is usable by checking {@link #isAvailable()}. */ class FastStringCodec implements ObjectCodec<String> { + /** Sentinel value for missing {@link #STRING_VALUE_OFFSET}. */ + private static final long UNSUPPORTED_STRING_VALUE_OFFSET = -1; + private static final Unsafe theUnsafe; private static final long STRING_VALUE_OFFSET; @@ -39,21 +45,16 @@ class FastStringCodec implements ObjectCodec<String> { static { theUnsafe = getUnsafe(); - try { - // String's 'value' field stores its char[]. If this field changes name or type then the - // reflective check below will fail. We can reasonably expect our approach to be stable for - // now, but things are likely to change in java 9, hopefully in a way which obsoletes this - // optimization. - Field valueField = String.class.getDeclaredField("value"); - Class<?> valueFieldType = valueField.getType(); - if (!valueFieldType.equals(char[].class)) { - throw new AssertionError( - "Expected String's value field to be char[], but was " + valueFieldType); - } - STRING_VALUE_OFFSET = theUnsafe.objectFieldOffset(valueField); - } catch (NoSuchFieldException | SecurityException e) { - throw new AssertionError("Failed to find String's 'value' offset", e); - } + STRING_VALUE_OFFSET = getStringValueOffset(); + } + + /** Returns whether or not this implementation is supported. */ + static boolean isAvailable() { + return STRING_VALUE_OFFSET != UNSUPPORTED_STRING_VALUE_OFFSET; + } + + FastStringCodec() { + Preconditions.checkState(isAvailable(), "FastStringCodec isn't available!"); } @Override @@ -137,4 +138,21 @@ class FastStringCodec implements ObjectCodec<String> { throw new AssertionError("Unable to get sun.misc.Unsafe", pae); } } + + private static long getStringValueOffset() { + try { + // We expect a String's value field to be a char[] - if that's not the case then we're + // probably on a more modern JDK and this optimization isn't available. + Field valueField = String.class.getDeclaredField("value"); + Class<?> valueFieldType = valueField.getType(); + if (valueFieldType.equals(char[].class)) { + return theUnsafe.objectFieldOffset(valueField); + } else { + // value was of a different type, bail. + return UNSUPPORTED_STRING_VALUE_OFFSET; + } + } catch (NoSuchFieldException | SecurityException e) { + throw new AssertionError("Failed to find String's 'value' field/offset", e); + } + } } diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/strings/StringCodecs.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/strings/StringCodecs.java index 24f36ecb07..60e5f29f87 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/strings/StringCodecs.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/strings/StringCodecs.java @@ -15,22 +15,46 @@ package com.google.devtools.build.lib.skyframe.serialization.strings; import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec; +import java.util.logging.Logger; /** Utility for accessing (potentially platform-specific) {@link String} {@link ObjectCodec}s. */ public final class StringCodecs { - private static final FastStringCodec fastStringCodec = new FastStringCodec(); - private static final StringCodec stringCodec = new StringCodec(); + private static final Logger logger = Logger.getLogger(StringCodecs.class.getName()); + + private static final StringCodec stringCodec; + private static final ObjectCodec<String> asciiOptimized; + + static { + stringCodec = new StringCodec(); + if (FastStringCodec.isAvailable()) { + asciiOptimized = new FastStringCodec(); + } else { + logger.warning("Optimized string deserialization unavailable"); + asciiOptimized = stringCodec; + } + } private StringCodecs() {} /** - * Returns singleton instance optimized for almost-always ASCII data. This instance can still - * serialize/deserialize UTF-8 data, but with potentially worse performance than - * {@link #simple()}. + * Returns whether or not optimized codecs are available. Exposed so users can check at runtime + * if the expected optimizations are applied. + */ + public static boolean supportsOptimizedAscii() { + return asciiOptimized instanceof FastStringCodec; + } + + /** + * Returns singleton instance optimized for almost-always ASCII data, if supported. Otherwise, + * returns a functional, but not optimized implementation. To tell if the optimized version is + * supported see {@link #supportsOptimizedAscii()}. + * + * <p>Note that when optimized, this instance can still serialize/deserialize UTF-8 data, but with + * potentially worse performance than {@link #simple()}. */ public static ObjectCodec<String> asciiOptimized() { - return fastStringCodec; + return asciiOptimized; } /** |