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
97 changes: 7 additions & 90 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,95 +1,12 @@
# native-loop (PRE-RELEASE)
Extensible event loop and async-oriented IO for Scala Native; powered by libuv.
# scala-native-loop

## UNDER CONSTRUCTION

If you're looking for the new 0.4 rewrite, check the `04` branch. The current state of master is mostly extracted from the book [Modern Systems Programming in Scala Native](https://pragprog.com/book/rwscala/modern-systems-programming-with-scala-native).
Async IO and event loop for Scala Native

## What is it?

scala-native-loop provides a real, asynchronous ExecutionContext implementation for Scala Native.
It's backed by libuv, the same C library that the node.js ecosystem runs on; in addition to basic
Future dispatching, we can also use libuv to provide other basic functionality, like:

- File IO
- Pipe IO
- TCP Sockets
- UDP Sockets
- Timers

To provide a working API for practical, async Scala Native programs, we have two subprojects,
`client` and `server`, which provide an async HTTP client and server, respectively, by integrating addtional C libraries: [nodejs/http-parser](https://github.com/nodejs/http-parser) for request parsing, and [curl](https://github.com/curl/curl) for a full featured client with HTTPS support.

That said - providing a full-featured ecosystem in a single library isn't feasible - instead, we provide a `LoopExtension` trait that allows other C libraries to be integrated to the underlying event loop, in the same way that libcurl and http-parser are integrated; this opens up the possiblity of fully asynchronous bindings for postgres, redis, and many others.

## Why is this here?

To demonstrate the architectural style of a full, extensible async ecosystem for Scala Native, with an idiomatic Future-based API, implemented entirely as a library, and to start discussion about what we our priorities are.

## LoopExtension trait

To attach a new library to the event loop, all we need to do is provide the `LoopExtension` trait:

```
trait LoopExtension {
def activeRequests:Int
}
```

And then register the component at runtime with `EventLoop.addExtension()`.

This is necessary because we need some way to know if there are pending IO tasks being managed by a C library, even if there are no outstanding Futures, and prevent the event loop from shutting down prematurely in that case.

## Maintenance Status

This code is a pre-release preview - I am cleaning up both the style and the implementation,
aiming to align with Scala Native 0.4 for something more robust.

For now, I'm filing issues to remind myself of work that needs to be done.

I'll also create a few "discussion" issues for broader conversation.

Please feel free to file additional issues with questions, comments, and concerns!

## Server API Example

```
def main(args:Array[String]):Unit = {
Service()
.getAsync("/async") { r => Future {
s"got (async routed) request $r"
}.map { message => OK(
Map("asyncMessage" -> message)
)
}
}
.getAsync("/fetch/example") { r =>
Curl.get(c"https://www.example.com").map { response =>
Response(200,"OK",Map(),response.body)
}
}
.get("/") { r => OK {
Map("default_message" -> s"got (default routed) request $r")
}
}
.run(9999)
uv_run(EventLoop.loop, UV_RUN_DEFAULT)
}
```

## Streaming API Example
scala-native-loop provides asynchronous utilities for Scala Native.
It's backed by libuv, the same C library that the Node.js ecosystem runs on.
It currently offers:

```
def main(args:Array[String]):Unit = {
val p = FilePipe(c"./data.txt")
.map { d =>
println(s"consumed $d")
d
}.addDestination(Tokenizer("\n"))
.addDestination(Tokenizer(" "))
.map { d => d + "\n" }
.addDestination(FileOutputPipe(c"./output.txt", false))
println("running")
uv_run(EventLoop.loop,UV_RUN_DEFAULT)
}
```
- `scala.scalanative.loop.Timer`: to schedule callbacks to execute after a timeout
- `scala.scalanative.loop.Poll`: to schedule callbacks when data is read/written on a file descriptor
85 changes: 0 additions & 85 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,9 @@ val publishSettings = Seq(
url("https://github.com/scala-native/scala-native-loop"),
"scm:git:git@github.com:scala-native/scala-native-loop.git"
)
),
developers := List(
Developer(
"rwhaling",
"Richard Whaling",
"richard@whaling.dev",
url("http://whaling.dev")
)
)
)

val noPublishSettings = Seq(
publish := {},
publishLocal := {},
publishArtifact := false,
skip in publish := true
)

lazy val commonSettings = Seq(
scalacOptions ++= Seq(
"-deprecation",
Expand All @@ -56,11 +41,6 @@ lazy val commonSettings = Seq(
),
libraryDependencies += "com.lihaoyi" %%% "utest" % "0.7.11" % Test,
testFrameworks += new TestFramework("utest.runner.Framework"),
Test / nativeLinkStubs := true,
)

lazy val examplesSettings = Seq(
test := {}
)

lazy val core = project
Expand All @@ -70,75 +50,10 @@ lazy val core = project
.settings(publishSettings)
.enablePlugins(ScalaNativePlugin)

lazy val pipe = project
.in(file("pipe"))
.settings(commonSettings)
.settings(test := {})
.settings(noPublishSettings)
.enablePlugins(ScalaNativePlugin)
.dependsOn(core)

lazy val client = project
.in(file("client"))
.settings(commonSettings)
.settings(test := {})
.settings(noPublishSettings)
.enablePlugins(ScalaNativePlugin)
.dependsOn(core)

lazy val server = project
.in(file("server"))
.settings(commonSettings)
.settings(test := {})
.settings(noPublishSettings)
.enablePlugins(ScalaNativePlugin)
.dependsOn(core)

lazy val scalaJsCompat = project
.in(file("scalajs-compat"))
.settings(name := "native-loop-js-compat")
.settings(commonSettings)
.settings(publishSettings)
.settings(test := {})
.enablePlugins(ScalaNativePlugin)
.dependsOn(core)

lazy val serverExample = project
.in(file("examples/server"))
.settings(
commonSettings,
examplesSettings
)
.settings(noPublishSettings)
.enablePlugins(ScalaNativePlugin)
.dependsOn(core, server, client)

lazy val pipeExample = project
.in(file("examples/pipe"))
.settings(
commonSettings,
examplesSettings
)
.settings(noPublishSettings)
.enablePlugins(ScalaNativePlugin)
.dependsOn(core, pipe, client)

lazy val curlExample = project
.in(file("examples/curl"))
.settings(
commonSettings,
examplesSettings
)
.settings(noPublishSettings)
.enablePlugins(ScalaNativePlugin)
.dependsOn(core, client)

lazy val timerExample = project
.in(file("examples/timer"))
.settings(
commonSettings,
examplesSettings
)
.settings(noPublishSettings)
.enablePlugins(ScalaNativePlugin)
.dependsOn(core)
Loading