This is an automated email from the ASF dual-hosted git repository.

bdeggleston pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 9063cea  Fix AbstractBTreePartition locking in java 11
9063cea is described below

commit 9063ceabc9a41825a09db811f74821537dfe726b
Author: Blake Eggleston <[email protected]>
AuthorDate: Tue Mar 12 20:46:37 2019 -0700

    Fix AbstractBTreePartition locking in java 11
    
    Patch by Blake Eggleston; Reviewed by Benedict Elliot Smith for 
CASSANDRA-14607
---
 CHANGES.txt                                        |   1 +
 build.xml                                          |  43 +++-----
 ide/idea-iml-file.xml                              |   1 -
 .../db/partitions/AtomicBTreePartition.java        | 109 +++++++++++----------
 .../db/partitions/AtomicBTreePartitionBase.java    |  65 ------------
 .../db/partitions/AtomicBTreePartitionBase.java    |  81 ---------------
 6 files changed, 71 insertions(+), 229 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 1cfbbfc..131bcb2 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 4.0
+ * Fix AbstractBTreePartition locking in java 11 (CASSANDRA-14607)
  * SimpleClient should pass connection properties as options (CASSANDRA-15056)
  * Set repaired data tracking flag on range reads if enabled (CASSANDRA-15019)
  * Calculate pending ranges for BOOTSTRAP_REPLACE correctly (CASSANDRA-14802)
diff --git a/build.xml b/build.xml
index 8fcb53e..6d79aca 100644
--- a/build.xml
+++ b/build.xml
@@ -35,8 +35,6 @@
     <property name="build.src" value="${basedir}/src"/>
     <property name="build.src.java" value="${basedir}/src/java"/>
     <property name="build.src.antlr" value="${basedir}/src/antlr"/>
-    <property name="build.src.java8" value="${basedir}/src/java8"/>
-    <property name="build.src.java11" value="${basedir}/src/java11"/>
     <property name="build.src.resources" value="${basedir}/src/resources"/>
     <property name="build.src.gen-java" value="${basedir}/src/gen-java"/>
     <property name="build.lib" value="${basedir}/lib"/>
@@ -811,14 +809,13 @@
         depends="maven-ant-tasks-retrieve-build,build-project" 
description="Compile Cassandra classes"/>
     <target name="codecoverage" depends="jacoco-run,jacoco-report" 
description="Create code coverage report"/>
 
-    <target name="_build_java8_only" if="java.version.8">
+    <target name="_build_java8" if="java.version.8">
         <echo message="Compiling only for Java 8 ..."/>
         <javac fork="true"
                debug="true" debuglevel="${debuglevel}" encoding="utf-8"
                destdir="${build.classes.main}" includeantruntime="false" 
source="8" target="8"
                memorymaximumsize="512M">
             <src path="${build.src.java}"/>
-            <src path="${build.src.java8}"/>
             <src path="${build.src.gen-java}"/>
             <compilerarg value="-XDignore.symbol.file"/>
             <classpath>
@@ -826,9 +823,12 @@
             </classpath>
         </javac>
     </target>
-    <target name="_build_multi_java" unless="java.version.8">
+    <target name="_build_java11" unless="java.version.8">
         <!-- Note: we cannot use javac's 'release' option, as that does not 
allow accessing sun.misc.Unsafe nor
         Nashorn's ClassFilter class as any javac modules option is invalid for 
relase 8. -->
+        <fail message="JAVA8_HOME env variable must be set when building with 
java >= 11">
+            <condition><not><isset 
property="env.JAVA8_HOME"/></not></condition>
+        </fail>
         <echo message="Compiling for Java 8 (using 
${env.JAVA8_HOME}/bin/javac) ..."/>
         <javac fork="true"
                debug="true" debuglevel="${debuglevel}" encoding="utf-8"
@@ -836,33 +836,19 @@
                executable="${env.JAVA8_HOME}/bin/javac"
                memorymaximumsize="512M">
             <src path="${build.src.java}"/>
