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

dev.tauri.choam.data.GcHostileMsQueue.scala Maven / Gradle / Ivy

The newest version!
/*
 * SPDX-License-Identifier: Apache-2.0
 * Copyright 2016-2024 Daniel Urban and contributors listed in NOTICE.txt
 *
 * 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 dev.tauri.choam
package data

import GcHostileMsQueue._

/**
 * Unoptimized Michael-Scott queue (for benchmarks)
 *
 * It is GC-hostile, because it allocates a lot of
 * objects, including `Ref`s, which are only used once
 * (see `GcBench`), and also doesn't clear the next
 * link of dequed nodes (which can become cross-generational,
 * which is harder for the GC to deal with).
 *
 * It also lacks other optimizations present in
 * `MsQueue` (see there).
 */
private final class GcHostileMsQueue[A] private[this] (sentinel: Node[A])
  extends Queue[A] {

  private[this] val head: Ref[Node[A]] = Ref.unsafePadded(sentinel)
  private[this] val tail: Ref[Node[A]] = Ref.unsafePadded(sentinel)

  private def this() =
    this(Node(nullOf[A], Ref.unsafePadded(End[A]())))

  override val tryDeque: Axn[Option[A]] = {
    head.modifyWith { node =>
      node.next.get.flatMapF { next =>
        next match {
          case n @ Node(a, _) =>
            Rxn.pure((n.copy(data = nullOf[A]), Some(a)))
          case End() =>
            Rxn.pure((node, None))
        }
      }
    }
  }

  override val enqueue: Rxn[A, Unit] = Rxn.computed { (a: A) =>
    Ref.padded[Elem[A]](End()).flatMapF { newRef =>
      findAndEnqueue(Node(a, newRef))
    }
  }

  final override def tryEnqueue: Rxn[A, Boolean] =
    this.enqueue.as(true)

  private[this] def findAndEnqueue(node: Node[A]): Axn[Unit] = {
    def go(n: Node[A]): Axn[Unit] = {
      n.next.get.flatMapF {
        case End() =>
          // found true tail; will update, and adjust the tail ref:
          n.next.set.provide(node) >>> tail.set.provide(node)
        case nv @ Node(_, _) =>
          // not the true tail; try to catch up, and continue:
          go(n = nv)
      }
    }
    tail.get.flatMapF(go)
  }
}

private object GcHostileMsQueue {

  private sealed trait Elem[A]
  private final case class Node[A](data: A, next: Ref[Elem[A]]) extends Elem[A]
  private final case class End[A]() extends Elem[A]

  def apply[A]: Axn[GcHostileMsQueue[A]] =
    Rxn.unsafe.delay { _ => new GcHostileMsQueue[A] }

  def fromList[F[_], A](as: List[A])(implicit F: Reactive[F]): F[GcHostileMsQueue[A]] = {
    Queue.fromList[F, GcHostileMsQueue, A](this.apply[A])(as)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy