From 39324a227871904e6f9a68c50e49ce8a0d1bc7ec Mon Sep 17 00:00:00 2001 From: Matthew de Detrich Date: Mon, 1 May 2023 10:31:06 +0200 Subject: [PATCH] Enable inliner for Scala 2 --- .../apache/pekko/actor/AbstractActorRef.java | 34 --- .../actor/dungeon/AbstractActorCell.java | 47 ---- .../pekko/dispatch/AbstractMailbox.java | 30 --- .../pekko/pattern/AbstractCircuitBreaker.java | 34 --- .../pattern/AbstractPromiseActorRef.java | 34 --- .../java/org/apache/pekko/util/Unsafe.java | 211 ------------------ .../org/apache/pekko/util/ByteIterator.scala | 2 +- .../org/apache/pekko/util/ByteIterator.scala | 4 +- .../org/apache/pekko/util/ByteIterator.scala | 16 +- .../org/apache/pekko/util/ByteString.scala | 6 +- .../apache/pekko/actor/AbstractActorRef.scala | 32 +++ .../actor/dungeon/AbstractActorCell.scala | 39 ++++ .../pekko/dispatch/AbstractMailbox.scala | 32 +++ .../pattern/AbstractCircuitBreaker.scala | 32 +++ .../pattern/AbstractPromiseActorRef.scala | 32 +++ .../apache/pekko/pattern/CircuitBreaker.scala | 8 +- .../scala/org/apache/pekko/util/Unsafe.scala | 186 +++++++++++++++ project/PekkoInlinePlugin.scala | 48 ++++ 18 files changed, 419 insertions(+), 408 deletions(-) delete mode 100644 actor/src/main/java/org/apache/pekko/actor/AbstractActorRef.java delete mode 100644 actor/src/main/java/org/apache/pekko/actor/dungeon/AbstractActorCell.java delete mode 100644 actor/src/main/java/org/apache/pekko/dispatch/AbstractMailbox.java delete mode 100644 actor/src/main/java/org/apache/pekko/pattern/AbstractCircuitBreaker.java delete mode 100644 actor/src/main/java/org/apache/pekko/pattern/AbstractPromiseActorRef.java delete mode 100644 actor/src/main/java/org/apache/pekko/util/Unsafe.java create mode 100644 actor/src/main/scala/org/apache/pekko/actor/AbstractActorRef.scala create mode 100644 actor/src/main/scala/org/apache/pekko/actor/dungeon/AbstractActorCell.scala create mode 100644 actor/src/main/scala/org/apache/pekko/dispatch/AbstractMailbox.scala create mode 100644 actor/src/main/scala/org/apache/pekko/pattern/AbstractCircuitBreaker.scala create mode 100644 actor/src/main/scala/org/apache/pekko/pattern/AbstractPromiseActorRef.scala create mode 100644 actor/src/main/scala/org/apache/pekko/util/Unsafe.scala create mode 100644 project/PekkoInlinePlugin.scala diff --git a/actor/src/main/java/org/apache/pekko/actor/AbstractActorRef.java b/actor/src/main/java/org/apache/pekko/actor/AbstractActorRef.java deleted file mode 100644 index 46b800723dd..00000000000 --- a/actor/src/main/java/org/apache/pekko/actor/AbstractActorRef.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * license agreements; and to You under the Apache License, version 2.0: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * This file is part of the Apache Pekko project, derived from Akka. - */ - -/* - * Copyright (C) 2009-2022 Lightbend Inc. - */ - -package org.apache.pekko.actor; - -import org.apache.pekko.util.Unsafe; - -final class AbstractActorRef { - static final long cellOffset; - static final long lookupOffset; - - static { - try { - cellOffset = - Unsafe.instance.objectFieldOffset( - RepointableActorRef.class.getDeclaredField("_cellDoNotCallMeDirectly")); - lookupOffset = - Unsafe.instance.objectFieldOffset( - RepointableActorRef.class.getDeclaredField("_lookupDoNotCallMeDirectly")); - } catch (Throwable t) { - throw new ExceptionInInitializerError(t); - } - } -} diff --git a/actor/src/main/java/org/apache/pekko/actor/dungeon/AbstractActorCell.java b/actor/src/main/java/org/apache/pekko/actor/dungeon/AbstractActorCell.java deleted file mode 100644 index 0327c535e22..00000000000 --- a/actor/src/main/java/org/apache/pekko/actor/dungeon/AbstractActorCell.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * license agreements; and to You under the Apache License, version 2.0: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * This file is part of the Apache Pekko project, derived from Akka. - */ - -/* - * Copyright (C) 2009-2022 Lightbend Inc. - */ - -package org.apache.pekko.actor.dungeon; - -import org.apache.pekko.actor.ActorCell; -import org.apache.pekko.util.Unsafe; - -final class AbstractActorCell { - static final long mailboxOffset; - static final long childrenOffset; - static final long nextNameOffset; - static final long functionRefsOffset; - - static { - try { - mailboxOffset = - Unsafe.instance.objectFieldOffset( - ActorCell.class.getDeclaredField( - "org$apache$pekko$actor$dungeon$Dispatch$$_mailboxDoNotCallMeDirectly")); - childrenOffset = - Unsafe.instance.objectFieldOffset( - ActorCell.class.getDeclaredField( - "org$apache$pekko$actor$dungeon$Children$$_childrenRefsDoNotCallMeDirectly")); - nextNameOffset = - Unsafe.instance.objectFieldOffset( - ActorCell.class.getDeclaredField( - "org$apache$pekko$actor$dungeon$Children$$_nextNameDoNotCallMeDirectly")); - functionRefsOffset = - Unsafe.instance.objectFieldOffset( - ActorCell.class.getDeclaredField( - "org$apache$pekko$actor$dungeon$Children$$_functionRefsDoNotCallMeDirectly")); - } catch (Throwable t) { - throw new ExceptionInInitializerError(t); - } - } -} diff --git a/actor/src/main/java/org/apache/pekko/dispatch/AbstractMailbox.java b/actor/src/main/java/org/apache/pekko/dispatch/AbstractMailbox.java deleted file mode 100644 index 48487593f00..00000000000 --- a/actor/src/main/java/org/apache/pekko/dispatch/AbstractMailbox.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * license agreements; and to You under the Apache License, version 2.0: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * This file is part of the Apache Pekko project, derived from Akka. - */ - -/* - * Copyright (C) 2009-2022 Lightbend Inc. - */ - -package org.apache.pekko.dispatch; - -import org.apache.pekko.util.Unsafe; - -final class AbstractMailbox { - final static long mailboxStatusOffset; - final static long systemMessageOffset; - - static { - try { - mailboxStatusOffset = Unsafe.instance.objectFieldOffset(Mailbox.class.getDeclaredField("_statusDoNotCallMeDirectly")); - systemMessageOffset = Unsafe.instance.objectFieldOffset(Mailbox.class.getDeclaredField("_systemQueueDoNotCallMeDirectly")); - } catch(Throwable t){ - throw new ExceptionInInitializerError(t); - } - } -} diff --git a/actor/src/main/java/org/apache/pekko/pattern/AbstractCircuitBreaker.java b/actor/src/main/java/org/apache/pekko/pattern/AbstractCircuitBreaker.java deleted file mode 100644 index 6cb3726d033..00000000000 --- a/actor/src/main/java/org/apache/pekko/pattern/AbstractCircuitBreaker.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * license agreements; and to You under the Apache License, version 2.0: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * This file is part of the Apache Pekko project, derived from Akka. - */ - -/* - * Copyright (C) 2009-2022 Lightbend Inc. - */ - -package org.apache.pekko.pattern; - -import org.apache.pekko.util.Unsafe; - -class AbstractCircuitBreaker { - protected static final long stateOffset; - protected static final long resetTimeoutOffset; - - static { - try { - stateOffset = - Unsafe.instance.objectFieldOffset( - CircuitBreaker.class.getDeclaredField("_currentStateDoNotCallMeDirectly")); - resetTimeoutOffset = - Unsafe.instance.objectFieldOffset( - CircuitBreaker.class.getDeclaredField("_currentResetTimeoutDoNotCallMeDirectly")); - } catch (Throwable t) { - throw new ExceptionInInitializerError(t); - } - } -} diff --git a/actor/src/main/java/org/apache/pekko/pattern/AbstractPromiseActorRef.java b/actor/src/main/java/org/apache/pekko/pattern/AbstractPromiseActorRef.java deleted file mode 100644 index fc9e47467a9..00000000000 --- a/actor/src/main/java/org/apache/pekko/pattern/AbstractPromiseActorRef.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * license agreements; and to You under the Apache License, version 2.0: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * This file is part of the Apache Pekko project, derived from Akka. - */ - -/* - * Copyright (C) 2009-2022 Lightbend Inc. - */ - -package org.apache.pekko.pattern; - -import org.apache.pekko.util.Unsafe; - -final class AbstractPromiseActorRef { - static final long stateOffset; - static final long watchedByOffset; - - static { - try { - stateOffset = - Unsafe.instance.objectFieldOffset( - PromiseActorRef.class.getDeclaredField("_stateDoNotCallMeDirectly")); - watchedByOffset = - Unsafe.instance.objectFieldOffset( - PromiseActorRef.class.getDeclaredField("_watchedByDoNotCallMeDirectly")); - } catch (Throwable t) { - throw new ExceptionInInitializerError(t); - } - } -} diff --git a/actor/src/main/java/org/apache/pekko/util/Unsafe.java b/actor/src/main/java/org/apache/pekko/util/Unsafe.java deleted file mode 100644 index 8352f81d5e1..00000000000 --- a/actor/src/main/java/org/apache/pekko/util/Unsafe.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * license agreements; and to You under the Apache License, version 2.0: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * This file is part of the Apache Pekko project, derived from Akka. - */ - -/* - * Copyright (C) 2009-2022 Lightbend Inc. - */ - -package org.apache.pekko.util; - -import org.apache.pekko.annotation.InternalApi; - -import java.lang.reflect.Field; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; - -/** INTERNAL API */ -@InternalApi -public final class Unsafe { - public static final sun.misc.Unsafe instance; - - private static final long stringValueFieldOffset; - private static final boolean isJavaVersion9Plus; - private static final int copyUSAsciiStrToBytesAlgorithm; - - static { - try { - sun.misc.Unsafe found = null; - for (Field field : sun.misc.Unsafe.class.getDeclaredFields()) { - if (field.getType() == sun.misc.Unsafe.class) { - field.setAccessible(true); - found = (sun.misc.Unsafe) field.get(null); - break; - } - } - if (found == null) throw new IllegalStateException("Can't find instance of sun.misc.Unsafe"); - else instance = found; - - long fo; - try { - fo = instance.objectFieldOffset(String.class.getDeclaredField("value")); - } catch (NoSuchFieldException nsfe) { - // The platform's implementation of String doesn't have a 'value' field, so we have to use - // algorithm 0 - fo = -1; - } - stringValueFieldOffset = fo; - - isJavaVersion9Plus = isIsJavaVersion9Plus(); - - if (stringValueFieldOffset > -1) { - // Select optimization algorithm for `copyUSAciiBytesToStr`. - // For example algorithm 1 will fail with JDK 11 on ARM32 (Raspberry Pi), - // and therefore algorithm 0 is selected on that architecture. - String testStr = "abc"; - if (isJavaVersion9Plus && testUSAsciiStrToBytesAlgorithm1(testStr)) - copyUSAsciiStrToBytesAlgorithm = 1; - else if (testUSAsciiStrToBytesAlgorithm2(testStr)) copyUSAsciiStrToBytesAlgorithm = 2; - else copyUSAsciiStrToBytesAlgorithm = 0; - } else - // We know so little about the platform's String implementation that we have - // no choice but to select algorithm 0 - copyUSAsciiStrToBytesAlgorithm = 0; - } catch (Throwable t) { - throw new ExceptionInInitializerError(t); - } - } - - static boolean isIsJavaVersion9Plus() { - // See Oracle section 1.5.3 at: - // https://docs.oracle.com/javase/8/docs/technotes/guides/versioning/spec/versioning2.html - final int[] version = - Arrays.stream(System.getProperty("java.specification.version").split("\\.")) - .mapToInt(Integer::parseInt) - .toArray(); - final int javaVersion = version[0] == 1 ? version[1] : version[0]; - return javaVersion > 8; - } - - static boolean testUSAsciiStrToBytesAlgorithm0(String str) { - try { - byte[] bytes = new byte[str.length()]; - - // copy of implementation in copyUSAciiBytesToStr - byte[] strBytes = str.getBytes(StandardCharsets.US_ASCII); - System.arraycopy(strBytes, 0, bytes, 0, str.length()); - // end copy - - String result = copyUSAciiBytesToStr(str.length(), bytes); - return str.equals(result); - } catch (Throwable all) { - return false; - } - } - - static boolean testUSAsciiStrToBytesAlgorithm1(String str) { - try { - byte[] bytes = new byte[str.length()]; - - // copy of implementation in copyUSAciiBytesToStr - final byte[] chars = (byte[]) instance.getObject(str, stringValueFieldOffset); - System.arraycopy(chars, 0, bytes, 0, str.length()); - // end copy - - String result = copyUSAciiBytesToStr(str.length(), bytes); - return str.equals(result); - } catch (Throwable all) { - return false; - } - } - - static boolean testUSAsciiStrToBytesAlgorithm2(String str) { - try { - byte[] bytes = new byte[str.length()]; - - // copy of implementation in copyUSAciiBytesToStr - final char[] chars = (char[]) instance.getObject(str, stringValueFieldOffset); - int i = 0; - while (i < str.length()) { - bytes[i] = (byte) chars[i++]; - } - // end copy - - String result = copyUSAciiBytesToStr(str.length(), bytes); - return str.equals(result); - } catch (Throwable all) { - return false; - } - } - - private static String copyUSAciiBytesToStr(int length, byte[] bytes) { - char[] resultChars = new char[length]; - int i = 0; - while (i < length) { - // UsAscii - resultChars[i] = (char) bytes[i]; - i += 1; - } - return String.valueOf(resultChars, 0, length); - } - - public static void copyUSAsciiStrToBytes(String str, byte[] bytes) { - if (copyUSAsciiStrToBytesAlgorithm == 1) { - final byte[] chars = (byte[]) instance.getObject(str, stringValueFieldOffset); - System.arraycopy(chars, 0, bytes, 0, str.length()); - } else if (copyUSAsciiStrToBytesAlgorithm == 2) { - final char[] chars = (char[]) instance.getObject(str, stringValueFieldOffset); - int i = 0; - while (i < str.length()) { - bytes[i] = (byte) chars[i++]; - } - } else { - byte[] strBytes = str.getBytes(StandardCharsets.US_ASCII); - System.arraycopy(strBytes, 0, bytes, 0, str.length()); - } - } - - public static int fastHash(String str) { - long s0 = 391408; - long s1 = 601258; - int i = 0; - - if (copyUSAsciiStrToBytesAlgorithm == 1) { - final byte[] chars = (byte[]) instance.getObject(str, stringValueFieldOffset); - while (i < str.length()) { - long x = s0 ^ (long) chars[i++]; // Mix character into PRNG state - long y = s1; - - // Xorshift128+ round - s0 = y; - x ^= x << 23; - y ^= y >>> 26; - x ^= x >>> 17; - s1 = x ^ y; - } - } else if (copyUSAsciiStrToBytesAlgorithm == 2) { - final char[] chars = (char[]) instance.getObject(str, stringValueFieldOffset); - while (i < str.length()) { - long x = s0 ^ (long) chars[i++]; // Mix character into PRNG state - long y = s1; - - // Xorshift128+ round - s0 = y; - x ^= x << 23; - y ^= y >>> 26; - x ^= x >>> 17; - s1 = x ^ y; - } - } else { - byte[] chars = str.getBytes(StandardCharsets.US_ASCII); - while (i < str.length()) { - long x = s0 ^ (long) chars[i++]; // Mix character into PRNG state - long y = s1; - - // Xorshift128+ round - s0 = y; - x ^= x << 23; - y ^= y >>> 26; - x ^= x >>> 17; - s1 = x ^ y; - } - } - - return (int) (s0 + s1); - } -} diff --git a/actor/src/main/scala-2.12/org/apache/pekko/util/ByteIterator.scala b/actor/src/main/scala-2.12/org/apache/pekko/util/ByteIterator.scala index 244acd76bdc..c46ba2f54a0 100644 --- a/actor/src/main/scala-2.12/org/apache/pekko/util/ByteIterator.scala +++ b/actor/src/main/scala-2.12/org/apache/pekko/util/ByteIterator.scala @@ -208,7 +208,7 @@ object ByteIterator { @inline private def current: ByteArrayIterator = iterators.head @inline private def dropCurrent(): Unit = { iterators = iterators.tail } - @inline def clear(): Unit = { iterators = MultiByteArrayIterator.empty.iterators } + @inline final def clear(): Unit = { iterators = MultiByteArrayIterator.empty.iterators } @inline final def hasNext: Boolean = current.hasNext diff --git a/actor/src/main/scala-2.13/org/apache/pekko/util/ByteIterator.scala b/actor/src/main/scala-2.13/org/apache/pekko/util/ByteIterator.scala index fa5b9d08490..fedf925b31d 100644 --- a/actor/src/main/scala-2.13/org/apache/pekko/util/ByteIterator.scala +++ b/actor/src/main/scala-2.13/org/apache/pekko/util/ByteIterator.scala @@ -43,7 +43,7 @@ object ByteIterator { @inline final def hasNext: Boolean = from < until - @inline final def head: Byte = array(from) + final def head: Byte = array(from) final def next(): Byte = { if (!hasNext) EmptyImmutableSeq.iterator.next() @@ -220,7 +220,7 @@ object ByteIterator { @inline private def current: ByteArrayIterator = iterators.head @inline private def dropCurrent(): Unit = { iterators = iterators.tail } - @inline def clear(): Unit = { iterators = MultiByteArrayIterator.empty.iterators } + @inline final def clear(): Unit = { iterators = MultiByteArrayIterator.empty.iterators } @inline final def hasNext: Boolean = current.hasNext diff --git a/actor/src/main/scala-3/org/apache/pekko/util/ByteIterator.scala b/actor/src/main/scala-3/org/apache/pekko/util/ByteIterator.scala index f78128b6523..5b761b65c96 100644 --- a/actor/src/main/scala-3/org/apache/pekko/util/ByteIterator.scala +++ b/actor/src/main/scala-3/org/apache/pekko/util/ByteIterator.scala @@ -39,11 +39,11 @@ object ByteIterator { extends ByteIterator { iterator => - @inline final def len: Int = until - from + inline final def len: Int = until - from - @inline final def hasNext: Boolean = from < until + inline final def hasNext: Boolean = from < until - @inline final def head: Byte = array(from) + inline final def head: Byte = array(from) final def next(): Byte = { if (!hasNext) EmptyImmutableSeq.iterator.next() @@ -215,13 +215,13 @@ object ByteIterator { } normalize() - @inline private def current: ByteArrayIterator = iterators.head - @inline private def dropCurrent(): Unit = { iterators = iterators.tail } - @inline def clear(): Unit = { iterators = MultiByteArrayIterator.empty.iterators } + inline private def current: ByteArrayIterator = iterators.head + inline private def dropCurrent(): Unit = { iterators = iterators.tail } + inline def clear(): Unit = { iterators = MultiByteArrayIterator.empty.iterators } - @inline final def hasNext: Boolean = current.hasNext + inline final def hasNext: Boolean = current.hasNext - @inline final def head: Byte = current.head + inline final def head: Byte = current.head final def next(): Byte = { val result = current.next() diff --git a/actor/src/main/scala-3/org/apache/pekko/util/ByteString.scala b/actor/src/main/scala-3/org/apache/pekko/util/ByteString.scala index ed607dac97e..b8b84d2f476 100644 --- a/actor/src/main/scala-3/org/apache/pekko/util/ByteString.scala +++ b/actor/src/main/scala-3/org/apache/pekko/util/ByteString.scala @@ -1097,7 +1097,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { this } - @inline protected final def fillByteBuffer(len: Int, byteOrder: ByteOrder)(fill: ByteBuffer => Unit): this.type = { + inline protected final def fillByteBuffer(len: Int, byteOrder: ByteOrder)(fill: ByteBuffer => Unit): this.type = { fillArray(len) { case (array, start) => val buffer = ByteBuffer.wrap(array, start, len) @@ -1128,7 +1128,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { _tempCapacity = _temp.length } - @inline private def shouldResizeTempFor(size: Int): Boolean = _tempCapacity < size || _tempCapacity == 0 + inline private def shouldResizeTempFor(size: Int): Boolean = _tempCapacity < size || _tempCapacity == 0 private def ensureTempSize(size: Int): Unit = { if (shouldResizeTempFor(size)) { @@ -1139,7 +1139,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { } // We'd really like to have this overload to prevent boxing, but it's forbidden because sc.mutable.Growable makes - // it final. I guess it assumes to prevent the boxing overhead by using @inline but that doesn't seem to be true. + // it final. I guess it assumes to prevent the boxing overhead by using inline but that doesn't seem to be true. // def +=(elem: Byte): this.type = addOne(elem) override def addOne(elem: Byte): this.type = { diff --git a/actor/src/main/scala/org/apache/pekko/actor/AbstractActorRef.scala b/actor/src/main/scala/org/apache/pekko/actor/AbstractActorRef.scala new file mode 100644 index 00000000000..9b266835d41 --- /dev/null +++ b/actor/src/main/scala/org/apache/pekko/actor/AbstractActorRef.scala @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * license agreements; and to You under the Apache License, version 2.0: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * This file is part of the Apache Pekko project, derived from Akka. + */ + +/* + * Copyright (C) 2009-2022 Lightbend Inc. + */ + +package org.apache.pekko.actor + +import org.apache.pekko.util.Unsafe + +object AbstractActorRef { + private[actor] var cellOffset = 0L + private[actor] var lookupOffset = 0L + + try { + cellOffset = + Unsafe.instance.objectFieldOffset(classOf[RepointableActorRef].getDeclaredField("_cellDoNotCallMeDirectly")) + lookupOffset = + Unsafe.instance.objectFieldOffset(classOf[RepointableActorRef].getDeclaredField("_lookupDoNotCallMeDirectly")) + } catch { + case t: Throwable => + throw new ExceptionInInitializerError(t) + } + +} diff --git a/actor/src/main/scala/org/apache/pekko/actor/dungeon/AbstractActorCell.scala b/actor/src/main/scala/org/apache/pekko/actor/dungeon/AbstractActorCell.scala new file mode 100644 index 00000000000..e8a6304ce7d --- /dev/null +++ b/actor/src/main/scala/org/apache/pekko/actor/dungeon/AbstractActorCell.scala @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * license agreements; and to You under the Apache License, version 2.0: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * This file is part of the Apache Pekko project, derived from Akka. + */ + +/* + * Copyright (C) 2009-2022 Lightbend Inc. + */ + +package org.apache.pekko.actor.dungeon + +import org.apache.pekko.actor.ActorCell +import org.apache.pekko.util.Unsafe + +object AbstractActorCell { + private[dungeon] final var mailboxOffset = 0L + private[dungeon] final var childrenOffset = 0L + private[dungeon] final var nextNameOffset = 0L + private[dungeon] final var functionRefsOffset = 0L + + try { + mailboxOffset = Unsafe.instance.objectFieldOffset( + classOf[ActorCell].getDeclaredField("org$apache$pekko$actor$dungeon$Dispatch$$_mailboxDoNotCallMeDirectly")) + childrenOffset = Unsafe.instance.objectFieldOffset(classOf[ActorCell].getDeclaredField( + "org$apache$pekko$actor$dungeon$Children$$_childrenRefsDoNotCallMeDirectly")) + nextNameOffset = Unsafe.instance.objectFieldOffset( + classOf[ActorCell].getDeclaredField("org$apache$pekko$actor$dungeon$Children$$_nextNameDoNotCallMeDirectly")) + functionRefsOffset = Unsafe.instance.objectFieldOffset(classOf[ActorCell].getDeclaredField( + "org$apache$pekko$actor$dungeon$Children$$_functionRefsDoNotCallMeDirectly")) + } catch { + case t: Throwable => + throw new ExceptionInInitializerError(t) + } + +} diff --git a/actor/src/main/scala/org/apache/pekko/dispatch/AbstractMailbox.scala b/actor/src/main/scala/org/apache/pekko/dispatch/AbstractMailbox.scala new file mode 100644 index 00000000000..cd494f76903 --- /dev/null +++ b/actor/src/main/scala/org/apache/pekko/dispatch/AbstractMailbox.scala @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * license agreements; and to You under the Apache License, version 2.0: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * This file is part of the Apache Pekko project, derived from Akka. + */ + +/* + * Copyright (C) 2009-2022 Lightbend Inc. + */ + +package org.apache.pekko.dispatch + +import org.apache.pekko.util.Unsafe + +object AbstractMailbox { + private[dispatch] final var mailboxStatusOffset = 0L + private[dispatch] final var systemMessageOffset = 0L + + try { + mailboxStatusOffset = + Unsafe.instance.objectFieldOffset(classOf[Mailbox].getDeclaredField("_statusDoNotCallMeDirectly")) + systemMessageOffset = + Unsafe.instance.objectFieldOffset(classOf[Mailbox].getDeclaredField("_systemQueueDoNotCallMeDirectly")) + } catch { + case t: Throwable => + throw new ExceptionInInitializerError(t) + } + +} diff --git a/actor/src/main/scala/org/apache/pekko/pattern/AbstractCircuitBreaker.scala b/actor/src/main/scala/org/apache/pekko/pattern/AbstractCircuitBreaker.scala new file mode 100644 index 00000000000..baf79fc6d83 --- /dev/null +++ b/actor/src/main/scala/org/apache/pekko/pattern/AbstractCircuitBreaker.scala @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * license agreements; and to You under the Apache License, version 2.0: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * This file is part of the Apache Pekko project, derived from Akka. + */ + +/* + * Copyright (C) 2009-2022 Lightbend Inc. + */ + +package org.apache.pekko.pattern + +import org.apache.pekko.util.Unsafe + +class AbstractCircuitBreaker { + protected var stateOffset = 0L + protected var resetTimeoutOffset = 0L + + try { + stateOffset = + Unsafe.instance.objectFieldOffset(classOf[CircuitBreaker].getDeclaredField("_currentStateDoNotCallMeDirectly")) + resetTimeoutOffset = Unsafe.instance.objectFieldOffset( + classOf[CircuitBreaker].getDeclaredField("_currentResetTimeoutDoNotCallMeDirectly")) + } catch { + case t: Throwable => + throw new ExceptionInInitializerError(t) + } + +} diff --git a/actor/src/main/scala/org/apache/pekko/pattern/AbstractPromiseActorRef.scala b/actor/src/main/scala/org/apache/pekko/pattern/AbstractPromiseActorRef.scala new file mode 100644 index 00000000000..f27833e90e5 --- /dev/null +++ b/actor/src/main/scala/org/apache/pekko/pattern/AbstractPromiseActorRef.scala @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * license agreements; and to You under the Apache License, version 2.0: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * This file is part of the Apache Pekko project, derived from Akka. + */ + +/* + * Copyright (C) 2009-2022 Lightbend Inc. + */ + +package org.apache.pekko.pattern + +import org.apache.pekko.util.Unsafe + +object AbstractPromiseActorRef { + private[pattern] final var stateOffset = 0L + private[pattern] final var watchedByOffset = 0L + + try { + stateOffset = + Unsafe.instance.objectFieldOffset(classOf[PromiseActorRef].getDeclaredField("_stateDoNotCallMeDirectly")) + watchedByOffset = + Unsafe.instance.objectFieldOffset(classOf[PromiseActorRef].getDeclaredField("_watchedByDoNotCallMeDirectly")) + } catch { + case t: Throwable => + throw new ExceptionInInitializerError(t) + } + +} diff --git a/actor/src/main/scala/org/apache/pekko/pattern/CircuitBreaker.scala b/actor/src/main/scala/org/apache/pekko/pattern/CircuitBreaker.scala index 82072f5bf28..65ea481879c 100644 --- a/actor/src/main/scala/org/apache/pekko/pattern/CircuitBreaker.scala +++ b/actor/src/main/scala/org/apache/pekko/pattern/CircuitBreaker.scala @@ -319,7 +319,7 @@ class CircuitBreaker( */ @inline private[this] def swapState(oldState: State, newState: State): Boolean = - Unsafe.instance.compareAndSwapObject(this, AbstractCircuitBreaker.stateOffset, oldState, newState) + Unsafe.instance.compareAndSwapObject(this, stateOffset, oldState, newState) /** * Helper method for accessing underlying state via Unsafe @@ -328,7 +328,7 @@ class CircuitBreaker( */ @inline private[this] def currentState: State = - Unsafe.instance.getObjectVolatile(this, AbstractCircuitBreaker.stateOffset).asInstanceOf[State] + Unsafe.instance.getObjectVolatile(this, stateOffset).asInstanceOf[State] /** * Helper method for updating the underlying resetTimeout via Unsafe @@ -337,7 +337,7 @@ class CircuitBreaker( private[this] def swapResetTimeout(oldResetTimeout: FiniteDuration, newResetTimeout: FiniteDuration): Boolean = Unsafe.instance.compareAndSwapObject( this, - AbstractCircuitBreaker.resetTimeoutOffset, + resetTimeoutOffset, oldResetTimeout, newResetTimeout) @@ -346,7 +346,7 @@ class CircuitBreaker( */ @inline private[this] def currentResetTimeout: FiniteDuration = - Unsafe.instance.getObjectVolatile(this, AbstractCircuitBreaker.resetTimeoutOffset).asInstanceOf[FiniteDuration] + Unsafe.instance.getObjectVolatile(this, resetTimeoutOffset).asInstanceOf[FiniteDuration] /** * Wraps invocations of asynchronous calls that need to be protected. diff --git a/actor/src/main/scala/org/apache/pekko/util/Unsafe.scala b/actor/src/main/scala/org/apache/pekko/util/Unsafe.scala new file mode 100644 index 00000000000..3f7f9eba59b --- /dev/null +++ b/actor/src/main/scala/org/apache/pekko/util/Unsafe.scala @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * license agreements; and to You under the Apache License, version 2.0: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * This file is part of the Apache Pekko project, derived from Akka. + */ + +/* + * Copyright (C) 2009-2022 Lightbend Inc. + */ + +package org.apache.pekko.util + +import org.apache.pekko.annotation.InternalApi + +import java.nio.charset.StandardCharsets +import java.util + +/** INTERNAL API */ +@InternalApi object Unsafe { + var instance: sun.misc.Unsafe = _ + private var stringValueFieldOffset = 0L + private var isJavaVersion9Plus = false + private var copyUSAsciiStrToBytesAlgorithm = 0 + private[util] def isIsJavaVersion9Plus = { + // See Oracle section 1.5.3 at: + // https://docs.oracle.com/javase/8/docs/technotes/guides/versioning/spec/versioning2.html + val version = + util.Arrays.stream(System.getProperty("java.specification.version").split("\\.")).mapToInt( + Integer.parseInt).toArray + val javaVersion = if (version(0) == 1) version(1) + else version(0) + javaVersion > 8 + } + private[util] def testUSAsciiStrToBytesAlgorithm0(str: String) = + try { + val bytes = new Array[Byte](str.length) + // copy of implementation in copyUSAciiBytesToStr + val strBytes = str.getBytes(StandardCharsets.US_ASCII) + System.arraycopy(strBytes, 0, bytes, 0, str.length) + // end copy + val result = copyUSAciiBytesToStr(str.length, bytes) + str == result + } catch { + case _: Throwable => + false + } + private[util] def testUSAsciiStrToBytesAlgorithm1(str: String) = + try { + val bytes = new Array[Byte](str.length) + // copy of implementation in copyUSAciiBytesToStr + val chars = instance.getObject(str, stringValueFieldOffset).asInstanceOf[Array[Byte]] + System.arraycopy(chars, 0, bytes, 0, str.length) + // end copy + val result = copyUSAciiBytesToStr(str.length, bytes) + str == result + } catch { + case _: Throwable => + false + } + private[util] def testUSAsciiStrToBytesAlgorithm2(str: String) = + try { + val bytes = new Array[Byte](str.length) + // copy of implementation in copyUSAciiBytesToStr + val chars = instance.getObject(str, stringValueFieldOffset).asInstanceOf[Array[Char]] + var i = 0 + while (i < str.length) bytes(i) = chars { i += 1; i - 1 }.toByte + // end copy + val result = copyUSAciiBytesToStr(str.length, bytes) + str == result + } catch { + case _: Throwable => + false + } + private def copyUSAciiBytesToStr(length: Int, bytes: Array[Byte]) = { + val resultChars = new Array[Char](length) + var i = 0 + while (i < length) { + // UsAscii + resultChars(i) = bytes(i).toChar + i += 1 + } + String.valueOf(resultChars, 0, length) + } + def copyUSAsciiStrToBytes(str: String, bytes: Array[Byte]): Unit = { + if (copyUSAsciiStrToBytesAlgorithm == 1) { + val chars = instance.getObject(str, stringValueFieldOffset).asInstanceOf[Array[Byte]] + System.arraycopy(chars, 0, bytes, 0, str.length) + } else if (copyUSAsciiStrToBytesAlgorithm == 2) { + val chars = instance.getObject(str, stringValueFieldOffset).asInstanceOf[Array[Char]] + var i = 0 + while (i < str.length) bytes(i) = chars { i += 1; i - 1 }.toByte + } else { + val strBytes = str.getBytes(StandardCharsets.US_ASCII) + System.arraycopy(strBytes, 0, bytes, 0, str.length) + } + } + def fastHash(str: String): Int = { + var s0 = 391408L + var s1 = 601258L + var i = 0 + if (copyUSAsciiStrToBytesAlgorithm == 1) { + val chars = instance.getObject(str, stringValueFieldOffset).asInstanceOf[Array[Byte]] + while (i < str.length) { + var x = s0 ^ chars { i += 1; i - 1 }.toLong // Mix character into PRNG state + + var y = s1 + // Xorshift128+ round + s0 = y + x ^= x << 23 + y ^= y >>> 26 + x ^= x >>> 17 + s1 = x ^ y + } + } else if (copyUSAsciiStrToBytesAlgorithm == 2) { + val chars = instance.getObject(str, stringValueFieldOffset).asInstanceOf[Array[Char]] + while (i < str.length) { + var x = s0 ^ chars { i += 1; i - 1 }.toLong // Mix character into PRNG state + + var y = s1 + // Xorshift128+ round + s0 = y + x ^= x << 23 + y ^= y >>> 26 + x ^= x >>> 17 + s1 = x ^ y + } + } else { + val chars = str.getBytes(StandardCharsets.US_ASCII) + while (i < str.length) { + var x = s0 ^ chars { i += 1; i - 1 }.toLong // Mix character into PRNG state + + var y = s1 + // Xorshift128+ round + s0 = y + x ^= x << 23 + y ^= y >>> 26 + x ^= x >>> 17 + s1 = x ^ y + } + } + (s0 + s1).toInt + } + + try { + var found: sun.misc.Unsafe = null + val fields = classOf[sun.misc.Unsafe].getDeclaredFields + var i = 0 + while (i < fields.size && found == null) { + val field = fields(i) + if (field.getType eq classOf[sun.misc.Unsafe]) { + field.setAccessible(true) + found = field.get(null).asInstanceOf[sun.misc.Unsafe] + } + i += 1 + } + if (found == null) throw new IllegalStateException("Can't find instance of sun.misc.Unsafe") + else instance = found + var fo = 0L + try fo = instance.objectFieldOffset(classOf[String].getDeclaredField("value")) + catch { + case _: NoSuchFieldException => + // The platform's implementation of String doesn't have a 'value' field, so we have to use + // algorithm 0 + fo = -1 + } + stringValueFieldOffset = fo + isJavaVersion9Plus = isIsJavaVersion9Plus + if (stringValueFieldOffset > -1) { + // Select optimization algorithm for `copyUSAciiBytesToStr`. + // For example algorithm 1 will fail with JDK 11 on ARM32 (Raspberry Pi), + // and therefore algorithm 0 is selected on that architecture. + val testStr = "abc" + if (isJavaVersion9Plus && testUSAsciiStrToBytesAlgorithm1(testStr)) copyUSAsciiStrToBytesAlgorithm = 1 + else if (testUSAsciiStrToBytesAlgorithm2(testStr)) copyUSAsciiStrToBytesAlgorithm = 2 + else copyUSAsciiStrToBytesAlgorithm = 0 + } else copyUSAsciiStrToBytesAlgorithm = 0 // We know so little about the platform's String implementation that we have + // no choice but to select algorithm 0 + } catch { + case t: Throwable => + throw new ExceptionInInitializerError(t) + } + +} diff --git a/project/PekkoInlinePlugin.scala b/project/PekkoInlinePlugin.scala new file mode 100644 index 00000000000..4545b380b14 --- /dev/null +++ b/project/PekkoInlinePlugin.scala @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * license agreements; and to You under the Apache License, version 2.0: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * This file is part of the Apache Pekko project, derived from Akka. + */ + +import sbt.Keys._ +import sbt._ +import sbt.plugins.JvmPlugin + +object PekkoInlinePlugin extends AutoPlugin { + override def trigger: PluginTrigger = allRequirements + + override def requires: Plugins = JvmPlugin + + val enabled = !sys.props.contains("pekko.no.inline") + + private val flagsFor212 = Seq( + "-opt-inline-from:org.apache.pekko.**", + "-opt:l:inline") + + private val flagsFor213 = Seq( + "-opt-inline-from:org.apache.pekko.**", + "-opt:l:inline") + + // Optimizer not yet available for Scala3, see https://docs.scala-lang.org/overviews/compiler-options/optimizer.html + private val flagsFor3 = Seq() + + override lazy val projectSettings = Seq( + Compile / scalacOptions ++= { + if (enabled) { + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, n)) if n == 13 => + flagsFor213 + case Some((2, n)) if n == 12 => + flagsFor212 + case Some((3, _)) => + flagsFor3 + } + } else Seq.empty + }) + + lazy val compilationOrderSettings = Seq( + Compile / compileOrder := CompileOrder.JavaThenScala) +}