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 dc7ae0413c2..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, which was 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 ce319d3af41..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, which was 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/AbstractBoundedNodeQueue.java b/actor/src/main/java/org/apache/pekko/dispatch/AbstractBoundedNodeQueue.java index 3ee85c929ff..e821101a5a2 100644 --- a/actor/src/main/java/org/apache/pekko/dispatch/AbstractBoundedNodeQueue.java +++ b/actor/src/main/java/org/apache/pekko/dispatch/AbstractBoundedNodeQueue.java @@ -13,7 +13,7 @@ package org.apache.pekko.dispatch; -import org.apache.pekko.util.Unsafe; +import org.apache.pekko.util.Unsafe$; /** * Lock-free bounded non-blocking multiple-producer single-consumer queue based on the works of: @@ -45,29 +45,29 @@ protected AbstractBoundedNodeQueue(final int capacity) { } private void setEnq(Node n) { - Unsafe.instance.putObjectVolatile(this, enqOffset, n); + Unsafe$.MODULE$.instance().putObjectVolatile(this, enqOffset, n); } @SuppressWarnings("unchecked") private Node getEnq() { - return (Node)Unsafe.instance.getObjectVolatile(this, enqOffset); + return (Node)Unsafe$.MODULE$.instance().getObjectVolatile(this, enqOffset); } private boolean casEnq(Node old, Node nju) { - return Unsafe.instance.compareAndSwapObject(this, enqOffset, old, nju); + return Unsafe$.MODULE$.instance().compareAndSwapObject(this, enqOffset, old, nju); } private void setDeq(Node n) { - Unsafe.instance.putObjectVolatile(this, deqOffset, n); + Unsafe$.MODULE$.instance().putObjectVolatile(this, deqOffset, n); } @SuppressWarnings("unchecked") private Node getDeq() { - return (Node)Unsafe.instance.getObjectVolatile(this, deqOffset); + return (Node)Unsafe$.MODULE$.instance().getObjectVolatile(this, deqOffset); } private boolean casDeq(Node old, Node nju) { - return Unsafe.instance.compareAndSwapObject(this, deqOffset, old, nju); + return Unsafe$.MODULE$.instance().compareAndSwapObject(this, deqOffset, old, nju); } protected final Node peekNode() { @@ -187,8 +187,8 @@ public final Node pollNode() { static { try { - enqOffset = Unsafe.instance.objectFieldOffset(AbstractBoundedNodeQueue.class.getDeclaredField("_enqDoNotCallMeDirectly")); - deqOffset = Unsafe.instance.objectFieldOffset(AbstractBoundedNodeQueue.class.getDeclaredField("_deqDoNotCallMeDirectly")); + enqOffset = Unsafe$.MODULE$.instance().objectFieldOffset(AbstractBoundedNodeQueue.class.getDeclaredField("_enqDoNotCallMeDirectly")); + deqOffset = Unsafe$.MODULE$.instance().objectFieldOffset(AbstractBoundedNodeQueue.class.getDeclaredField("_deqDoNotCallMeDirectly")); } catch(Throwable t){ throw new ExceptionInInitializerError(t); } @@ -202,18 +202,18 @@ public static class Node { @SuppressWarnings("unchecked") public final Node next() { - return (Node)Unsafe.instance.getObjectVolatile(this, nextOffset); + return (Node)Unsafe$.MODULE$.instance().getObjectVolatile(this, nextOffset); } protected final void setNext(final Node newNext) { - Unsafe.instance.putOrderedObject(this, nextOffset, newNext); + Unsafe$.MODULE$.instance().putOrderedObject(this, nextOffset, newNext); } private final static long nextOffset; static { try { - nextOffset = Unsafe.instance.objectFieldOffset(Node.class.getDeclaredField("_nextDoNotCallMeDirectly")); + nextOffset = Unsafe$.MODULE$.instance().objectFieldOffset(Node.class.getDeclaredField("_nextDoNotCallMeDirectly")); } 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 cb08254e519..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, which was 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/dispatch/AbstractMessageDispatcher.java b/actor/src/main/java/org/apache/pekko/dispatch/AbstractMessageDispatcher.java index 6fafd0dd0ff..f58b55bcc31 100644 --- a/actor/src/main/java/org/apache/pekko/dispatch/AbstractMessageDispatcher.java +++ b/actor/src/main/java/org/apache/pekko/dispatch/AbstractMessageDispatcher.java @@ -13,7 +13,7 @@ package org.apache.pekko.dispatch; -import org.apache.pekko.util.Unsafe; +import org.apache.pekko.util.Unsafe$; abstract class AbstractMessageDispatcher { final static long shutdownScheduleOffset; @@ -21,8 +21,8 @@ abstract class AbstractMessageDispatcher { static { try { - shutdownScheduleOffset = Unsafe.instance.objectFieldOffset(MessageDispatcher.class.getDeclaredField("_shutdownScheduleDoNotCallMeDirectly")); - inhabitantsOffset = Unsafe.instance.objectFieldOffset(MessageDispatcher.class.getDeclaredField("_inhabitantsDoNotCallMeDirectly")); + shutdownScheduleOffset = Unsafe$.MODULE$.instance().objectFieldOffset(MessageDispatcher.class.getDeclaredField("_shutdownScheduleDoNotCallMeDirectly")); + inhabitantsOffset = Unsafe$.MODULE$.instance().objectFieldOffset(MessageDispatcher.class.getDeclaredField("_inhabitantsDoNotCallMeDirectly")); } catch(Throwable t){ throw new ExceptionInInitializerError(t); } diff --git a/actor/src/main/java/org/apache/pekko/dispatch/AbstractNodeQueue.java b/actor/src/main/java/org/apache/pekko/dispatch/AbstractNodeQueue.java index a67ba9741be..c114c2930d8 100644 --- a/actor/src/main/java/org/apache/pekko/dispatch/AbstractNodeQueue.java +++ b/actor/src/main/java/org/apache/pekko/dispatch/AbstractNodeQueue.java @@ -13,7 +13,7 @@ package org.apache.pekko.dispatch; -import org.apache.pekko.util.Unsafe; +import org.apache.pekko.util.Unsafe$; import java.util.concurrent.atomic.AtomicReference; @@ -54,7 +54,7 @@ protected AbstractNodeQueue() { */ @SuppressWarnings("unchecked") protected final Node peekNode() { - final Node tail = ((Node)Unsafe.instance.getObjectVolatile(this, tailOffset)); + final Node tail = ((Node)Unsafe$.MODULE$.instance().getObjectVolatile(this, tailOffset)); Node next = tail.next(); if (next == null && get() != tail) { // if tail != head this is not going to change until producer makes progress @@ -110,7 +110,7 @@ public final void addNode(final Node n) { * @return true if queue was empty at some point in the past */ public final boolean isEmpty() { - return Unsafe.instance.getObjectVolatile(this, tailOffset) == get(); + return Unsafe$.MODULE$.instance().getObjectVolatile(this, tailOffset) == get(); } /** @@ -126,7 +126,7 @@ public final boolean isEmpty() { public final int count() { int count = 0; final Node head = get(); - for(Node n = ((Node) Unsafe.instance.getObjectVolatile(this, tailOffset)).next(); + for(Node n = ((Node) Unsafe$.MODULE$.instance().getObjectVolatile(this, tailOffset)).next(); n != null && count < Integer.MAX_VALUE; n = n.next()) { ++count; @@ -162,7 +162,7 @@ public final T poll() { */ @SuppressWarnings("unchecked") public final Node pollNode() { - final Node tail = (Node) Unsafe.instance.getObjectVolatile(this, tailOffset); + final Node tail = (Node) Unsafe$.MODULE$.instance().getObjectVolatile(this, tailOffset); Node next = tail.next(); if (next == null && get() != tail) { // if tail != head this is not going to change until producer makes progress @@ -175,7 +175,7 @@ public final Node pollNode() { else { tail.value = next.value; next.value = null; - Unsafe.instance.putOrderedObject(this, tailOffset, next); + Unsafe$.MODULE$.instance().putOrderedObject(this, tailOffset, next); tail.setNext(null); return tail; } @@ -185,7 +185,7 @@ public final Node pollNode() { static { try { - tailOffset = Unsafe.instance.objectFieldOffset(AbstractNodeQueue.class.getDeclaredField("_tailDoNotCallMeDirectly")); + tailOffset = Unsafe$.MODULE$.instance().objectFieldOffset(AbstractNodeQueue.class.getDeclaredField("_tailDoNotCallMeDirectly")); } catch(Throwable t){ throw new ExceptionInInitializerError(t); } @@ -206,18 +206,18 @@ public Node(final T value) { @SuppressWarnings("unchecked") public final Node next() { - return (Node)Unsafe.instance.getObjectVolatile(this, nextOffset); + return (Node)Unsafe$.MODULE$.instance().getObjectVolatile(this, nextOffset); } protected final void setNext(final Node newNext) { - Unsafe.instance.putOrderedObject(this, nextOffset, newNext); + Unsafe$.MODULE$.instance().putOrderedObject(this, nextOffset, newNext); } private final static long nextOffset; static { try { - nextOffset = Unsafe.instance.objectFieldOffset(Node.class.getDeclaredField("_nextDoNotCallMeDirectly")); + nextOffset = Unsafe$.MODULE$.instance().objectFieldOffset(Node.class.getDeclaredField("_nextDoNotCallMeDirectly")); } 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 13e4874db6c..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, which was 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 f6cf890071d..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, which was 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 c26ea4d9dab..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, which was 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/mima-filters/1.1.0.backwards.excludes/future-converters.excludes b/actor/src/main/mima-filters/1.1.0.backwards.excludes/future-converters.excludes new file mode 100644 index 00000000000..bf0a9e2c4b9 --- /dev/null +++ b/actor/src/main/mima-filters/1.1.0.backwards.excludes/future-converters.excludes @@ -0,0 +1,6 @@ +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.FutureConverters#CompletionStageOps.asScala$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.FutureConverters#CompletionStageOps.asScala") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.FutureConverters#CompletionStageOps.asScala$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.FutureConverters#FutureOps.asJava$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.FutureConverters#FutureOps.asJava") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.FutureConverters#FutureOps.asJava$extension") diff --git a/actor/src/main/mima-filters/1.1.0.backwards.excludes/option-converters.excludes b/actor/src/main/mima-filters/1.1.0.backwards.excludes/option-converters.excludes new file mode 100644 index 00000000000..3815de9f40a --- /dev/null +++ b/actor/src/main/mima-filters/1.1.0.backwards.excludes/option-converters.excludes @@ -0,0 +1,30 @@ +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOption.toJava$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOption.toJavaPrimitive$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOption.toJava") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOption.toJavaPrimitive") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOption.toJava$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOption.toJavaPrimitive$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptional.toJavaPrimitive$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptional.toScala$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptional.toScala") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptional.toJavaPrimitive") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptional.toScala$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptional.toJavaPrimitive$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptionalDouble.toJavaGeneric$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptionalDouble.toScala$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptionalDouble.toScala") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptionalDouble.toJavaGeneric") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptionalDouble.toScala$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptionalDouble.toJavaGeneric$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptionalInt.toJavaGeneric$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptionalInt.toScala$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptionalInt.toScala") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptionalInt.toJavaGeneric") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptionalInt.toScala$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptionalInt.toJavaGeneric$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptionalLong.toJavaGeneric$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptionalLong.toScala$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptionalLong.toScala") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptionalLong.toJavaGeneric") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptionalLong.toScala$extension") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.OptionConverters#RichOptionalLong.toJavaGeneric$extension") diff --git a/actor/src/main/mima-filters/1.1.0.backwards.excludes/unsafe.excludes b/actor/src/main/mima-filters/1.1.0.backwards.excludes/unsafe.excludes new file mode 100644 index 00000000000..d5b25d8d0b1 --- /dev/null +++ b/actor/src/main/mima-filters/1.1.0.backwards.excludes/unsafe.excludes @@ -0,0 +1,3 @@ +ProblemFilters.exclude[IncompatibleTemplateDefProblem]("org.apache.pekko.util.Unsafe") +ProblemFilters.exclude[MissingFieldProblem]("org.apache.pekko.util.Unsafe.instance") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.util.Unsafe.this") diff --git a/actor/src/main/scala/org/apache/pekko/actor/package.scala b/actor/src/main/scala-2.12/org/apache/pekko/actor/package.scala similarity index 100% rename from actor/src/main/scala/org/apache/pekko/actor/package.scala rename to actor/src/main/scala-2.12/org/apache/pekko/actor/package.scala diff --git a/actor/src/main/scala-2.13/org/apache/pekko/actor/package.scala b/actor/src/main/scala-2.13/org/apache/pekko/actor/package.scala new file mode 100644 index 00000000000..c0734cf0083 --- /dev/null +++ b/actor/src/main/scala-2.13/org/apache/pekko/actor/package.scala @@ -0,0 +1,23 @@ +/* + * 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, which was derived from Akka. + */ + +/* + * Copyright (C) 2009-2022 Lightbend Inc. + */ + +package org.apache.pekko + +import language.implicitConversions + +package object actor { + @deprecated("implicit conversion is obsolete", "Akka 2.6.13") + @inline implicit final def actorRef2Scala(ref: ActorRef): ScalaActorRef = ref.asInstanceOf[ScalaActorRef] + @deprecated("implicit conversion is obsolete", "Akka 2.6.13") + @inline implicit final def scala2ActorRef(ref: ScalaActorRef): ActorRef = ref.asInstanceOf[ActorRef] +} 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 5965345199e..5fbd41f7f99 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() diff --git a/actor/src/main/scala-2.13+/org/apache/pekko/util/FutureConverters.scala b/actor/src/main/scala-2.13/org/apache/pekko/util/FutureConverters.scala similarity index 100% rename from actor/src/main/scala-2.13+/org/apache/pekko/util/FutureConverters.scala rename to actor/src/main/scala-2.13/org/apache/pekko/util/FutureConverters.scala diff --git a/actor/src/main/scala-2.13+/org/apache/pekko/util/OptionConverters.scala b/actor/src/main/scala-2.13/org/apache/pekko/util/OptionConverters.scala similarity index 100% rename from actor/src/main/scala-2.13+/org/apache/pekko/util/OptionConverters.scala rename to actor/src/main/scala-2.13/org/apache/pekko/util/OptionConverters.scala diff --git a/actor/src/main/scala-3/org/apache/pekko/actor/package.scala b/actor/src/main/scala-3/org/apache/pekko/actor/package.scala new file mode 100644 index 00000000000..17765b645ca --- /dev/null +++ b/actor/src/main/scala-3/org/apache/pekko/actor/package.scala @@ -0,0 +1,23 @@ +/* + * 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, which was derived from Akka. + */ + +/* + * Copyright (C) 2009-2022 Lightbend Inc. + */ + +package org.apache.pekko + +import language.implicitConversions + +package object actor { + @deprecated("implicit conversion is obsolete", "Akka 2.6.13") + inline implicit final def actorRef2Scala(ref: ActorRef): ScalaActorRef = ref.asInstanceOf[ScalaActorRef] + @deprecated("implicit conversion is obsolete", "Akka 2.6.13") + inline implicit final def scala2ActorRef(ref: ScalaActorRef): ActorRef = ref.asInstanceOf[ActorRef] +} 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 c4eed52125d..c5e1bfdb7af 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 077a69770e3..2ac23905e64 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-3/org/apache/pekko/util/FutureConverters.scala b/actor/src/main/scala-3/org/apache/pekko/util/FutureConverters.scala new file mode 100644 index 00000000000..b3b8e63c9b8 --- /dev/null +++ b/actor/src/main/scala-3/org/apache/pekko/util/FutureConverters.scala @@ -0,0 +1,37 @@ +/* + * 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. + */ + +package org.apache.pekko.util + +import org.apache.pekko.annotation.InternalStableApi + +import java.util.concurrent.CompletionStage +import scala.concurrent.Future + +/** + * INTERNAL API + * + * Remove this once Scala 2.12 support is dropped since all methods are in Scala 2.13+ stdlib + */ +@InternalStableApi +private[pekko] object FutureConverters { + import scala.jdk.javaapi + + def asJava[T](f: Future[T]): CompletionStage[T] = javaapi.FutureConverters.asJava(f) + + implicit final class FutureOps[T](private val f: Future[T]) extends AnyVal { + inline def asJava: CompletionStage[T] = FutureConverters.asJava(f) + } + + def asScala[T](cs: CompletionStage[T]): Future[T] = javaapi.FutureConverters.asScala(cs) + + implicit final class CompletionStageOps[T](private val cs: CompletionStage[T]) extends AnyVal { + inline def asScala: Future[T] = FutureConverters.asScala(cs) + } +} diff --git a/actor/src/main/scala-3/org/apache/pekko/util/OptionConverters.scala b/actor/src/main/scala-3/org/apache/pekko/util/OptionConverters.scala new file mode 100644 index 00000000000..2f2fef5c378 --- /dev/null +++ b/actor/src/main/scala-3/org/apache/pekko/util/OptionConverters.scala @@ -0,0 +1,77 @@ +/* + * 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. + */ + +package org.apache.pekko.util + +import org.apache.pekko.annotation.InternalStableApi + +import java.util._ +import scala.jdk.OptionShape + +/** + * INTERNAL API + * + * Remove this once Scala 2.12 support is dropped since all methods are in Scala 2.13+ stdlib + */ +@InternalStableApi +private[pekko] object OptionConverters { + + inline final def toScala[A](o: Optional[A]): Option[A] = scala.jdk.javaapi.OptionConverters.toScala(o) + + inline def toScala(o: OptionalDouble): Option[java.lang.Double] = scala.jdk.javaapi.OptionConverters.toScala(o) + + inline def toScala(o: OptionalInt): Option[java.lang.Integer] = scala.jdk.javaapi.OptionConverters.toScala(o) + + inline def toScala(o: OptionalLong): Option[java.lang.Long] = scala.jdk.javaapi.OptionConverters.toScala(o) + + inline final def toJava[A](o: Option[A]): Optional[A] = scala.jdk.javaapi.OptionConverters.toJava(o) + + implicit final class RichOptional[A](private val o: java.util.Optional[A]) extends AnyVal { + inline def toScala: Option[A] = scala.jdk.OptionConverters.RichOptional(o).toScala + + inline def toJavaPrimitive[O](implicit shape: OptionShape[A, O]): O = + scala.jdk.OptionConverters.RichOptional(o).toJavaPrimitive + } + + implicit final class RichOption[A](private val o: Option[A]) extends AnyVal { + inline def toJava: Optional[A] = scala.jdk.OptionConverters.RichOption(o).toJava + + inline def toJavaPrimitive[O](implicit shape: OptionShape[A, O]): O = + scala.jdk.OptionConverters.RichOption(o).toJavaPrimitive + } + + implicit class RichOptionalDouble(private val o: OptionalDouble) extends AnyVal { + + /** Convert a Java `OptionalDouble` to a Scala `Option` */ + inline def toScala: Option[Double] = scala.jdk.OptionConverters.RichOptionalDouble(o).toScala + + /** Convert a Java `OptionalDouble` to a generic Java `Optional` */ + inline def toJavaGeneric: Optional[Double] = scala.jdk.OptionConverters.RichOptionalDouble(o).toJavaGeneric + } + + /** Provides conversions from `OptionalInt` to Scala `Option` and the generic `Optional` */ + implicit class RichOptionalInt(private val o: OptionalInt) extends AnyVal { + + /** Convert a Java `OptionalInt` to a Scala `Option` */ + inline def toScala: Option[Int] = scala.jdk.OptionConverters.RichOptionalInt(o).toScala + + /** Convert a Java `OptionalInt` to a generic Java `Optional` */ + inline def toJavaGeneric: Optional[Int] = scala.jdk.OptionConverters.RichOptionalInt(o).toJavaGeneric + } + + /** Provides conversions from `OptionalLong` to Scala `Option` and the generic `Optional` */ + implicit class RichOptionalLong(private val o: OptionalLong) extends AnyVal { + + /** Convert a Java `OptionalLong` to a Scala `Option` */ + inline def toScala: Option[Long] = scala.jdk.OptionConverters.RichOptionalLong(o).toScala + + /** Convert a Java `OptionalLong` to a generic Java `Optional` */ + inline def toJavaGeneric: Optional[Long] = scala.jdk.OptionConverters.RichOptionalLong(o).toJavaGeneric + } +} 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 a296c8962fe..f1666dad922 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..c5177ab3d18 --- /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 private[pekko] 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..779f5ec4c5a --- /dev/null +++ b/project/PekkoInlinePlugin.scala @@ -0,0 +1,45 @@ +/* + * 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 + }) +} diff --git a/remote/src/main/java/org/apache/pekko/remote/artery/AbstractAssociation.java b/remote/src/main/java/org/apache/pekko/remote/artery/AbstractAssociation.java deleted file mode 100644 index 2ee9d60ff41..00000000000 --- a/remote/src/main/java/org/apache/pekko/remote/artery/AbstractAssociation.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, which was derived from Akka. - */ - -/* - * Copyright (C) 2016-2022 Lightbend Inc. - */ - -package org.apache.pekko.remote.artery; - -import org.apache.pekko.util.Unsafe; - -class AbstractAssociation { - protected static final long sharedStateOffset; - - static { - try { - sharedStateOffset = - Unsafe.instance.objectFieldOffset( - Association.class.getDeclaredField("_sharedStateDoNotCallMeDirectly")); - } catch (Throwable t) { - throw new ExceptionInInitializerError(t); - } - } -} diff --git a/remote/src/main/scala-2.12/org/apache/pekko/remote/artery/AssociationInline.scala b/remote/src/main/scala-2.12/org/apache/pekko/remote/artery/AssociationInline.scala new file mode 100644 index 00000000000..f359aa69c1d --- /dev/null +++ b/remote/src/main/scala-2.12/org/apache/pekko/remote/artery/AssociationInline.scala @@ -0,0 +1,36 @@ +/* + * 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.pekko.remote.artery + +import org.apache.pekko.annotation.InternalApi +import org.apache.pekko.util.Unsafe + +@InternalApi +private[artery] trait AssociationInline extends AbstractAssociation { + + /** + * Helper method for access to underlying state via Unsafe + * + * @param oldState Previous state + * @param newState Next state on transition + * @return Whether the previous state matched correctly + */ + @inline final def swapState(oldState: AssociationState, newState: AssociationState): Boolean = + Unsafe.instance.compareAndSwapObject(this, sharedStateOffset, oldState, newState) + +} diff --git a/remote/src/main/scala-2.13/org/apache/pekko/remote/artery/AssociationInline.scala b/remote/src/main/scala-2.13/org/apache/pekko/remote/artery/AssociationInline.scala new file mode 100644 index 00000000000..f359aa69c1d --- /dev/null +++ b/remote/src/main/scala-2.13/org/apache/pekko/remote/artery/AssociationInline.scala @@ -0,0 +1,36 @@ +/* + * 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.pekko.remote.artery + +import org.apache.pekko.annotation.InternalApi +import org.apache.pekko.util.Unsafe + +@InternalApi +private[artery] trait AssociationInline extends AbstractAssociation { + + /** + * Helper method for access to underlying state via Unsafe + * + * @param oldState Previous state + * @param newState Next state on transition + * @return Whether the previous state matched correctly + */ + @inline final def swapState(oldState: AssociationState, newState: AssociationState): Boolean = + Unsafe.instance.compareAndSwapObject(this, sharedStateOffset, oldState, newState) + +} diff --git a/remote/src/main/scala-3/org/apache/pekko/remote/artery/AssociationInline.scala b/remote/src/main/scala-3/org/apache/pekko/remote/artery/AssociationInline.scala new file mode 100644 index 00000000000..d831763b546 --- /dev/null +++ b/remote/src/main/scala-3/org/apache/pekko/remote/artery/AssociationInline.scala @@ -0,0 +1,36 @@ +/* + * 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.pekko.remote.artery + +import org.apache.pekko.annotation.InternalApi +import org.apache.pekko.util.Unsafe + +@InternalApi +private[artery] trait AssociationInline extends AbstractAssociation { + + /** + * Helper method for access to underlying state via Unsafe + * + * @param oldState Previous state + * @param newState Next state on transition + * @return Whether the previous state matched correctly + */ + inline final def swapState(oldState: AssociationState, newState: AssociationState): Boolean = + Unsafe.instance.compareAndSwapObject(this, sharedStateOffset, oldState, newState) + +} diff --git a/remote/src/main/scala/org/apache/pekko/remote/artery/AbstractAssociation.scala b/remote/src/main/scala/org/apache/pekko/remote/artery/AbstractAssociation.scala new file mode 100644 index 00000000000..98a390f4768 --- /dev/null +++ b/remote/src/main/scala/org/apache/pekko/remote/artery/AbstractAssociation.scala @@ -0,0 +1,30 @@ +/* + * 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, which was derived from Akka. + */ + +/* + * Copyright (C) 2016-2022 Lightbend Inc. + */ + +package org.apache.pekko.remote.artery + +import org.apache.pekko.util.Unsafe + +trait AbstractAssociation { + protected var sharedStateOffset = 0L + + try sharedStateOffset = + Unsafe.instance.objectFieldOffset(classOf[Association].getDeclaredField("_sharedStateDoNotCallMeDirectly")) + catch { + case t: Throwable => + throw new ExceptionInInitializerError(t) + } + +} + +object AbstractAssociation extends AbstractAssociation diff --git a/remote/src/main/scala/org/apache/pekko/remote/artery/Association.scala b/remote/src/main/scala/org/apache/pekko/remote/artery/Association.scala index e44c30140f3..3e83a3e66cf 100644 --- a/remote/src/main/scala/org/apache/pekko/remote/artery/Association.scala +++ b/remote/src/main/scala/org/apache/pekko/remote/artery/Association.scala @@ -150,7 +150,7 @@ private[remote] class Association( largeMessageDestinations: WildcardIndex[NotUsed], priorityMessageDestinations: WildcardIndex[NotUsed], outboundEnvelopePool: ObjectPool[ReusableOutboundEnvelope]) - extends AbstractAssociation + extends AssociationInline with OutboundContext { import Association._ @@ -263,22 +263,11 @@ private[remote] class Association( @nowarn("msg=never used") private[artery] var _sharedStateDoNotCallMeDirectly: AssociationState = AssociationState() - /** - * Helper method for access to underlying state via Unsafe - * - * @param oldState Previous state - * @param newState Next state on transition - * @return Whether the previous state matched correctly - */ - @inline - private[artery] def swapState(oldState: AssociationState, newState: AssociationState): Boolean = - Unsafe.instance.compareAndSwapObject(this, AbstractAssociation.sharedStateOffset, oldState, newState) - /** * @return Reference to current shared state */ def associationState: AssociationState = - Unsafe.instance.getObjectVolatile(this, AbstractAssociation.sharedStateOffset).asInstanceOf[AssociationState] + Unsafe.instance.getObjectVolatile(this, sharedStateOffset).asInstanceOf[AssociationState] def setControlIdleKillSwitch(killSwitch: OptionVal[SharedKillSwitch]): Unit = { val current = associationState