-
-
Notifications
You must be signed in to change notification settings - Fork 576
Add ownPoller
#4133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ownPoller
#4133
Changes from all commits
7c610bb
5489d3a
a2fd0cb
26ef827
c125e73
cef80fe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -50,13 +50,8 @@ abstract class PollingSystem { | |
|
|
||
| /** | ||
| * Creates a new instance of the user-facing interface. | ||
| * | ||
| * @param access | ||
| * callback to obtain a thread-local `Poller`. | ||
| * @return | ||
| * an instance of the user-facing interface `Api`. | ||
| */ | ||
| def makeApi(access: (Poller => Unit) => Unit): Api | ||
| def makeApi(ctx: PollingContext[Poller]): Api | ||
|
|
||
| /** | ||
| * Creates a new instance of the thread-local data structure used for polling. | ||
|
|
@@ -109,7 +104,24 @@ abstract class PollingSystem { | |
|
|
||
| } | ||
|
|
||
| private object PollingSystem { | ||
| sealed trait PollingContext[P] { | ||
|
|
||
| /** | ||
| * Register a callback to obtain a thread-local `Poller` | ||
| */ | ||
| def accessPoller(cb: P => Unit): Unit | ||
|
|
||
| /** | ||
| * Returns `true` if it is safe to interact with this `Poller`. Implementors of this method | ||
| * may be best-effort: it is always safe to return `false`, so callers must have an adequate | ||
| * fallback for the non-owning case. | ||
| */ | ||
| def ownPoller(poller: P): Boolean | ||
| } | ||
|
|
||
| private[unsafe] trait UnsealedPollingContext[P] extends PollingContext[P] | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Before you tell me that
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks Scala! |
||
|
|
||
| object PollingSystem { | ||
|
|
||
| /** | ||
| * Type alias for a `PollingSystem` that has a specified `Poller` type. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ import scala.util.control.NonFatal | |
|
|
||
| import java.nio.channels.SelectableChannel | ||
| import java.nio.channels.spi.{AbstractSelector, SelectorProvider} | ||
| import java.util.Iterator | ||
|
|
||
| import SelectorSystem._ | ||
|
|
||
|
|
@@ -30,8 +31,8 @@ final class SelectorSystem private (provider: SelectorProvider) extends PollingS | |
|
|
||
| def close(): Unit = () | ||
|
|
||
| def makeApi(access: (Poller => Unit) => Unit): Selector = | ||
| new SelectorImpl(access, provider) | ||
| def makeApi(ctx: PollingContext[Poller]): Selector = | ||
| new SelectorImpl(ctx, provider) | ||
|
|
||
| def makePoller(): Poller = new Poller(provider.openSelector()) | ||
|
|
||
|
|
@@ -68,29 +69,21 @@ final class SelectorSystem private (provider: SelectorProvider) extends PollingS | |
|
|
||
| val value = if (error ne null) Left(error) else Right(readyOps) | ||
|
|
||
| var head: CallbackNode = null | ||
| var prev: CallbackNode = null | ||
| var node = key.attachment().asInstanceOf[CallbackNode] | ||
| while (node ne null) { | ||
| val next = node.next | ||
| val callbacks = key.attachment().asInstanceOf[Callbacks] | ||
| val iter = callbacks.iterator() | ||
| while (iter.hasNext()) { | ||
| val node = iter.next() | ||
|
|
||
| if ((node.interest & readyOps) != 0) { // execute callback and drop this node | ||
| if ((node.interest & readyOps) != 0) { // drop this node and execute callback | ||
| node.remove() | ||
| val cb = node.callback | ||
| if (cb != null) { | ||
| cb(value) | ||
| polled = true | ||
| } | ||
| if (prev ne null) prev.next = next | ||
| } else { // keep this node | ||
| prev = node | ||
| if (head eq null) | ||
| head = node | ||
| } | ||
|
|
||
| node = next | ||
| } | ||
|
|
||
| key.attach(head) // if key was canceled this will null attachment | ||
| () | ||
| } | ||
|
|
||
|
|
@@ -107,42 +100,38 @@ final class SelectorSystem private (provider: SelectorProvider) extends PollingS | |
| } | ||
|
|
||
| final class SelectorImpl private[SelectorSystem] ( | ||
| access: (Poller => Unit) => Unit, | ||
| ctx: PollingContext[Poller], | ||
| val provider: SelectorProvider | ||
| ) extends Selector { | ||
|
|
||
| def select(ch: SelectableChannel, ops: Int): IO[Int] = IO.async { selectCb => | ||
| IO.async_[CallbackNode] { cb => | ||
| access { data => | ||
| IO.async_[Option[IO[Unit]]] { cb => | ||
| ctx.accessPoller { poller => | ||
| try { | ||
| val selector = data.selector | ||
| val selector = poller.selector | ||
| val key = ch.keyFor(selector) | ||
|
|
||
| val node = if (key eq null) { // not yet registered on this selector | ||
| val node = new CallbackNode(ops, selectCb, null) | ||
| ch.register(selector, ops, node) | ||
| node | ||
| val cbs = new Callbacks | ||
| ch.register(selector, ops, cbs) | ||
| cbs.append(ops, selectCb) | ||
| } else { // existing key | ||
| // mixin the new interest | ||
| key.interestOps(key.interestOps() | ops) | ||
| val node = | ||
| new CallbackNode(ops, selectCb, key.attachment().asInstanceOf[CallbackNode]) | ||
| key.attach(node) | ||
| node | ||
| val cbs = key.attachment().asInstanceOf[Callbacks] | ||
| cbs.append(ops, selectCb) | ||
| } | ||
|
|
||
| val cancel = IO { | ||
| if (ctx.ownPoller(poller)) | ||
| node.remove() | ||
| else | ||
| node.clear() | ||
| } | ||
|
|
||
| cb(Right(node)) | ||
| cb(Right(Some(cancel))) | ||
| } catch { case ex if NonFatal(ex) => cb(Left(ex)) } | ||
| } | ||
| }.map { node => | ||
| Some { | ||
| IO { | ||
| // set all interest bits | ||
| node.interest = -1 | ||
| // clear for gc | ||
| node.callback = null | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -161,9 +150,55 @@ object SelectorSystem { | |
|
|
||
| def apply(): SelectorSystem = apply(SelectorProvider.provider()) | ||
|
|
||
| private final class CallbackNode( | ||
| var interest: Int, | ||
| var callback: Either[Throwable, Int] => Unit, | ||
| var next: CallbackNode | ||
| ) | ||
| private final class Callbacks { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We've built so many of these… |
||
|
|
||
| private var head: Node = null | ||
| private var last: Node = null | ||
|
|
||
| def append(interest: Int, callback: Either[Throwable, Int] => Unit): Node = { | ||
| val node = new Node(interest, callback) | ||
| if (last ne null) { | ||
| last.next = node | ||
| node.prev = last | ||
| } else { | ||
| head = node | ||
| } | ||
| last = node | ||
| node | ||
| } | ||
|
|
||
| def iterator(): Iterator[Node] = new Iterator[Node] { | ||
| private var _next = head | ||
|
|
||
| def hasNext() = _next ne null | ||
|
|
||
| def next() = { | ||
| val next = _next | ||
| _next = next.next | ||
| next | ||
| } | ||
| } | ||
|
|
||
| final class Node( | ||
| var interest: Int, | ||
| var callback: Either[Throwable, Int] => Unit | ||
| ) { | ||
| var prev: Node = null | ||
| var next: Node = null | ||
|
|
||
| def remove(): Unit = { | ||
| if (prev ne null) prev.next = next | ||
| else head = next | ||
|
|
||
| if (next ne null) next.prev = prev | ||
| else last = prev | ||
| } | ||
|
|
||
| def clear(): Unit = { | ||
| interest = -1 // set all interest bits | ||
| callback = null // clear for gc | ||
| } | ||
| } | ||
| } | ||
|
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.