-            <src path="${build.src.java8}"/>
             <src path="${build.src.gen-java}"/>
             <compilerarg value="-XDignore.symbol.file"/>
             <classpath>
                 <path refid="cassandra.classpath"/>
             </classpath>
         </javac>
-        <echo message="Compiling for current java version ..."/>
-        <javac fork="true"
-               debug="true" debuglevel="${debuglevel}" encoding="utf-8"
-               destdir="${build.classes.main}/META-INF/versions/11" 
includeantruntime="false"
-               memorymaximumsize="512M">
-            <src path="${build.src.java11}"/>
-            <compilerarg value="--release"/>
-            <compilerarg value="${release.version}"/>
-            <compilerarg value="-XDignore.symbol.file"/>
-            <classpath>
-                <path refid="cassandra.classpath"/>
-            </classpath>
-        </javac>
     </target>
     <target 
depends="init,gen-cql3-grammar,generate-cql-html,generate-jflex-java"
             name="build-project">
         <echo message="${ant.project.name}: ${ant.file}"/>
         <!-- Order matters! -->
-        <antcall target="_build_java8_only"/>
-        <antcall target="_build_multi_java"/>
+        <antcall target="_build_java8"/>
+        <antcall target="_build_java11"/>
         <antcall target="createVersionPropFile"/>
         <copy todir="${build.classes.main}">
             <fileset dir="${build.src.resources}" />
@@ -1032,9 +1018,6 @@
           <fileset dir="${build.src.java}" defaultexcludes="yes">
             <include name="org/apache/**/*.java"/>
           </fileset>
-          <fileset dir="${build.src.java11}" defaultexcludes="yes">
-            <include name="org/apache/**/*.java"/>
-          </fileset>
           <fileset dir="${build.src.gen-java}" defaultexcludes="yes">
             <include name="org/apache/**/*.java"/>
           </fileset>
@@ -1056,9 +1039,6 @@
         <fileset dir="${build.src.java}" defaultexcludes="yes">
           <include name="org/apache/**/*.java"/>
         </fileset>
-        <fileset dir="${build.src.java11}" defaultexcludes="yes">
-          <include name="org/apache/**/*.java"/>
-        </fileset>
         <fileset dir="${build.src.gen-java}" defaultexcludes="yes">
           <include name="org/apache/**/*.java"/>
         </fileset>
@@ -1234,11 +1214,11 @@
   </target>
 
   <target name="build-test" depends="_main-jar, stress-build, fqltool-build, 
write-poms" description="Compile test classes">
-    <antcall target="_build-test_java8_only"/>
-    <antcall target="_build-test_multi_java"/>
+    <antcall target="_build-test_java8"/>
+    <antcall target="_build-test_java11"/>
   </target>
 
-  <target name="_build-test_java8_only" if="java.version.8">
+  <target name="_build-test_java8" if="java.version.8">
     <javac
      fork="true"
      compiler="modern"
@@ -1266,7 +1246,7 @@
     </copy>
   </target>
 
-  <target name="_build-test_multi_java" unless="java.version.8">
+  <target name="_build-test_java11" unless="java.version.8">
     <javac
      fork="true"
      compiler="modern"
@@ -2032,7 +2012,6 @@
        <echo file=".classpath"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
 <classpath>
   <classpathentry kind="src" path="src/java"/>
-  <classpathentry kind="src" path="src/java8"/>
   <classpathentry kind="src" path="src/resources"/>
   <classpathentry kind="src" path="src/gen-java"/>
   <classpathentry kind="src" path="conf" including="hotspot_compiler"/>
diff --git a/ide/idea-iml-file.xml b/ide/idea-iml-file.xml
index 0045ae6..17d4c5b 100644
--- a/ide/idea-iml-file.xml
+++ b/ide/idea-iml-file.xml
@@ -24,7 +24,6 @@
         <exclude-output />
         <content url="file://$MODULE_DIR$">
             <sourceFolder url="file://$MODULE_DIR$/src/java" 
