-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New ParallelUnorderedStreamProcessor
- Loading branch information
1 parent
5d0dee0
commit 2568e06
Showing
7 changed files
with
150 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,4 +49,4 @@ class ParallelStreamSpec extends AnyWordSpec with Matchers { | |
task.sync().sum should be(1_409_965_408) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package rapid | ||
|
||
sealed trait Opt[+A] extends Any { | ||
def isEmpty: Boolean | ||
def isNonEmpty: Boolean = !isEmpty | ||
} | ||
|
||
object Opt { | ||
case class Value[+A](value: A) extends AnyVal with Opt[A] { | ||
override def isEmpty: Boolean = false | ||
} | ||
case object Empty extends Opt[Nothing] { | ||
override def isEmpty: Boolean = true | ||
} | ||
|
||
def apply[A](value: A): Opt[A] = if (value == null) { | ||
Empty | ||
} else { | ||
Value(value) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
core/shared/src/main/scala/rapid/ParallelUnorderedStreamProcessor.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package rapid | ||
|
||
import scala.annotation.tailrec | ||
|
||
case class ParallelUnorderedStreamProcessor[T, R](stream: ParallelStream[T, R], | ||
handle: R => Unit, | ||
complete: Int => Unit) { | ||
private val iteratorTask: Task[Iterator[T]] = Stream.task(stream.stream) | ||
private val ready = Queue[R](stream.maxBuffer) | ||
private val processing = Queue[Task[Unit]](stream.maxThreads) | ||
@volatile var counter = -1 | ||
|
||
// Processes the iterator feeding through processing and finally into ready | ||
iteratorTask.map { iterator => | ||
var size = 0 | ||
iterator.foreach { t => | ||
var task: Task[Unit] = null | ||
task = stream.f(t).map { r => | ||
ready.add(r) | ||
processing.remove(task) | ||
} | ||
processing.add(task) | ||
task.start() | ||
size += 1 | ||
} | ||
counter = size | ||
}.start() | ||
|
||
// Processes through the ready queue feeding to handle and finally complete | ||
Task(handleNext(0)).start() | ||
|
||
@tailrec | ||
private def handleNext(counter: Int): Unit = { | ||
val next = ready.poll() | ||
if (this.counter == counter) { | ||
complete(counter) | ||
} else { | ||
val c = next match { | ||
case Opt.Value(value) => | ||
handle(value) | ||
counter + 1 | ||
case Opt.Empty => counter | ||
} | ||
handleNext(c) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package rapid | ||
|
||
import java.util.concurrent.ConcurrentLinkedQueue | ||
import java.util.concurrent.atomic.AtomicInteger | ||
|
||
case class Queue[T](maxSize: Int) { | ||
private val q = new ConcurrentLinkedQueue[T] | ||
private val s = new AtomicInteger(0) | ||
|
||
def size: Int = s.get() | ||
|
||
def isEmpty: Boolean = size == 0 | ||
|
||
def tryAdd(value: T): Boolean = { | ||
var incremented = false | ||
s.updateAndGet((operand: Int) => { | ||
if (operand < maxSize) { | ||
incremented = true | ||
operand + 1 | ||
} else { | ||
incremented = false | ||
operand | ||
} | ||
}) | ||
if (incremented) { | ||
q.add(value) | ||
} | ||
incremented | ||
} | ||
|
||
def add(value: T): Unit = while (!tryAdd(value)) { | ||
Thread.`yield`() | ||
} | ||
|
||
def poll(): Opt[T] = { | ||
val o = Opt(q.poll()) | ||
if (o.isNonEmpty) { | ||
s.decrementAndGet() | ||
} | ||
o | ||
} | ||
|
||
def remove(value: T): Boolean = { | ||
val removed = q.remove(value) | ||
if (removed) { | ||
s.decrementAndGet() | ||
} | ||
removed | ||
} | ||
} |