Skip to content

Commit 2819896

Browse files
authored
Merge pull request #3539 from onsah/issue/3182
run `Process#waitFor` on virtual thread if available
2 parents 0ffc0c5 + 1f8de2e commit 2819896

File tree

5 files changed

+118
-10
lines changed

5 files changed

+118
-10
lines changed

.github/workflows/ci.yml

+75-1
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,17 @@ jobs:
2929
matrix:
3030
os: [ubuntu-latest]
3131
scala: [2.12, 2.13, 3]
32-
java: [temurin@17]
32+
java: [temurin@17, temurin@21]
3333
project: [rootJS, rootJVM, rootNative]
34+
exclude:
35+
- scala: 2.12
36+
java: temurin@21
37+
- scala: 3
38+
java: temurin@21
39+
- project: rootJS
40+
java: temurin@21
41+
- project: rootNative
42+
java: temurin@21
3443
runs-on: ${{ matrix.os }}
3544
timeout-minutes: 60
3645
steps:
@@ -55,6 +64,19 @@ jobs:
5564
if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false'
5665
run: sbt +update
5766

67+
- name: Setup Java (temurin@21)
68+
id: setup-java-temurin-21
69+
if: matrix.java == 'temurin@21'
70+
uses: actions/setup-java@v4
71+
with:
72+
distribution: temurin
73+
java-version: 21
74+
cache: sbt
75+
76+
- name: sbt update
77+
if: matrix.java == 'temurin@21' && steps.setup-java-temurin-21.outputs.cache-hit == 'false'
78+
run: sbt +update
79+
5880
- name: Install brew formulae (ubuntu)
5981
if: (matrix.project == 'rootNative') && startsWith(matrix.os, 'ubuntu')
6082
run: /home/linuxbrew/.linuxbrew/bin/brew install openssl s2n
@@ -137,6 +159,19 @@ jobs:
137159
if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false'
138160
run: sbt +update
139161

162+
- name: Setup Java (temurin@21)
163+
id: setup-java-temurin-21
164+
if: matrix.java == 'temurin@21'
165+
uses: actions/setup-java@v4
166+
with:
167+
distribution: temurin
168+
java-version: 21
169+
cache: sbt
170+
171+
- name: sbt update
172+
if: matrix.java == 'temurin@21' && steps.setup-java-temurin-21.outputs.cache-hit == 'false'
173+
run: sbt +update
174+
140175
- name: Download target directories (2.12, rootJS)
141176
uses: actions/download-artifact@v4
142177
with:
@@ -281,6 +316,19 @@ jobs:
281316
if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false'
282317
run: sbt +update
283318

319+
- name: Setup Java (temurin@21)
320+
id: setup-java-temurin-21
321+
if: matrix.java == 'temurin@21'
322+
uses: actions/setup-java@v4
323+
with:
324+
distribution: temurin
325+
java-version: 21
326+
cache: sbt
327+
328+
- name: sbt update
329+
if: matrix.java == 'temurin@21' && steps.setup-java-temurin-21.outputs.cache-hit == 'false'
330+
run: sbt +update
331+
284332
- name: Submit Dependencies
285333
uses: scalacenter/sbt-dependency-submission@v2
286334
with:
@@ -317,6 +365,19 @@ jobs:
317365
if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false'
318366
run: sbt +update
319367

368+
- name: Setup Java (temurin@21)
369+
id: setup-java-temurin-21
370+
if: matrix.java == 'temurin@21'
371+
uses: actions/setup-java@v4
372+
with:
373+
distribution: temurin
374+
java-version: 21
375+
cache: sbt
376+
377+
- name: sbt update
378+
if: matrix.java == 'temurin@21' && steps.setup-java-temurin-21.outputs.cache-hit == 'false'
379+
run: sbt +update
380+
320381
- if: matrix.project == 'ioNative'
321382
run: brew install s2n
322383

@@ -351,6 +412,19 @@ jobs:
351412
if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false'
352413
run: sbt +update
353414

415+
- name: Setup Java (temurin@21)
416+
id: setup-java-temurin-21
417+
if: matrix.java == 'temurin@21'
418+
uses: actions/setup-java@v4
419+
with:
420+
distribution: temurin
421+
java-version: 21
422+
cache: sbt
423+
424+
- name: sbt update
425+
if: matrix.java == 'temurin@21' && steps.setup-java-temurin-21.outputs.cache-hit == 'false'
426+
run: sbt +update
427+
354428
- name: Generate site
355429
run: sbt microsite/tlSite
356430