isTestSource="false" />
-            <sourceFolder url="file://$MODULE_DIR$/src/java8" 
isTestSource="false" />
             <sourceFolder url="file://$MODULE_DIR$/src/gen-java" 
isTestSource="false" generated="true" />
             <sourceFolder url="file://$MODULE_DIR$/src/resources" 
type="java-resource" />
             <sourceFolder url="file://$MODULE_DIR$/tools/stress/src" 
isTestSource="false" />
diff --git 
a/src/java/org/apache/cassandra/db/partitions/AtomicBTreePartition.java 
b/src/java/org/apache/cassandra/db/partitions/AtomicBTreePartition.java
index 8c62642..ec73814 100644
--- a/src/java/org/apache/cassandra/db/partitions/AtomicBTreePartition.java
+++ b/src/java/org/apache/cassandra/db/partitions/AtomicBTreePartition.java
@@ -47,7 +47,7 @@ import org.apache.cassandra.utils.memory.MemtableAllocator;
  * other thread can see the state where only parts but not all rows have
  * been added.
  */
-public final class AtomicBTreePartition extends AtomicBTreePartitionBase
+public final class AtomicBTreePartition extends AbstractBTreePartition
 {
     public static final long EMPTY_SIZE = ObjectSizes.measure(new 
AtomicBTreePartition(null,
                                                                                
        DatabaseDescriptor.getPartitioner().decorateKey(ByteBuffer.allocate(1)),
@@ -108,6 +108,50 @@ public final class AtomicBTreePartition extends 
AtomicBTreePartitionBase
         return true;
     }
 
+    private long[] addAllWithSizeDeltaInternal(RowUpdater updater, 
PartitionUpdate update, UpdateTransaction indexer)
+    {
+        Holder current = ref;
+        updater.ref = current;
+        updater.reset();
+
+        if (!update.deletionInfo().getPartitionDeletion().isLive())
+            
indexer.onPartitionDeletion(update.deletionInfo().getPartitionDeletion());
+
+        if (update.deletionInfo().hasRanges())
+            
update.deletionInfo().rangeIterator(false).forEachRemaining(indexer::onRangeTombstone);
+
+        DeletionInfo deletionInfo;
+        if (update.deletionInfo().mayModify(current.deletionInfo))
+        {
+            if (updater.inputDeletionInfoCopy == null)
+                updater.inputDeletionInfoCopy = 
update.deletionInfo().copy(HeapAllocator.instance);
+
+            deletionInfo = 
current.deletionInfo.mutableCopy().add(updater.inputDeletionInfoCopy);
+            updater.allocated(deletionInfo.unsharedHeapSize() - 
current.deletionInfo.unsharedHeapSize());
+        }
+        else
+        {
+            deletionInfo = current.deletionInfo;
+        }
+
+        RegularAndStaticColumns columns = 
update.columns().mergeTo(current.columns);
+        Row newStatic = update.staticRow();
+        Row staticRow = newStatic.isEmpty()
+                        ? current.staticRow
+                        : (current.staticRow.isEmpty() ? 
updater.apply(newStatic) : updater.apply(current.staticRow, newStatic));
+        Object[] tree = BTree.update(current.tree, 
update.metadata().comparator, update, update.rowCount(), updater);
+        EncodingStats newStats = current.stats.mergeWith(update.stats());
+
+        if (tree != null && refUpdater.compareAndSet(this, current, new 
Holder(columns, tree, deletionInfo, staticRow, newStats)))
+        {
+            updater.finish();
+            return new long[]{ updater.dataSize, updater.colUpdateTimeDelta };
+        }
+        else
+        {
+            return null;
+        }
+    }
     /**
      * Adds a given update to this in-memtable partition.
      *
@@ -117,77 +161,40 @@ public final class AtomicBTreePartition extends 
AtomicBTreePartitionBase
     public long[] addAllWithSizeDelta(final PartitionUpdate update, 
OpOrder.Group writeOp, UpdateTransaction indexer)
     {
         RowUpdater updater = new RowUpdater(this, allocator, writeOp, indexer);
-        DeletionInfo inputDeletionInfoCopy = null;
-        boolean monitorOwned = false;
         try
         {
-            if (usePessimisticLocking())
-            {
-                acquireLock();
-                monitorOwned = true;
-            }
+            boolean shouldLock = usePessimisticLocking();
 
             indexer.start();
 
             while (true)
             {
-                Holder current = ref;
-                updater.ref = current;
-                updater.reset();
-
-                if (!update.deletionInfo().getPartitionDeletion().isLive())
-                    
indexer.onPartitionDeletion(update.deletionInfo().getPartitionDeletion());
-
-                if (update.deletionInfo().hasRanges())
-                    
update.deletionInfo().rangeIterator(false).forEachRemaining(indexer::onRangeTombstone);
-
-                DeletionInfo deletionInfo;
-                if (update.deletionInfo().mayModify(current.deletionInfo))
+                if (shouldLock)
                 {
-                    if (inputDeletionInfoCopy == null)
-                        inputDeletionInfoCopy = 
update.deletionInfo().copy(HeapAllocator.instance);
-
-                    deletionInfo = 
current.deletionInfo.mutableCopy().add(inputDeletionInfoCopy);
-                    updater.allocated(deletionInfo.unsharedHeapSize() - 
current.deletionInfo.unsharedHeapSize());
+                    synchronized (this)
+                    {
+                        long[] result = addAllWithSizeDeltaInternal(updater, 
update, indexer);
+                        if (result != null)
+                            return result;
+                    }
                 }
                 else
                 {
-                    deletionInfo = current.deletionInfo;
-                }
+                    long[] result = addAllWithSizeDeltaInternal(updater, 
update, indexer);
+                    if (result != null)
+                        return result;
 
-                RegularAndStaticColumns columns = 
update.columns().mergeTo(current.columns);
-                Row newStatic = update.staticRow();
-                Row staticRow = newStatic.isEmpty()
-                              ? current.staticRow
-                              : (current.staticRow.isEmpty() ? 
updater.apply(newStatic) : updater.apply(current.staticRow, newStatic));
-                Object[] tree = BTree.update(current.tree, 
update.metadata().comparator, update, update.rowCount(), updater);
-                EncodingStats newStats = 
current.stats.mergeWith(update.stats());
-
-                if (tree != null && refUpdater.compareAndSet(this, current, 
new Holder(columns, tree, deletionInfo, staticRow, newStats)))
-                {
-                    updater.finish();
-                    return new long[]{ updater.dataSize, 
updater.colUpdateTimeDelta };
-                }
-                else if (!monitorOwned)
-                {
-                    boolean shouldLock = usePessimisticLocking();
+                    shouldLock = usePessimisticLocking();
                     if (!shouldLock)
                     {
                         shouldLock = 
updateWastedAllocationTracker(updater.heapSize);
                     }
-                    if (shouldLock)
-                    {
-                        acquireLock();
-                        monitorOwned = true;
-                    }
                 }
             }
         }
         finally
         {
             indexer.commit();
-            if (monitorOwned)
-                releaseLock();
         }
     }
 
@@ -312,6 +319,8 @@ public final class AtomicBTreePartition extends 
AtomicBTreePartitionBase
         long colUpdateTimeDelta = Long.MAX_VALUE;
         List<Row> inserted; // TODO: replace with walk of aborted BTree
 
+        DeletionInfo inputDeletionInfoCopy = null;
+
         private RowUpdater(AtomicBTreePartition updating, MemtableAllocator 
allocator, OpOrder.Group writeOp, UpdateTransaction indexer)
         {
             this.updating = updating;
diff --git 
a/src/java11/org/apache/cassandra/db/partitions/AtomicBTreePartitionBase.java 
b/src/java11/org/apache/cassandra/db/partitions/AtomicBTreePartitionBase.java
deleted file mode 100644
index 56359a2..0000000
--- 
a/src/java11/org/apache/cassandra/db/partitions/AtomicBTreePartitionBase.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.cassandra.db.partitions;
-
-import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
-import java.util.concurrent.locks.ReentrantLock;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.cassandra.db.DecoratedKey;
-
-/**
- * Java 11 version for the partition-locks in {@link AtomicBTreePartition}.
- */
-public abstract class AtomicBTreePartitionBase extends AbstractBTreePartition
-{
-    private static final Logger logger = 
LoggerFactory.getLogger(AtomicBTreePartitionBase.class);
-
-    protected AtomicBTreePartitionBase(DecoratedKey partitionKey)
-    {
-        super(partitionKey);
-    }
-
-    // Replacement for Unsafe.monitorEnter/monitorExit.
-    private volatile ReentrantLock lock;
-    private static final AtomicReferenceFieldUpdater<AtomicBTreePartitionBase, 
ReentrantLock> lockUpdater = 
AtomicReferenceFieldUpdater.newUpdater(AtomicBTreePartitionBase.class, 
ReentrantLock.class, "lock");
-
-    static
-    {
-        logger.info("Initializing Java 11 support for AtomicBTreePartition");
-
-        if (Runtime.version().version().get(0) < 11)
-            throw new RuntimeException("Java 11 required, but found " + 
Runtime.version());
-    }
-
-    protected final void acquireLock()
-    {
-        if (lock == null)
-            lockUpdater.compareAndSet(this, null, new ReentrantLock());
-
-        lock.lock();
-    }
-
-    protected final void releaseLock()
-    {
-        lock.unlock();
-    }
-}
diff --git 
a/src/java8/org/apache/cassandra/db/partitions/AtomicBTreePartitionBase.java 
b/src/java8/org/apache/cassandra/db/partitions/AtomicBTreePartitionBase.java
deleted file mode 100644
index 32209e9..0000000
--- a/src/java8/org/apache/cassandra/db/partitions/AtomicBTreePartitionBase.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.cassandra.db.partitions;
-
-import sun.misc.Unsafe;
-
-import java.lang.reflect.Field;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.cassandra.db.DecoratedKey;
-
-/**
- * Java 8 version for the partition-locks in {@link AtomicBTreePartition}.
- */
-public abstract class AtomicBTreePartitionBase extends AbstractBTreePartition
-{
-    private static final Logger logger = 
LoggerFactory.getLogger(AtomicBTreePartitionBase.class);
-    private static final Unsafe unsafe;
-
-    static
-    {
-        logger.info("Initializing Java 8 support for AtomicBTreePartition");
-
-        if (!System.getProperty("java.version").startsWith("1.8.0"))
-            throw new RuntimeException("Java 8 required, but running " + 
System.getProperty("java.version"));
-
-        try
-        {
-            // Safety... in case someone builds only on Java 8 but runs on 
Java 11...
-            sun.misc.Unsafe.class.getDeclaredMethod("monitorEnter", 
Object.class);
-            sun.misc.Unsafe.class.getDeclaredMethod("monitorExit", 
Object.class);
-
-            Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
-            field.setAccessible(true);
-            unsafe = (sun.misc.Unsafe) field.get(null);
-        }
-        catch (NoSuchFieldException | NoSuchMethodException e)
-        {
-            throw new RuntimeException("This build of Cassandra has no support 
for Java 11 as only Java 8 was available during the build. This should never 
happen.");
-        }
-        catch (Exception e)
-        {
-            throw new AssertionError(e);
-        }
-    }
-
-    protected AtomicBTreePartitionBase(DecoratedKey partitionKey)
-    {
-        super(partitionKey);
-    }
-
-    protected final void acquireLock()
-    {
-        if (unsafe != null)
-            unsafe.monitorEnter(this);
-    }
-
-    protected final void releaseLock()
-    {
-        if (unsafe != null)
-            unsafe.monitorExit(this);
-    }
-}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to