Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,60 +7,21 @@ object PartitionedLinkedQueueSpec extends ZIOBaseSpec {

def spec = suite("PartitionedLinkedQueueSpec")(
test("partitions round to nearest power of 2") {
val q = new PartitionedLinkedQueue[String](9, addMetrics = false)
val q = new PartitionedLinkedQueue[String](9)

assertTrue(q.nPartitions() == 16)
},
test("addMetrics = true") {
val q = new PartitionedLinkedQueue[String](9, addMetrics = true)

q.offer("1")
q.offerAll(List("2", "3", "4"))
q.poll(null)
q.pollUpTo(2)
val enq = q.enqueuedCount()
val deq1 = q.dequeuedCount()
q.pollUpTo(10)
val deq2 = q.dequeuedCount()

assertTrue(enq == 4, deq1 == 3, deq2 == 4)
},
test("addMetrics = false") {
val q = new PartitionedLinkedQueue[String](9, addMetrics = false)

q.offer("1")
q.poll(null)

assertTrue(q.enqueuedCount() == 0, q.dequeuedCount() == 0)
},
test("addMetrics = false") {
val q = new PartitionedLinkedQueue[String](9, addMetrics = false)

q.offer("1")
q.poll(null)

assertTrue(q.enqueuedCount() == 0, q.dequeuedCount() == 0)
},
test("offerAll and pollUpTo items") {
val q = new PartitionedLinkedQueue[String](9, addMetrics = false)

val oneToHundred = (1 to 100).map(_.toString)
q.offerAll(oneToHundred)
val polled = q.pollUpTo(100)

assertTrue(polled.toSet == oneToHundred.toSet)
} @@ TestAspect.nonFlaky,
test("offer and poll items") {
val q = new PartitionedLinkedQueue[String](9, addMetrics = false)
val q = new PartitionedLinkedQueue[String](9)

val oneToHundred = (1 to 100).map(_.toString)
oneToHundred.foreach(q.offer)
val polled = (1 to 100).map(_ => q.poll(null)).toSet
val polled = (1 to 100).map(_ => q.poll()).toSet

assertTrue(polled == oneToHundred.toSet)
} @@ TestAspect.nonFlaky,
test("queue size") {
val q = new PartitionedLinkedQueue[String](9, addMetrics = false)
val q = new PartitionedLinkedQueue[String](9)

q.offerAll((1 to 3).map(_.toString))
assertTrue(q.size() == 3)
Expand Down
20 changes: 10 additions & 10 deletions core/jvm/src/main/scala/zio/internal/ZScheduler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package zio.internal
import zio._
import zio.stacktracer.TracingImplicits.disableAutoTrace

import java.util.concurrent.ThreadLocalRandom
import java.util.concurrent.{ConcurrentLinkedQueue, ThreadLocalRandom}
import java.util.concurrent.atomic.{AtomicInteger, AtomicLong}
import java.util.concurrent.locks.LockSupport
import scala.collection.mutable
Expand All @@ -32,9 +32,9 @@ import scala.collection.mutable
private final class ZScheduler(autoBlocking: Boolean) extends Executor {

private[this] val poolSize = java.lang.Runtime.getRuntime.availableProcessors
private[this] val globalQueue = new PartitionedLinkedQueue[Runnable](poolSize * 4, addMetrics = false)
private[this] val cache = new LinkedQueue[ZScheduler.Worker](addMetrics = false)
private[this] val idle = new LinkedQueue[ZScheduler.Worker](addMetrics = false)
private[this] val globalQueue = new PartitionedLinkedQueue[Runnable](poolSize * 4)
private[this] val cache = new ConcurrentLinkedQueue[ZScheduler.Worker]()
private[this] val idle = new ConcurrentLinkedQueue[ZScheduler.Worker]()
private[this] val globalLocations = makeLocations()
private[this] val state = new AtomicInteger(poolSize << 16)
private[this] val workers = Array.ofDim[ZScheduler.Worker](poolSize)
Expand Down Expand Up @@ -116,7 +116,7 @@ private final class ZScheduler(autoBlocking: Boolean) extends Executor {
} else {
runnable = worker.localQueue.poll(null)
if (runnable eq null) {
runnable = globalQueue.poll(null)
runnable = globalQueue.poll()
}
}

Expand Down Expand Up @@ -278,7 +278,7 @@ private final class ZScheduler(autoBlocking: Boolean) extends Executor {
currentWorker.blocking = true
val runnables = currentWorker.localQueue.pollUpTo(256)
globalQueue.offerAll(runnables)
val worker = cache.poll(null)
val worker = cache.poll()
if (worker eq null) {
val worker = makeWorker()
worker.setName(s"ZScheduler-Worker-$workerId")
Expand Down Expand Up @@ -330,7 +330,7 @@ private final class ZScheduler(autoBlocking: Boolean) extends Executor {
}
} else {
if ((currentOpCount & 63) == 0) {
runnable = globalQueue.poll(null, random)
runnable = globalQueue.poll(random)
if (runnable eq null) {
if (nextRunnable ne null) {
runnable = nextRunnable
Expand All @@ -346,7 +346,7 @@ private final class ZScheduler(autoBlocking: Boolean) extends Executor {
} else {
runnable = localQueue.poll(null)
if (runnable eq null) {
runnable = globalQueue.poll(null, random)
runnable = globalQueue.poll(random)
}
}
}
Expand Down Expand Up @@ -389,7 +389,7 @@ private final class ZScheduler(autoBlocking: Boolean) extends Executor {
i += 1
}
if (runnable eq null) {
runnable = globalQueue.poll(null, random)
runnable = globalQueue.poll(random)
}
}
}
Expand Down Expand Up @@ -448,7 +448,7 @@ private final class ZScheduler(autoBlocking: Boolean) extends Executor {
val currentSearching = currentState & 0xffff
val currentActive = (currentState & 0xffff0000) >> 16
if (currentActive != poolSize && currentSearching == 0) {
val worker = idle.poll(null)
val worker = idle.poll()
if (worker ne null) {
state.getAndAdd(0x10001)
worker.active = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ private[zio] object MutableConcurrentQueue {
else RingBuffer[A](capacity)

def unbounded[A]: MutableConcurrentQueue[A] =
new LinkedQueue[A](addMetrics = true)
new LinkedQueue[A]

/**
* Rounds up to the nearest power of 2 and subtracts 1. e.g.,
Expand Down
19 changes: 8 additions & 11 deletions core/shared/src/main/scala/zio/internal/impls/LinkedQueue.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,44 +23,41 @@ import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.atomic.AtomicLong
import scala.annotation.nowarn

private[zio] final class LinkedQueue[A](addMetrics: Boolean = true)
extends MutableConcurrentQueue[A]
with Serializable {
private[zio] final class LinkedQueue[A] extends MutableConcurrentQueue[A] with Serializable {
override final val capacity = Int.MaxValue

private[this] val jucConcurrentQueue = new ConcurrentLinkedQueue[A]()

/*
* Using increment on AtomicLongs to provide metrics '''will''' have
* performance implications. Having a better solution would be
* desirable.
*/
private[this] val enqueuedCounter = if (addMetrics) new AtomicLong(0) else null
private[this] val dequeuedCounter = if (addMetrics) new AtomicLong(0) else null
private[this] val enqueuedCounter = new AtomicLong(0)
private[this] val dequeuedCounter = new AtomicLong(0)

override def size(): Int = jucConcurrentQueue.size()

override def enqueuedCount(): Long = if (enqueuedCounter ne null) enqueuedCounter.get() else 0L
override def enqueuedCount(): Long = enqueuedCounter.get()

override def dequeuedCount(): Long = if (dequeuedCounter ne null) dequeuedCounter.get() else 0L
override def dequeuedCount(): Long = dequeuedCounter.get()

override def offer(a: A): Boolean = {
val success = jucConcurrentQueue.offer(a)
if (success && (enqueuedCounter ne null)) enqueuedCounter.incrementAndGet()
if (success) enqueuedCounter.incrementAndGet()
success
}

override def offerAll[A1 <: A](as: Iterable[A1]): Chunk[A1] = {
import collection.JavaConverters._
jucConcurrentQueue.addAll(as.asJavaCollection): @nowarn("msg=JavaConverters")
if (enqueuedCounter ne null) enqueuedCounter.addAndGet(as.size.toLong)
enqueuedCounter.addAndGet(as.size.toLong)
Chunk.empty
}

override def poll(default: A): A = {
val polled = jucConcurrentQueue.poll()
if (polled != null) {
if (dequeuedCounter ne null) dequeuedCounter.incrementAndGet()
dequeuedCounter.incrementAndGet()
polled
} else default
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,17 @@ package zio.internal
import zio.Chunk
import zio.stacktracer.TracingImplicits.disableAutoTrace

import java.util.concurrent.ThreadLocalRandom
import java.util.concurrent.{ConcurrentLinkedQueue, ThreadLocalRandom}

private[zio] final class PartitionedLinkedQueue[A <: AnyRef](
preferredPartitions: Int,
addMetrics: Boolean
) extends MutableConcurrentQueue[A]
with Serializable {

override final val capacity = Int.MaxValue
private final class PartitionedLinkedQueue[A <: AnyRef](preferredPartitions: Int) extends Serializable {

private[this] val mask = MutableConcurrentQueue.roundToPow2MinusOne(preferredPartitions)
private[this] val nQueues = mask + 1
private[this] val queues = Array.fill(nQueues)(new LinkedQueue[A](addMetrics = addMetrics))
private[this] val queues = Array.fill(nQueues)(new ConcurrentLinkedQueue[A]())

def nPartitions(): Int = nQueues

override def size(): Int = {
def size(): Int = {
val from = ThreadLocalRandom.current().nextInt(nQueues)
var i = 0
var size = 0
Expand All @@ -47,39 +41,12 @@ private[zio] final class PartitionedLinkedQueue[A <: AnyRef](
size
}

override def enqueuedCount(): Long =
if (addMetrics) {
val from = ThreadLocalRandom.current().nextInt(nQueues)
var i = 0
var size = 0L
while (i < nQueues) {
val idx = (from + i) & mask
size += queues(idx).enqueuedCount()
i += 1
}
size
} else 0L

override def dequeuedCount(): Long =
if (addMetrics) {
val random = ThreadLocalRandom.current()
val from = random.nextInt(nQueues)
var i = 0
var size = 0L
while (i < nQueues) {
val idx = (from + i) & mask
size += queues(idx).dequeuedCount()
i += 1
}
size
} else 0L

def offer(a: A, random: ThreadLocalRandom): Unit = {
val idx = random.nextInt(nQueues)
queues(idx).offer(a)
}

override def offer(a: A): Boolean = {
def offer(a: A): Boolean = {
offer(a, ThreadLocalRandom.current())
true
}
Expand All @@ -96,38 +63,36 @@ private[zio] final class PartitionedLinkedQueue[A <: AnyRef](
}
}

override def offerAll[A1 <: A](as: Iterable[A1]): Chunk[A1] = {
def offerAll[A1 <: A](as: Iterable[A1]): Chunk[A1] = {
offerAll(as, ThreadLocalRandom.current())
Chunk.empty
}

def poll(default: A, random: ThreadLocalRandom): A = {
def poll(random: ThreadLocalRandom): A = {
val from = random.nextInt(nQueues)
var i = 0
var result = null.asInstanceOf[A]
while ((result eq null) && i < nQueues) {
val idx = (from + i) & mask
result = queues(idx).poll(default)
result = queues(idx).poll()
i += 1
}
result
}

override def poll(default: A): A =
poll(default, ThreadLocalRandom.current())
def poll(): A =
poll(ThreadLocalRandom.current())

override def isEmpty(): Boolean = {
def isEmpty(): Boolean = {
val random = ThreadLocalRandom.current()
val from = random.nextInt(nQueues)
var i = 0
var result = true
while (result && i < nQueues) {
val idx = (from + i) & mask
result = queues(idx).isEmpty()
result = queues(idx).isEmpty
i += 1
}
result
}

override def isFull(): Boolean = false
}