build.sbt

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ ThisBuild / crossScalaVersions := Seq("2.12.20", Scala213, "3.3.5")
1515
ThisBuild / tlVersionIntroduced := Map("3" -> "3.0.3")
1616

1717
ThisBuild / githubWorkflowOSes := Seq("ubuntu-latest")
18-
ThisBuild / githubWorkflowJavaVersions := Seq(JavaSpec.temurin("17"))
18+
ThisBuild / githubWorkflowJavaVersions := Seq(JavaSpec.temurin("17"), JavaSpec.temurin("21"))
1919
ThisBuild / githubWorkflowBuildPreamble ++= nativeBrewInstallWorkflowSteps.value
2020
ThisBuild / nativeBrewInstallCond := Some("matrix.project == 'rootNative'")
2121

io/jvm-native/src/main/scala/fs2/io/process/ProcessesPlatform.scala

+11-8
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@ package process
2525

2626
import cats.effect.kernel.Async
2727
import cats.effect.kernel.Resource
28-
import cats.syntax.all._
29-
import fs2.io.CollectionCompat._
28+
import cats.syntax.all.*
29+
import fs2.io.CollectionCompat.*
3030

3131
import java.lang
3232

3333
private[process] trait ProcessesCompanionPlatform {
3434
def forAsync[F[_]](implicit F: Async[F]): Processes[F] = new UnsealedProcesses[F] {
35+
3536
def spawn(process: ProcessBuilder): Resource[F, Process[F]] =
3637
Resource
3738
.make {
@@ -53,11 +54,13 @@ private[process] trait ProcessesCompanionPlatform {
5354
} { process =>
5455
F.delay(process.isAlive())
5556
.ifM(
56-
F.blocking {
57-
process.destroy()
58-
process.waitFor()
59-
()
60-
},
57+
evalOnVirtualThreadIfAvailable(
58+
F.blocking {
59+
process.destroy()
60+
process.waitFor()
61+
()
62+
}
63+
),
6164
F.unit
6265
)
6366
}
@@ -66,7 +69,7 @@ private[process] trait ProcessesCompanionPlatform {
6669
def isAlive = F.delay(process.isAlive())
6770

6871
def exitValue = isAlive.ifM(
69-
F.interruptible(process.waitFor()),
72+
evalOnVirtualThreadIfAvailable(F.interruptible(process.waitFor())),
7073
F.delay(process.exitValue())
7174
)
7275

io/jvm/src/main/scala/fs2/io/ioplatform.scala

+28
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import fs2.io.internal.PipedStreamBuffer
3131

3232
import java.io.{InputStream, OutputStream}
3333
import java.util.concurrent.Executors
34+
import scala.concurrent.ExecutionContext
35+
import java.util.concurrent.ExecutorService
3436

3537
private[fs2] trait ioplatform extends iojvmnative {
3638

@@ -144,4 +146,30 @@ private[fs2] trait ioplatform extends iojvmnative {
144146
}
145147
}
146148

149+
// Using null instead of Option because null check is faster
150+
private lazy val vtExecutor: ExecutionContext = {
151+
val javaVersion: Int =
152+
System.getProperty("java.version").stripPrefix("1.").takeWhile(_.isDigit).toInt
153+
154+
// From JVM 21 on we can use virtual threads
155+
if (javaVersion >= 21) {
156+
val virtualThreadExecutor = classOf[Executors]
157+
.getDeclaredMethod("newVirtualThreadPerTaskExecutor")
158+
.invoke(null)
159+
.asInstanceOf[ExecutorService]
160+
161+
ExecutionContext.fromExecutor(virtualThreadExecutor)
162+
} else {
163+
null
164+
}
165+
166+
}
167+
168+
private[io] def evalOnVirtualThreadIfAvailable[F[_]: Async, A](fa: F[A]): F[A] =
169+
if (vtExecutor != null) {
170+
fa.evalOn(vtExecutor)
171+
} else {
172+
fa
173+
}
174+
147175
}

io/native/src/main/scala/fs2/io/ioplatform.scala

+3
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,7 @@ private[fs2] trait ioplatform extends iojvmnative {
3232
def stdinUtf8[F[_]](bufSize: Int)(implicit F: Sync[F]): Stream[F, String] =
3333
stdin(bufSize).through(text.utf8.decode)
3434

35+
// Scala-native doesn't support virtual threads
36+
private[io] def evalOnVirtualThreadIfAvailable[F[_], A](fa: F[A]): F[A] = fa
37+
3538
}

0 commit comments

Comments
 (0)