Skip to content
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

One module, blocking & non-blocking operations, misc. #385

Merged
merged 134 commits into from
Sep 6, 2021
Merged
Show file tree
Hide file tree
Changes from 132 commits
Commits
Show all changes
134 commits
Select commit Hold shift + click to select a range
603aa0d
Improve end-of-stream handling.
quelgar Sep 15, 2020
91cd09b
Update docs/essentials/index.md
quelgar Sep 20, 2020
8baadba
Switch to using ZManaged for core resources.
quelgar Sep 9, 2020
3cbd28d
Add resource management documentation.
quelgar Sep 9, 2020
f593378
Fix extension method on Scala 3.
quelgar Sep 23, 2020
3d877e3
Merge remote-tracking branch 'origin/quelgar/eof-handling' into quelg…
quelgar Sep 25, 2020
a6e2eca
Separate blocking and non-blocking APIs.
quelgar Sep 27, 2020
4ce31e5
Improvements to WatchService.
quelgar Jul 18, 2020
bbb6106
Add utility methods and example for directory watching.
quelgar Jul 24, 2020
51a4545
Improvements to Files.
quelgar Jul 18, 2020
4f76514
Move Files object into core module.
quelgar Jul 24, 2020
b359ad6
Cherry-pick fixups.
quelgar Sep 28, 2020
8f83795
Add streaming read and write.
quelgar Sep 29, 2020
7812290
Add example stream-based TCP server.
quelgar Sep 29, 2020
c45fc26
Improve InetSocketAddress and socket binding APIs.
quelgar Sep 29, 2020
eeac5f6
Test that blocking read/write/accept can be interrupted.
quelgar Sep 29, 2020
8d11612
Minor improvements to FileChannel and FileLock.
quelgar Aug 8, 2020
9fe83f4
Change organization from zio.
quelgar Sep 30, 2020
3e99cf7
Remove non-core project.
quelgar Sep 30, 2020
e82b098
Rename nio-core project to nio.
quelgar Sep 30, 2020
1053bcd
Rename zio.nio.core package to zio.nio.
quelgar Sep 30, 2020
60e9790
Make Selector#select interruptible.
quelgar Oct 1, 2020
e10ddf2
Fix SelectableChannel#register error type.
quelgar Oct 5, 2020
c87c79a
Improve `SelectableChannel#register` API.
quelgar Oct 9, 2020
67f0d9f
Improve InetSocketAddress API.
quelgar Oct 9, 2020
fd43c6d
Don't hold two references to underlying Java buffer.
quelgar Oct 9, 2020
954b571
Improvements to address handling.
quelgar Oct 10, 2020
206fda5
Fix several typos in docs (#261)
oranda Oct 6, 2020
4ce46ab
Add tests for Buffer#put (#264)
oranda Oct 7, 2020
666aba6
Update sbt-mdoc to 2.2.7 (#250)
scala-steward Oct 10, 2020
09b9d98
Update sbt-mdoc to 2.2.9 (#257)
scala-steward Oct 10, 2020
31c291a
Update sbt-tpolecat to 0.1.14 (#265)
scala-steward Oct 10, 2020
67bd52d
Update dotty to 0.27.0-RC1 (#267)
jczuchnowski Oct 10, 2020
898fd3a
Update scalafmt-core to 2.7.4.
quelgar Oct 10, 2020
b84c7b6
Update zio, zio-streams, zio-test, ... to 1.0.3 (#269)
scala-steward Oct 10, 2020
93202fe
Update index.md
jczuchnowski Oct 10, 2020
13027bf
Add read and write permissions to opened file (#270)
z4f1r0v Oct 21, 2020
67045c7
Add localhost constructor to InetSocketAddress.
quelgar Oct 26, 2020
fbd793d
Improvements to async channels.
quelgar Oct 26, 2020
08c786d
SBT 1.4.1.
quelgar Oct 28, 2020
1c3c1d8
Add stream/sink to AsynchronousFileChannel.
quelgar Nov 12, 2020
f2984a6
Minor WatchService improvements.
quelgar Nov 12, 2020
e237fd6
Document changes in this branch.
quelgar Oct 28, 2020
5b42a99
Improve end-of-stream handling. (#255)
quelgar Oct 29, 2020
ce49fca
Update scalafmt-core to 2.7.5 (#272)
scala-steward Nov 3, 2020
260c2cf
Update sbt-mdoc to 2.2.10 (#273)
scala-steward Nov 3, 2020
327d7b5
Update sbt-ci-release to 1.5.4 (#279)
scala-steward Nov 3, 2020
7e85516
Update sbt-bloop to 1.4.5 (#281)
scala-steward Nov 5, 2020
16a0433
Update sbt to 1.4.3 (#285)
scala-steward Nov 18, 2020
7655ee1
Update sbt-tpolecat to 0.1.15 (#283)
scala-steward Nov 18, 2020
caed3b5
Update sbt-dotty to 0.4.6 (#284)
scala-steward Nov 18, 2020
417b989
Update sbt-bloop to 1.4.6 (#290)
scala-steward Dec 9, 2020
a632621
Update sbt to 1.4.6 (#298)
scala-steward Dec 28, 2020
ca3cc40
Update scala-collection-compat to 2.3.2 (#297)
scala-steward Dec 28, 2020
984cc1f
Update sbt-ci-release to 1.5.5 (#292)
scala-steward Dec 28, 2020
f8a3d84
Update sbt-tpolecat to 0.1.16 (#291)
scala-steward Dec 28, 2020
7861568
Migrate to github actions (#300)
softinio Dec 30, 2020
4ab179b
Review updates (#301)
softinio Dec 30, 2020
ecaf0b1
Update release trigger
mijicd Jan 30, 2021
02b0510
Delete file cleanup
mijicd Jan 30, 2021
a902c52
Simplify CI
mijicd Jan 30, 2021
917689d
Update sbt to 1.4.7 (#307)
scala-steward Jan 31, 2021
5ae8df0
Update scala-collection-compat to 2.4.1 (#306)
scala-steward Feb 12, 2021
f6afcc5
Update Scala dependencies (#299)
jczuchnowski Feb 12, 2021
f2eb146
Update sbt-dotty to 0.5.2 (#304)
scala-steward Feb 12, 2021
0016dd9
update mdoc (#309)
pshemass Feb 12, 2021
77921fe
Update sbt-bloop to 1.4.7 (#310)
scala-steward Feb 16, 2021
814cb1c
Update sbt-bloop to 1.4.8 (#314)
scala-steward Feb 26, 2021
55bbc74
Update mdoc_2.13, sbt-mdoc to 2.2.18 (#313)
scala-steward Feb 26, 2021
47a11a6
Update scala-collection-compat to 2.4.2 (#312)
scala-steward Feb 26, 2021
7b3cb3d
Update sbt-dotty to 0.5.3 (#311)
scala-steward Feb 26, 2021
a33818d
Update sbt to 1.4.9 (#318)
scala-steward Mar 10, 2021
4f7c0cb
Update codeowners
softinio Mar 20, 2021
45331a2
Update sbt-ci-release to 1.5.7 (#328)
scala-steward Mar 28, 2021
fbbf5d9
[#323] added Files.createTemp* Managed methods; added Files.deleteRec…
vitaliihonta Apr 5, 2021
f76fde4
Update sbt-dotty to 0.5.5 (#336)
scala-steward May 4, 2021
60cf821
Update to the latest standard (#339)
sideeffffect May 11, 2021
2d2979a
Update sbt-explicit-dependencies to 0.2.16 (#340)
scala-steward May 11, 2021
8f88491
Update Node.js to v14.17.0 (#342)
renovate[bot] May 12, 2021
46bd091
Update sbt-jmh to 0.4.1 (#343)
scala-steward May 12, 2021
cdf57d1
Fix website (#341)
sideeffffect May 12, 2021
310254a
Prevent issues with Website in CI (#344)
sideeffffect May 12, 2021
70cca69
Update README.md
ghostdogpr May 12, 2021
85574f6
Update kind-projector to 0.12.0 (#346)
scala-steward May 13, 2021
e9a2d54
Update sbt-mima-plugin to 0.9.1 (#347)
scala-steward May 13, 2021
a28b342
Update scala-collection-compat to 2.4.4 (#348)
scala-steward May 14, 2021
6ac5b12
Update mdoc, sbt-mdoc to 2.2.21 (#349)
scala-steward May 14, 2021
1b7f287
Update kind-projector to 0.13.0 (#350)
scala-steward May 15, 2021
b0dbf7e
Update silencer-lib, silencer-lib_2.13.5, ... to 1.7.4 (#353)
scala-steward May 18, 2021
00a1b44
Update sbt-scalafix to 0.9.28 (#354)
scala-steward May 20, 2021
f1cd65a
Bumped zio to 1.0.8 (#355)
ithinkicancode May 21, 2021
c9db10c
Add test coverage plugin, fixes #266 (#321)
softdevca May 21, 2021
a5ec108
Update sbt-scoverage to 1.8.1 (#356)
scala-steward May 21, 2021
db91048
Update sbt-mima-plugin to 0.9.2 (#358)
scala-steward May 26, 2021
ef681a2
Update sbt-scoverage to 1.8.2 (#360)
scala-steward May 28, 2021
d6fcf7a
Update silencer-lib, silencer-lib_2.13.6, ... to 1.7.5 (#361)
scala-steward May 29, 2021
3ddee66
Update sbt-scalafix to 0.9.29 (#362)
scala-steward May 30, 2021
74ecabc
Update olafurpg/setup-scala action to v11 (#363)
renovate[bot] May 31, 2021
15ae6cd
Update olafurpg/setup-scala action to v12 (#364)
renovate[bot] Jun 1, 2021
1580ded
Update zio, zio-streams, zio-test, ... to 1.0.9 (#367)
scala-steward Jun 2, 2021
7c03a97
Update sbt-scalajs to 1.6.0 (#369)
scala-steward Jun 8, 2021
122aa63
Update sbt to 1.5.4 (#370)
scala-steward Jun 14, 2021
55b7ef0
Update Node.js to v14.17.1 (#371)
renovate[bot] Jun 15, 2021
0187147
Update scala-library to 2.12.14 (#359)
scala-steward Jun 22, 2021
d097072
Fix FilesSpec import
svroonland Aug 8, 2021
2d8058c
Update Node.js to v14.17.2 (#373)
renovate[bot] Jul 1, 2021
d406f78
Update Node.js to v14.17.3 (#375)
renovate[bot] Jul 5, 2021
73d06da
Update sbt-scalafmt to 2.4.3 (#376)
scala-steward Jul 8, 2021
bd17470
Update scala-collection-compat to 2.5.0 (#377)
scala-steward Jul 9, 2021
8fe59b0
Update sbt to 1.5.5 (#379)
scala-steward Jul 12, 2021
a2666c6
Update mdoc, sbt-mdoc to 2.2.22 (#380)
scala-steward Jul 22, 2021
4a42186
Update zio, zio-streams, zio-test, ... to 1.0.10 (#381)
scala-steward Jul 27, 2021
19c963e
Update Node.js to v14.17.4 (#382)
renovate[bot] Jul 29, 2021
ec068a3
Update olafurpg/setup-scala action to v13 (#383)
renovate[bot] Aug 2, 2021
d13c89e
Update sbt-scalajs to 1.7.0 (#384)
scala-steward Aug 3, 2021
fa13823
Update sbt-scala-native-crossproject, ... to 1.1.0 (#374)
scala-steward Aug 4, 2021
6e830d5
Merge remote-tracking branch 'origin/master' into quelgar/next-gen
svroonland Aug 8, 2021
57e59c6
Re-add accidentally deleted file
svroonland Aug 8, 2021
05641d6
Unused imports
svroonland Aug 8, 2021
0724850
Some scala3 fixes
svroonland Mar 13, 2021
e3ceaba
Fix scala 3 compilation issue with useBlocking and useNonBlocking ext…
svroonland Aug 14, 2021
7337069
Formatting
svroonland Aug 15, 2021
3f07ba5
Fix scala 2.11 compilation issue
svroonland Aug 15, 2021
c41d07b
Fix docs
svroonland Aug 15, 2021
e118408
Fix name conflict
svroonland Aug 15, 2021
07c3c91
Fix imports
svroonland Aug 15, 2021
04ec563
Fixup docs after hostName rename
svroonland Aug 15, 2021
aea93eb
Scalafix and scalafmt
svroonland Aug 15, 2021
be94da9
Restore build info
svroonland Aug 15, 2021
4677c3b
Merge remote-tracking branch 'origin/master' into quelgar/next-gen
svroonland Aug 15, 2021
1ba246b
Reduce some diffs + typos
svroonland Aug 15, 2021
1d3ef5e
Remove extras.md, moved to PR description
svroonland Aug 15, 2021
a8f632c
Merge remote-tracking branch 'origin/master' into quelgar/next-gen
quelgar Sep 5, 2021
0a73133
Fix Scala 3 tests.
quelgar Sep 5, 2021
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
25 changes: 5 additions & 20 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ addCommandAlias("coverageReport", "clean coverage test coverageReport coverageAg

val zioVersion = "1.0.10"

lazy val zioNioCore = project
.in(file("nio-core"))
.settings(stdSettings("zio-nio-core"))
lazy val zioNio = project
.in(file("nio"))
.settings(stdSettings("zio-nio"))
.settings(
libraryDependencies ++= Seq(
"dev.zio" %% "zio" % zioVersion,
Expand All @@ -32,35 +32,20 @@ lazy val zioNioCore = project
)
.settings(dottySettings)

lazy val zioNio = project
.in(file("nio"))
.dependsOn(zioNioCore)
.settings(stdSettings("zio-nio"))
.settings(
libraryDependencies ++= Seq(
"dev.zio" %% "zio" % zioVersion,
"dev.zio" %% "zio-streams" % zioVersion,
"dev.zio" %% "zio-test" % zioVersion % Test,
"dev.zio" %% "zio-test-sbt" % zioVersion % Test
),
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.settings(dottySettings)

lazy val docs = project
.in(file("zio-nio-docs"))
.settings(
publish / skip := true,
moduleName := "zio-nio-docs",
scalacOptions -= "-Yno-imports",
scalacOptions -= "-Xfatal-warnings",
ScalaUnidoc / unidoc / unidocProjectFilter := inProjects(zioNioCore, zioNio),
ScalaUnidoc / unidoc / unidocProjectFilter := inProjects(zioNio),
ScalaUnidoc / unidoc / target := (LocalRootProject / baseDirectory).value / "website" / "static" / "api",
cleanFiles += (ScalaUnidoc / unidoc / target).value,
docusaurusCreateSite := docusaurusCreateSite.dependsOn(Compile / unidoc).value,
docusaurusPublishGhpages := docusaurusPublishGhpages.dependsOn(Compile / unidoc).value
)
.dependsOn(zioNioCore, zioNio)
.dependsOn(zioNio)
.enablePlugins(MdocPlugin, DocusaurusPlugin, ScalaUnidocPlugin)

lazy val examples = project
Expand Down
90 changes: 90 additions & 0 deletions docs/essentials/blocking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
---
id: essentials_blocking
title: "Blocking I/O"
---

The default ZIO runtime assumes that threads will never block, and maintains a small fixed-size thread pool to perform all its operations. If threads become blocked, CPU utilization can be reduced as the number of available threads drops below the number of available CPU cores. If enough threads block, the entire program may halt.

Another issue with blocked threads is interruption. It is important that if the ZIO fiber is interrupted that this cancels the blocking operation and unblocks the thread.

Many NIO operations can block the calling thread when called. ZIO-NIO provides APIs to help ZIO-based code deal with this. The following describes how to use channels that offer blocking operations, which is all channels except for the asynchronous ones.

## Blocking and Non-Blocking Channel Operations

Channel APIs that may block are not exposed on the channel itself. They are accessed via the channel's `useBlocking` method. You provide this method a function that excepts a `BlockingOps` object and returns a `ZIO` effect value. The `BlockingOps` parameter will be appropriate to the type of channel and has the actual blocking I/O effects such as read and write.

The `useBlocking` method performs some setup required for safe use of blocking NIO APIs:

* Puts the channel in blocking mode
* Runs the resulting effect value on ZIO's blocking thread pool, leaving the standard pool unblocked.
* Installs interrupt handling, so the channel will be closed if the ZIO fiber is interrupted. This unblocks the blocked I/O operation. (Note that NIO does not offer a way to interrupt a blocked I/O operation on a channel that does not close the channel).

Non-blocking usage does not require this special handling, but for consistency the non-blocking operations are accessed in a similar way by calling `useNonBlocking` on the channel. For some channels there are some small differences between the blocking and non-blocking APIs. For example, `SocketChannel` only offers the `finishConnect` operation in the non-blocking case, as it is never needed in blocking mode.

```scala mdoc:silent
import zio.ZIO
import zio.nio._
import zio.nio.channels._

def readHeader(c: SocketChannel): ZIO[Blocking, IOException, (Chunk[Byte], Chunk[Byte])] =
c.useBlocking { ops =>
ops.readChunk(10) <*> ops.readChunk(25)
}
```

### Using Managed Channels

To help with the common use-case where you want to create a channel, there is versions of `useBlocking` and `useNonBlocking` that can be called directly on a managed value providing a channel.

`useNioBlocking` provides both the channel and the requested type of operations:

```scala mdoc:silent
import zio.nio._
import zio.nio.channels._

SocketChannel.open.useNioBlocking { (channel, blockingOps) =>
blockingOps.readChunk(100) <*> channel.remoteAddress
}
```

If you don't need the channel, there's `useNioBlockingOps`:

```scala mdoc:silent
import zio.nio.channels._

SocketChannel.open.useNioBlockingOps { blockingOps =>
blockingOps.readChunk(100)
}
```

To use the channel in non-blocking mode, there's corresponding `useNioNonBlocking` and `useNioNonBlockingOps` methods.

### Avoiding Asynchronous Boundaries

If you have a complex program that makes more than one call to `useBlocking`, then it may be worth running *all* of the ZIO-NIO parts using the blocking pool. This can be done by wrapping the effect value with your ZIO-NIO operations in `zio.blocking.blocking`.

If this isn't done, you can end up with the calls using `BlockingOps` running on a thread from the blocking pool, while the other parts run on a thread from the standard pool. This involves an "asynchronous boundary" whever the fiber changes the underlying thread it's running on, which imposes some overheads including a full memory barrier. By using `zio.blocking.blocking` up-front, all the code can run on the same thread from the blocking pool.

## Comparing the Channel Options

There are three main styles of channel available: blocking, non-blocking and asynchronous. Which to choose?

### Blocking Channels

Easy to use, with a straight-forward operation. The downsides are that you have to use `useBlocking`, which creates a new thread, and will create an additional thread for every forked fiber subsequently created. Essentially you have a blocked thread for every active I/O call, which limits scalability. Also, the additional interrupt handling logic imposes a small overhead.

### Non-Blocking Channels

These scale very well because you basically do as many concurrent I/O operations as you like without creating any new threads. The big downside is that they aren't of practical use without using a `Selector`, which is *very* tricky API to use correctly.

Note that while it is possible to use non-blocking channels without a `Selector`, this means you have to busy-wait on the channel for the simplest reads and writes. It's not efficient.

The other issue is that only network channels and pipes support non-blocking mode.

### Asynchronous Channels

Asynchronous channels give us what we want: we don't need a `Selector` to use them, and our thread will never block when we use them.

However, it should be noted that asynchronous file I/O is not currently possible on the JVM. `AsynchronousFileChannel` is performing blocking I/O using a pool of blocked threads, which exactly what `useBlocking` does, and shares the same drawbacks. It may be preferable to use a standard `FileChannel`, as you'll have more visibility and control over what's going on.

The asynchronous socket channels do *appear* to use non-blocking I/O, although they also have some form of internal thread pool as well. These should scale roughly as well as non-blocking channels. One downside is that there is no asynchronous datagram channel.
17 changes: 9 additions & 8 deletions docs/essentials/charsets.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ id: essentials_charsets
title: "Character Sets"
---

The `zio.nio.core.charset` package offers an API for ZIO programs to work with character sets, using the Java NIO support for character sets. Any character set supported by your JVM can be used.
The `zio.nio.charset` package offers an API for ZIO programs to work with character sets, using the Java NIO support for character sets. Any character set supported by your JVM can be used.

## `Charset`

Expand Down Expand Up @@ -31,9 +31,9 @@ JVMs typically support many more charsets than these; use `Charset.availableChar
### Example

```scala mdoc:silent
import zio.nio.core.charset._
import zio.nio.charset._
import zio.nio.file.Files
import zio.nio.core.file.Path
import zio.nio.file.Path

val s = "Hello, world!"
for {
Expand All @@ -49,19 +49,20 @@ Using streams instead of buffers or chunks is great for bigger jobs. ZIO Streams
Stream-based encoding and decoding are provided by the `transducer` method of the `CharsetEncoder` and `CharsetDecoder` classes respectively.

```scala mdoc:silent
import zio.nio.core.charset._
import zio.nio.core.channels.FileChannel
import zio.nio.core.file.Path
import zio.nio.charset._
import zio.nio.channels.FileChannel
import zio.nio.channels._
import zio.nio.file.Path
import zio.stream.ZStream
import zio.blocking.Blocking
import zio.console
import zio.ZIO

// dump a file encoded in ISO8859 to the console

FileChannel.open(Path("iso8859.txt")).use { fileChan =>
FileChannel.open(Path("iso8859.txt")).useNioBlockingOps { fileOps =>
val inStream: ZStream[Blocking, Exception, Byte] = ZStream.repeatEffectChunkOption {
fileChan.readChunk(1000).asSomeError.flatMap { chunk =>
fileOps.readChunk(1000).asSomeError.flatMap { chunk =>
if (chunk.isEmpty) ZIO.fail(None) else ZIO.succeed(chunk)
}
}
Expand Down
2 changes: 1 addition & 1 deletion docs/essentials/files.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Required imports for presented snippets:
```scala mdoc:silent
import zio._
import zio.nio.channels._
import zio.nio.core.file._
import zio.nio.file._
import zio.console._
```

Expand Down
33 changes: 8 additions & 25 deletions docs/essentials/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,19 @@ id: essentials_index
title: "Overview"
---

ZIO-NIO is a ZIO wrapper on Java NIO. It comes in two flavours:

- `zio.nio.core` - a small and unopionanted ZIO interface to NIO that just wraps NIO API in ZIO effects,
- `zio.nio` - an opinionated interface with deeper ZIO integration that provides more type and resource safety.

A very simple example of these differences would be the signature of `apply` method on `AsynchronousSocketChannel`:
```scala
//zio.nio.core
def apply(): IO[Exception, AsynchronousSocketChannel]
```
vs
```scala
//zio.nio
def apply(): Managed[Exception, AsynchronousSocketChannel]
```
ZIO-NIO is a ZIO wrapper on Java NIO, an opinionated interface with deep ZIO integration that provides type and resource safety.

## Installation

`ZIO-NIO` is available via maven repo. Add this to your dependencies in `sbt`:

```scala
libraryDependencies += "dev.zio" %% "zio-nio-core" % "1.0.0-RC10"
```
or
```scala
libraryDependencies += "dev.zio" %% "zio-nio" % "1.0.0-RC10"
```

## Main abstractions
## Main Abstractions

- **[Using Blocking APIs](blocking.md)** — How to deal with NIO APIs that block the calling thread
- **[File Channel](files.md)** — For processing files that are available locally. For every operation a new fiber is started to perform the operation.
- **[Socket Channel](sockets.md)** — Provides an API for remote communication with `InetSocket`s.
- **[Resource Management](resources.md)** - Avoiding resource leaks
Expand All @@ -45,15 +28,15 @@ When reading from channels, the end of the stream may be reached at any time. Th
```scala mdoc:silent
import zio._
import zio.blocking.Blocking
import zio.nio.core._
import zio.nio.core.channels._
import zio.nio.core.file.Path
import zio.nio._
import zio.nio.channels._
import zio.nio.file.Path
import java.io.IOException

val read100: ZIO[Blocking, Option[IOException], Chunk[Byte]] =
FileChannel.open(Path("foo.txt"))
.asSomeError
.use(_.readChunk(100).eofCheck)
.useNioBlockingOps(_.readChunk(100))
.eofCheck
```

End-of-stream will be signalled with `None`. Any errors will be wrapped in `Some`.
Expand Down
8 changes: 4 additions & 4 deletions docs/essentials/resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ The most straight-forward way to use a managed resource is with the `use` method
```scala mdoc:silent
import zio._
import zio.blocking.Blocking
import zio.nio.core.channels._
import zio.nio.core.file.Path
import zio.nio.channels._
import zio.nio.file.Path
import java.io.IOException

def useChannel(f: FileChannel): ZIO[Blocking, IOException, Unit] = ???
Expand Down Expand Up @@ -43,15 +43,15 @@ ZManaged.scope.use { scope =>
}

// use channel, perhaps with a Selector
channel.flatMap(_.readChunk(10))
channel.flatMap(_.useNonBlocking(_.readChunk(10)))

}
// the scope has now been released, as have all the resources attached to it
```

Note that `scope` returns both the resource and an "early release" effect. This allows you to release the resource before the scope exits, if you know it is no longer needed. This allows efficient use of the resource while still having the safety net of the scope to ensure the release happens even if there are failures, defects or interruptions.

The `zio.nio.core.channels.SelectorSpec` test demonstrates the use of scoping to ensure nothing leaks if an error occurs.
The `zio.nio.channels.SelectorSpec` test demonstrates the use of scoping to ensure nothing leaks if an error occurs.

### Using `close` for Early Release

Expand Down
16 changes: 8 additions & 8 deletions docs/essentials/sockets.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@ import zio._
import zio.clock._
import zio.console._
import zio.nio.channels._
import zio.nio.core._
import zio.nio._
```

## Creating sockets

Creating a server socket:

```scala mdoc:silent
val server = AsynchronousServerSocketChannel()
val server = AsynchronousServerSocketChannel.open
.mapM { socket =>
for {
sockAddr <- InetSocketAddress.hostNameResolved("127.0.0.1", 1337)
_ <- socket.bindTo(sockAddr)
address <- InetSocketAddress.hostName("127.0.0.1", 1337)
_ <- socket.bindTo(address)
_ <- socket.accept.preallocate.flatMap(_.use(channel => doWork(channel).catchAll(ex => putStrLn(ex.getMessage))).fork).forever.fork
} yield ()
}.useForever
Expand All @@ -44,10 +44,11 @@ def doWork(channel: AsynchronousSocketChannel): ZIO[Console with Clock, Throwabl
Creating a client socket:

```scala mdoc:silent
val clientM: Managed[Exception, AsynchronousSocketChannel] = AsynchronousSocketChannel()
val clientM: Managed[Exception, AsynchronousSocketChannel] = AsynchronousSocketChannel.open
.mapM { client =>
for {
address <- InetSocketAddress.localHost(2552)
host <- InetAddress.localHost
address <- InetSocketAddress.inetAddress(host, 2552)
_ <- client.connect(address)
} yield client
}
Expand All @@ -58,8 +59,7 @@ Reading and writing to a socket:
```scala mdoc:silent
for {
serverFiber <- server.fork
clientFiber <- clientM.use(_.writeChunk(Chunk.fromArray(Array(1, 2, 3).map(_.toByte)))).fork
_ <- clientFiber.join
_ <- clientM.use(_.writeChunk(Chunk.fromArray(Array(1, 2, 3).map(_.toByte))))
_ <- serverFiber.join
} yield ()
```
2 changes: 2 additions & 0 deletions docs/usecases/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ So now where to use it? Here you can find some examples to dive into:

- [Stream-based socket server](https://github.com/zio/zio-nio/blob/master/examples/src/main/scala/StreamsBasedServer.scala)
- [Stream-based character decoding](https://github.com/zio/zio-nio/blob/master/examples/src/main/scala/TextFileDump.scala)
- [Stream-based directory watcher](https://github.com/zio/zio-nio/blob/master/examples/src/main/scala/StreamDirWatch.scala)
- [Transducer-based socket server](https://github.com/zio/zio-nio/blob/master/examples/src/main/scala/ToUppercaseAsAService.scala)
53 changes: 53 additions & 0 deletions examples/src/main/scala/StreamDirWatch.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package zio
package nio
package examples

import zio.nio.file.{ Path, WatchService }

import java.nio.file.{ StandardWatchEventKinds, WatchEvent }

/**
* Example of using the `ZStream` API for watching a file system directory for events.
*
* Note that on macOS the standard Java `WatchService` uses polling and so is a bit slow,
* and only registers at most one type of event for each directory member since the last poll.
*/
object StreamDirWatch extends App {

private def watch(dir: Path) =
WatchService.forDefaultFileSystem.use { service =>
for {
_ <- dir.registerTree(
watcher = service,
events = Set(
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE
),
maxDepth = 100
)
_ <- console.putStrLn(s"Watching directory '$dir'")
_ <- console.putStrLn("")
_ <- service.stream.foreach { key =>
val eventProcess = { (event: WatchEvent[_]) =>
val desc = event.kind() match {
case StandardWatchEventKinds.ENTRY_CREATE => "Create"
case StandardWatchEventKinds.ENTRY_MODIFY => "Modify"
case StandardWatchEventKinds.ENTRY_DELETE => "Delete"
case StandardWatchEventKinds.OVERFLOW => "** Overflow **"
case other => s"Unknown: $other"
}
val path = key.resolveEventPath(event).getOrElse("** PATH UNKNOWN **")
console.putStrLn(s"$desc, count: ${event.count()}, $path")
}
key.pollEventsManaged.use(ZIO.foreach_(_)(eventProcess))
}
} yield ()
}

override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] =
args.headOption
.map(dirString => watch(Path(dirString)).exitCode)
.getOrElse(console.putStrLn("A directory argument is required").exitCode)

}
Loading