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

Use ZManaged for resources in core #249

Merged
merged 7 commits into from
Oct 28, 2020

Conversation

quelgar
Copy link
Contributor

@quelgar quelgar commented Sep 10, 2020

The goal of this PR is to be a first step in undoing the split between the core and "high-level" modules. The split seemed like a good idea at the time, but in practice the only significant difference between the two modules is the high-level one uses ZManaged, whereas core requires the user to manually release resources using close. This minor difference comes at the cost of maintaining mostly duplicate copies of much of the code.

ZManaged was originally seen as too opinionated as a requirement to use ZIO-NIO. There are situations where lexically scoping resources with use is too limiting. However, ZManaged in ZIO 1.0 offers more flexible resource management via scopes or reservations, so a ZManaged based API shouldn't prevent ZIO-NIO from doing anything that can be done directly with NIO. So this PR makes all resource acquisition in core use ZManaged.

I've also left close as public, which probably seems odd. But close is still useful with ZManaged as an early release mechanism. While ZManaged lets you get an early release effect value, it's often more convenient to just have this effect available on the channel itself. For example, if we want to use the separate early release value with a channel registered in a selector, we have to attach it to the SelectionKey and later get the attachment and cast it to the correct type. But we already have a reference to the channel, so it's a lot simpler to just call close on it directly to release early.

zio.nio.core.channels.SelectorSpec provides an example of what I'm talking about above. I've also added a documentation page describing the resource management options. Hopefully what I've said is accurate 🤞

If we decide to go this way, we can at some point throw out the high-level module and rename zio.nio.core to zio.nio.

@quelgar quelgar force-pushed the quelgar/resource-management-core branch from 6f8da72 to bf7a44b Compare September 10, 2020 10:35
ZManaged.scope.use { scope =>

val channel: IO[IOException, SocketChannel] = scope(SocketChannel.open).map {
case (_ /* early release */, channel) => channel
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use case (earlyRelease @ _, channel) here to give it a name but avoid an unused param compiler warning.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good trick, I didn't know that.


### Using `close` for Early Release

In the case of channels, we don't actually need the early release features that `ZManaged` provides, as every channel has a built-in early release in the form of the `close` method. Closing a channel more than once is a perfectly safe thing to do, so you can use `close` to release a channel's resources early. When the `ZManaged` scope of the channel later ends, `close` will be called again, but it will be a no-op.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it desirable to expose close to the user in addition to open/close as a ZManaged? With ZManaged you can be sure that the resource is still 'open' as long as the ZManaged is alive, but by exposing close that is no longer the case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true, but the same risk exists if you get the release effect via withEarlyRelease or scope.

@@ -30,4 +30,11 @@ package object core {
case _: EOFException => ZIO.none
}

implicit final private[nio] class IOCloseableManagement[-R, +E, +A <: IOCloseable { type Env >: R }](
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason to use a structured type here instead of trait IOCloseable[Env] ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I was experimenting with blocking variants of the channel APIs I found a member type for this was less hassle, and I just copied that idea here. But for this PR, it doesn't actually matter.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe using structural types uses reflection, which incurs some runtime overhead. Do you think that would matter for the usage in zio-nio?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reflection is definitely used to access term-level members, but I think type members are handled entirely at compile time.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, I didn't know that

@quelgar quelgar force-pushed the quelgar/resource-management-core branch from bf7a44b to 3cbd28d Compare September 23, 2020 10:29
@jdegoes
Copy link
Member

jdegoes commented Oct 21, 2020

@quelgar Conflicts?

@quelgar quelgar force-pushed the quelgar/resource-management-core branch from f593378 to 7c9865e Compare October 21, 2020 20:13
@quelgar quelgar force-pushed the quelgar/resource-management-core branch from 7c9865e to 25db9f4 Compare October 21, 2020 20:18
@quelgar
Copy link
Contributor Author

quelgar commented Oct 21, 2020

@jdegoes good to go

@@ -34,7 +34,8 @@ libraryDependencies += "dev.zio" %% "zio-nio" % "1.0.0-RC10"
## Main abstractions

- **[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.
- **[Socket Channel](sockets.md)** — Provides anAPI for remote communication with `InetSocket`s.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo anAPI

jczuchnowski
jczuchnowski previously approved these changes Oct 25, 2020
@jczuchnowski jczuchnowski merged commit 028a3c8 into master Oct 28, 2020
@jczuchnowski jczuchnowski deleted the quelgar/resource-management-core branch October 28, 2020 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants