All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cc.otavia.core.stack.FutureDispatcher.scala Maven / Gradle / Ivy

/*
 * Copyright 2022 Yan Kun 
 *
 * Licensed 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 cc.otavia.core.stack

import scala.language.unsafeNulls

/** This abstract class is extend by [[cc.otavia.core.actor.AbstractActor]] for manage [[Future]]s like
 *  [[scala.collection.mutable.HashMap]]. We don't use hashmap because is box/unbox [[Long]] as key, this class is avoid
 *  this cost.
 */
private[core] abstract class FutureDispatcher {

    import FutureDispatcher.*

    private var table: Array[MessagePromise[?]] = _
    private var mask: Int                       = tableSizeFor(initialCapacity) - 1

    private var threshold: Int = newThreshold(tableSizeFor(initialCapacity))

    private var contentSize: Int = 0

    private final def loadFactor: Double   = 2.0
    private final def initialCapacity: Int = 16

    private def newThreshold(size: Int) = (size.toDouble * loadFactor).toInt

    inline private def index(id: Long): Int = (id & mask).toInt

    inline private def findNode(id: Long): MessagePromise[?] = {
        if (table ne null) {
            table(index(id)) match
                case null    => null
                case promise => promise.findNode(id)
        } else null
    }

    final protected def push(promise: MessagePromise[?]): Unit = {
        if (table eq null) table = new Array[MessagePromise[?]](tableSizeFor(initialCapacity))
        else if (contentSize + 1 >= threshold) resizeTable(table.length * 2)
        put0(promise)
    }

    private final def put0(promise: MessagePromise[?]): Unit = {
        val idx = index(promise.id)
        table(idx) match
            case null => table(idx) = promise
            case old =>
                var tail = old
                while (tail.hashNext ne null) tail = tail.hashNext
                tail.hashNext = promise
    }

    final protected def pop(id: Long): MessagePromise[?] = {
        val idx = index(id)
        val promise = table(idx) match
            case null => null
            case node if node.id == id =>
                table(idx) = node.hashNext
                contentSize -= 1
                node.hashNext = null
                node
            case node =>
                var prev   = node
                var cursor = node.hashNext
                while ((cursor ne null) && cursor.id != id) {
                    prev = cursor
                    cursor = cursor.hashNext
                }
                if (cursor ne null) {
                    prev.hashNext = cursor.hashNext
                    contentSize -= 1
                    cursor.hashNext = null
                }
                cursor

        if (table.length >= initialCapacity * 4 && contentSize < table.length / 2) { // shrinkage the hash table
            resizeTable(table.length / 2)
        }
        promise
    }

    final protected def contains(id: Long): Boolean = findNode(id) ne null

    private final def resizeTable(newLen: Int): Unit = {
        val oldTable = table
        table = new Array[MessagePromise[?]](newLen)
        mask = newLen - 1
        threshold = newThreshold(table.length)

        for (node <- oldTable) {
            var cursor = node
            while (cursor ne null) {
                val promise = cursor
                cursor = cursor.hashNext
                promise.hashNext = null
                put0(promise)
            }
        }
    }

}

private[core] object FutureDispatcher {
    private[stack] def tableSizeFor(capacity: Int): Int =
        (Integer.highestOneBit((capacity - 1).max(4)) * 2).min(1 << 30)

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy