Skip to content

Commit 2b164c0

Browse files
authored
Simplify resources allocation, add releaseAfterScope (#102)
1 parent 7ed5554 commit 2b164c0

File tree

4 files changed

+31
-30
lines changed

4 files changed

+31
-30
lines changed

core/src/main/scala/ox/resource.scala

+15-6
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
package ox
22

3-
/** Use the given resource in the current scope. The resource is allocated using `acquire`, and released after the scope is done using
4-
* `release`. Releasing is uninterruptible.
3+
/** Use the given resource in the current concurrency scope. The resource is allocated using `acquire`, and released after the all forks in
4+
* the scope complete (either successfully or with an error), using `release`. Releasing is uninterruptible.
55
*/
66
def useInScope[T](acquire: => T)(release: T => Unit)(using Ox): T =
77
val t = acquire
88
summon[Ox].addFinalizer(() => release(t))
99
t
1010

11+
/** Use the given resource, which implements [[AutoCloseable]], in the current concurrency scope. The resource is allocated using `acquire`,
12+
* and released after the all forks in the scope complete (either successfully or with an error), using [[AutoCloseable.close()]].
13+
* Releasing is uninterruptible.
14+
*/
1115
def useCloseableInScope[T <: AutoCloseable](c: => T)(using Ox): T = useInScope(c)(_.close())
1216

13-
def useScoped[T, U](acquire: => T)(release: T => Unit)(b: T => U): U = scoped(b(useInScope(acquire)(release)))
14-
def useScoped[T <: AutoCloseable, U](acquire: => T)(b: T => U): U = scoped(b(useInScope(acquire)(_.close())))
17+
/** Release the given resource, by running the `release` code block. Releasing is done after all the forks in the scope complete (either
18+
* successfully or with an error), but before the current concurrency scope completes. Releasing is uninterruptible.
19+
*/
20+
def releaseAfterScope(release: => Unit)(using Ox): Unit = useInScope(())(_ => release)
1521

16-
def useSupervised[T, U](acquire: => T)(release: T => Unit)(b: T => U): U = supervised(b(useInScope(acquire)(release)))
17-
def useSupervised[T <: AutoCloseable, U](acquire: => T)(b: T => U): U = supervised(b(useInScope(acquire)(_.close())))
22+
/** Release the given resource, which implements [[AutoCloseable]], by running its `.close()` method. Releasing is done after all the forks
23+
* in the scope complete (either successfully or with an error), but before the current concurrency scope completes. Releasing is
24+
* uninterruptible.
25+
*/
26+
def releaseCloseableAfterScope(toRelease: AutoCloseable)(using Ox): Unit = useInScope(())(_ => toRelease.close())

core/src/main/scala/ox/syntax.scala

-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package ox
33
import ox.retry.RetryPolicy
44

55
import scala.concurrent.duration.FiniteDuration
6-
import scala.util.Try
76

87
object syntax:
98
extension [T](f: => T) def forever: Fork[Nothing] = ox.forever(f)
@@ -28,8 +27,6 @@ object syntax:
2827

2928
extension [T <: AutoCloseable](f: => T)(using Ox)
3029
def useInScope: T = ox.useCloseableInScope(f)
31-
def useScoped[U](p: T => U): U = ox.useScoped(f)(p)
32-
def useSupervised[U](p: T => U): U = ox.useSupervised(f)(p)
3330

3431
extension [I, C[E] <: Iterable[E]](f: => C[I])
3532
def mapPar[O](parallelism: Int)(transform: I => O): C[O] = ox.mapPar(parallelism)(f)(transform)

core/src/test/scala/ox/ResourceTest.scala

+6-7
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,13 @@ class ResourceTest extends AnyFlatSpec with Matchers {
7676
trail.get shouldBe Vector("allocate 1", "allocate 2", "release 2", "release 1", "exception")
7777
}
7878

79-
"useScoped" should "release resources after allocation" in {
79+
it should "release registered resources" in {
8080
val trail = Trail()
81-
useScoped {
82-
trail.add("allocate"); 1
83-
}(n => trail.add(s"release $n")) { r =>
84-
r shouldBe 1
85-
trail.get shouldBe Vector("allocate")
81+
82+
scoped {
83+
releaseAfterScope(trail.add("release"))
84+
trail.add("in scope")
8685
}
87-
trail.get shouldBe Vector("allocate", "release 1")
86+
trail.get shouldBe Vector("in scope", "release")
8887
}
8988
}

doc/resources.md

+10-14
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# Resources
22

3-
## In-scope
3+
## Allocate & release
44

5-
Resources can be allocated within a scope. They will be released in reverse acquisition order, after the scope completes
6-
(that is, after all forks started within finish). E.g.:
5+
Resources can be allocated within a concurrency scope. They will be released in reverse acquisition order, after all
6+
forks started within the scope finish (but before the scope completes). E.g.:
77

88
```scala mdoc:compile-only
99
import ox.{supervised, useInScope}
@@ -25,25 +25,21 @@ supervised {
2525
}
2626
```
2727

28-
## Supervised / scoped
28+
## Release-only
2929

30-
Resources can also be used in a dedicated scope:
30+
You can also register resources to be released (without acquisition logic), before the scope completes:
3131

3232
```scala mdoc:compile-only
33-
import ox.useSupervised
33+
import ox.{supervised, releaseAfterScope}
3434

3535
case class MyResource(c: Int)
3636

37-
def acquire(c: Int): MyResource =
38-
println(s"acquiring $c ...")
39-
MyResource(c)
40-
4137
def release(resource: MyResource): Unit =
4238
println(s"releasing ${resource.c} ...")
4339

44-
useSupervised(acquire(10))(release) { resource =>
45-
println(s"Using $resource ...")
40+
supervised {
41+
val resource1 = MyResource(10)
42+
releaseAfterScope(release(resource1))
43+
println(s"Using $resource1 ...")
4644
}
4745
```
48-
49-
If the resource extends `AutoCloseable`, the `release` method doesn't need to be provided.

0 commit comments

Comments
 (0)