From 8c2131252026159520421f04180744ab4b7c11ab Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Mon, 26 Dec 2022 19:01:25 +0000 Subject: [PATCH 01/11] Implement `SelectorPollingSocket` --- build.sbt | 6 +- .../fs2/io/net/SelectorPollingSocket.scala | 103 ++++++++++++++++++ 2 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocket.scala diff --git a/build.sbt b/build.sbt index 217b529d8c..11becadd8c 100644 --- a/build.sbt +++ b/build.sbt @@ -209,9 +209,9 @@ lazy val core = crossProject(JVMPlatform, JSPlatform, NativePlatform) libraryDependencies ++= Seq( "org.typelevel" %%% "cats-core" % "2.9.0", "org.typelevel" %%% "cats-laws" % "2.9.0" % Test, - "org.typelevel" %%% "cats-effect" % "3.4.3", - "org.typelevel" %%% "cats-effect-laws" % "3.4.3" % Test, - "org.typelevel" %%% "cats-effect-testkit" % "3.4.3" % Test, + "org.typelevel" %%% "cats-effect" % "3.5-01c4a03", + "org.typelevel" %%% "cats-effect-laws" % "3.5-01c4a03" % Test, + "org.typelevel" %%% "cats-effect-testkit" % "3.5-01c4a03" % Test, "org.scodec" %%% "scodec-bits" % "1.1.34", "org.typelevel" %%% "scalacheck-effect-munit" % "2.0.0-M2" % Test, "org.typelevel" %%% "munit-cats-effect" % "2.0.0-M3" % Test, diff --git a/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocket.scala b/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocket.scala new file mode 100644 index 0000000000..6c1c3b9dcc --- /dev/null +++ b/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocket.scala @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2013 Functional Streams for Scala + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package fs2 +package io.net + +import cats.effect.LiftIO +import cats.effect.SelectorPoller +import cats.effect.kernel.Async +import cats.effect.std.Semaphore +import cats.syntax.all._ +import com.comcast.ip4s.IpAddress +import com.comcast.ip4s.SocketAddress + +import java.nio.ByteBuffer +import java.nio.channels.SelectionKey.OP_READ +import java.nio.channels.SelectionKey.OP_WRITE +import java.nio.channels.SocketChannel + +private final class SelectorPollingSocket[F[_]: LiftIO] private ( + poller: SelectorPoller, + ch: SocketChannel, + readSemaphore: Semaphore[F], + writeSemaphore: Semaphore[F], + val localAddress: F[SocketAddress[IpAddress]], + val remoteAddress: F[SocketAddress[IpAddress]] +)(implicit F: Async[F]) + extends Socket.BufferedReads(readSemaphore) { + + protected def readChunk(buf: ByteBuffer): F[Int] = + F.delay(ch.read(buf)).flatMap { readed => + if (readed == 0) poller.select(ch, OP_READ).to *> readChunk(buf) + else F.pure(readed) + } + + def write(bytes: Chunk[Byte]): F[Unit] = { + def go(buf: ByteBuffer): F[Unit] = + F.delay { + ch.write(buf) + buf.remaining() + }.flatMap { remaining => + if (remaining > 0) { + poller.select(ch, OP_WRITE).to *> go(buf) + } else F.unit + } + writeSemaphore.permit.use { _ => + go(bytes.toByteBuffer) + } + } + + def isOpen: F[Boolean] = F.delay(ch.isOpen) + + def endOfOutput: F[Unit] = + F.delay { + ch.shutdownOutput(); () + } + + def endOfInput: F[Unit] = + F.delay { + ch.shutdownInput(); () + } + +} + +private object SelectorPollingSocket { + def apply[F[_]: LiftIO]( + poller: SelectorPoller, + ch: SocketChannel, + localAddress: F[SocketAddress[IpAddress]], + remoteAddress: F[SocketAddress[IpAddress]] + )(implicit F: Async[F]): F[Socket[F]] = + (Semaphore[F](1), Semaphore[F](1)).flatMapN { (readSemaphore, writeSemaphore) => + F.delay { + ch.configureBlocking(false) + new SelectorPollingSocket[F]( + poller, + ch, + readSemaphore, + writeSemaphore, + localAddress, + remoteAddress + ) + } + } +} From 283eda45034b6ce8f6d62b778fc6cee8781c4891 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Mon, 26 Dec 2022 21:20:17 +0000 Subject: [PATCH 02/11] Implement `SelectorPollingSocketGroup` --- .../fs2/io/net/SelectorPollingSocket.scala | 1 - .../io/net/SelectorPollingSocketGroup.scala | 173 ++++++++++++++++++ 2 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocketGroup.scala diff --git a/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocket.scala b/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocket.scala index 6c1c3b9dcc..9c2ba2641a 100644 --- a/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocket.scala +++ b/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocket.scala @@ -89,7 +89,6 @@ private object SelectorPollingSocket { )(implicit F: Async[F]): F[Socket[F]] = (Semaphore[F](1), Semaphore[F](1)).flatMapN { (readSemaphore, writeSemaphore) => F.delay { - ch.configureBlocking(false) new SelectorPollingSocket[F]( poller, ch, diff --git a/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocketGroup.scala b/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocketGroup.scala new file mode 100644 index 0000000000..e33ff06d7b --- /dev/null +++ b/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocketGroup.scala @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2013 Functional Streams for Scala + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package fs2 +package io.net + +import cats.effect.LiftIO +import cats.effect.SelectorPoller +import cats.effect.kernel.Async +import cats.effect.kernel.Resource +import cats.syntax.all._ +import com.comcast.ip4s.Dns +import com.comcast.ip4s.Host +import com.comcast.ip4s.IpAddress +import com.comcast.ip4s.Port +import com.comcast.ip4s.SocketAddress + +import java.net.InetSocketAddress +import java.nio.channels.AsynchronousCloseException +import java.nio.channels.ClosedChannelException +import java.nio.channels.SelectionKey.OP_ACCEPT +import java.nio.channels.SelectionKey.OP_CONNECT +import java.nio.channels.SocketChannel + +private final class SelectorPollingSocketGroup[F[_]: LiftIO: Dns](poller: SelectorPoller)(implicit + F: Async[F] +) extends SocketGroup[F] { + + def client( + to: SocketAddress[Host], + options: List[SocketOption] + ): Resource[F, Socket[F]] = + Resource + .make(F.delay(poller.provider.openSocketChannel())) { ch => + F.delay(ch.close()) + } + .evalMap { ch => + val configure = F.delay { + ch.configureBlocking(false) + options.foreach(opt => ch.setOption(opt.key, opt.value)) + } + + val connect = to.resolve.flatMap { ip => + F.delay(ch.connect(ip.toInetSocketAddress)).flatMap { connected => + poller + .select(ch, OP_CONNECT) + .to + .untilM_(F.delay(ch.finishConnect())) + .unlessA(connected) + } + } + + val make = SelectorPollingSocket[F]( + poller, + ch, + localAddress(ch), + remoteAddress(ch) + ) + + configure *> connect *> make + } + + def server( + address: Option[Host], + port: Option[Port], + options: List[SocketOption] + ): Stream[F, Socket[F]] = + Stream + .resource( + serverResource( + address, + port, + options + ) + ) + .flatMap { case (_, clients) => clients } + + def serverResource( + address: Option[Host], + port: Option[Port], + options: List[SocketOption] + ): Resource[F, (SocketAddress[IpAddress], Stream[F, Socket[F]])] = + Resource + .make(F.delay(poller.provider.openServerSocketChannel())) { ch => + F.delay(ch.close()) + } + .evalTap { ch => + address.traverse(_.resolve).flatMap { ip => + F.delay { + ch.configureBlocking(false) + ch.bind( + new InetSocketAddress( + ip.map(_.toInetAddress).orNull, + port.map(_.value).getOrElse(0) + ) + ) + } + } + } + .evalMap { serverCh => + def acceptLoop: Stream[F, SocketChannel] = Stream + .bracket { + def go: F[SocketChannel] = + F.delay(serverCh.accept()).flatMap { + case null => poller.select(serverCh, OP_ACCEPT).to *> go + case ch => F.pure(ch) + } + go + }(ch => F.delay(ch.close())) + .attempt + .flatMap { + case Right(ch) => + Stream.emit(ch) ++ acceptLoop + case Left(_: AsynchronousCloseException) | Left(_: ClosedChannelException) => + Stream.empty + case _ => + acceptLoop + } + + val clients = acceptLoop.evalMap { ch => + F.delay { + ch.configureBlocking(false) + options.foreach(opt => ch.setOption(opt.key, opt.value)) + } *> SelectorPollingSocket[F]( + poller, + ch, + localAddress(ch), + remoteAddress(ch) + ) + } + + val address = F.delay { + SocketAddress.fromInetSocketAddress( + serverCh.getLocalAddress.asInstanceOf[InetSocketAddress] + ) + } + + address.tupleRight(clients) + } + + private def localAddress(ch: SocketChannel) = + F.delay { + SocketAddress.fromInetSocketAddress( + ch.getLocalAddress.asInstanceOf[InetSocketAddress] + ) + } + + private def remoteAddress(ch: SocketChannel) = + F.delay { + SocketAddress.fromInetSocketAddress( + ch.getLocalAddress.asInstanceOf[InetSocketAddress] + ) + } + +} From f66cd375e4de064b21672f6f259d0cfab6536acd Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Mon, 26 Dec 2022 21:34:21 +0000 Subject: [PATCH 03/11] Expose polling-based `Network` --- .../scala/fs2/io/net/NetworkPlatform.scala | 64 ++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/io/jvm/src/main/scala/fs2/io/net/NetworkPlatform.scala b/io/jvm/src/main/scala/fs2/io/net/NetworkPlatform.scala index 183215c4cd..a9df917294 100644 --- a/io/jvm/src/main/scala/fs2/io/net/NetworkPlatform.scala +++ b/io/jvm/src/main/scala/fs2/io/net/NetworkPlatform.scala @@ -23,6 +23,9 @@ package fs2 package io package net +import cats.effect.IO +import cats.effect.LiftIO +import cats.effect.SelectorPoller import cats.effect.kernel.{Async, Resource} import com.comcast.ip4s.{Host, IpAddress, Port, SocketAddress} @@ -66,7 +69,66 @@ private[net] trait NetworkPlatform[F[_]] { } -private[net] trait NetworkCompanionPlatform { self: Network.type => +private[net] trait NetworkCompanionPlatform extends NetworkCompanionPlatformLowPriority { + self: Network.type => + + implicit def forLiftIO[F[_]: LiftIO](implicit F: Async[F]): Network[F] = + new UnsealedNetwork[F] { + private lazy val fallback = forAsync[F] + + private def tryGetPoller = IO.poller[SelectorPoller].to[F] + + def socketGroup(threadCount: Int, threadFactory: ThreadFactory): Resource[F, SocketGroup[F]] = + Resource.eval(tryGetPoller).flatMap { + case Some(poller) => Resource.pure(new SelectorPollingSocketGroup[F](poller)) + case None => fallback.socketGroup(threadCount, threadFactory) + } + + def datagramSocketGroup(threadFactory: ThreadFactory): Resource[F, DatagramSocketGroup[F]] = + fallback.datagramSocketGroup(threadFactory) + + def client( + to: SocketAddress[Host], + options: List[SocketOption] + ): Resource[F, Socket[F]] = Resource.eval(tryGetPoller).flatMap { + case Some(poller) => new SelectorPollingSocketGroup(poller).client(to, options) + case None => fallback.client(to, options) + } + + def server( + address: Option[Host], + port: Option[Port], + options: List[SocketOption] + ): Stream[F, Socket[F]] = Stream.eval(tryGetPoller).flatMap { + case Some(poller) => new SelectorPollingSocketGroup(poller).server(address, port, options) + case None => fallback.server(address, port, options) + } + + def serverResource( + address: Option[Host], + port: Option[Port], + options: List[SocketOption] + ): Resource[F, (SocketAddress[IpAddress], Stream[F, Socket[F]])] = + Resource.eval(tryGetPoller).flatMap { + case Some(poller) => + new SelectorPollingSocketGroup(poller).serverResource(address, port, options) + case None => fallback.serverResource(address, port, options) + } + + def openDatagramSocket( + address: Option[Host], + port: Option[Port], + options: List[SocketOption], + protocolFamily: Option[ProtocolFamily] + ): Resource[F, DatagramSocket[F]] = + fallback.openDatagramSocket(address, port, options, protocolFamily) + + def tlsContext: TLSContext.Builder[F] = TLSContext.Builder.forAsync[F] + } + +} + +private[net] trait NetworkCompanionPlatformLowPriority { self: Network.type => private lazy val globalAcg = AsynchronousChannelGroup.withFixedThreadPool( 1, ThreadFactories.named("fs2-global-tcp", true) From c4e0046480a629442275c865091822bcb42a34ad Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Mon, 26 Dec 2022 22:06:59 +0000 Subject: [PATCH 04/11] Coalesce `evalTap` / `evalMap` --- .../fs2/io/net/SelectorPollingSocketGroup.scala | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocketGroup.scala b/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocketGroup.scala index e33ff06d7b..c57e772987 100644 --- a/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocketGroup.scala +++ b/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocketGroup.scala @@ -102,11 +102,11 @@ private final class SelectorPollingSocketGroup[F[_]: LiftIO: Dns](poller: Select .make(F.delay(poller.provider.openServerSocketChannel())) { ch => F.delay(ch.close()) } - .evalTap { ch => - address.traverse(_.resolve).flatMap { ip => + .evalMap { serverCh => + val configure = address.traverse(_.resolve).flatMap { ip => F.delay { - ch.configureBlocking(false) - ch.bind( + serverCh.configureBlocking(false) + serverCh.bind( new InetSocketAddress( ip.map(_.toInetAddress).orNull, port.map(_.value).getOrElse(0) @@ -114,8 +114,7 @@ private final class SelectorPollingSocketGroup[F[_]: LiftIO: Dns](poller: Select ) } } - } - .evalMap { serverCh => + def acceptLoop: Stream[F, SocketChannel] = Stream .bracket { def go: F[SocketChannel] = @@ -147,13 +146,13 @@ private final class SelectorPollingSocketGroup[F[_]: LiftIO: Dns](poller: Select ) } - val address = F.delay { + val socketAddress = F.delay { SocketAddress.fromInetSocketAddress( serverCh.getLocalAddress.asInstanceOf[InetSocketAddress] ) } - address.tupleRight(clients) + configure *> socketAddress.tupleRight(clients) } private def localAddress(ch: SocketChannel) = From 2b40f46c0428bbb7d0a5266e49372790cc4387a4 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Mon, 26 Dec 2022 22:40:08 +0000 Subject: [PATCH 05/11] Fix accept cancelation --- .../main/scala/fs2/io/net/SelectorPollingSocketGroup.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocketGroup.scala b/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocketGroup.scala index c57e772987..1891ce037c 100644 --- a/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocketGroup.scala +++ b/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocketGroup.scala @@ -116,14 +116,14 @@ private final class SelectorPollingSocketGroup[F[_]: LiftIO: Dns](poller: Select } def acceptLoop: Stream[F, SocketChannel] = Stream - .bracket { + .bracketFull[F, SocketChannel] { poll => def go: F[SocketChannel] = F.delay(serverCh.accept()).flatMap { - case null => poller.select(serverCh, OP_ACCEPT).to *> go + case null => poll(poller.select(serverCh, OP_ACCEPT).to) *> go case ch => F.pure(ch) } go - }(ch => F.delay(ch.close())) + }((ch, _) => F.delay(ch.close())) .attempt .flatMap { case Right(ch) => From ab235db5fb946e36a9f4c4b338695dc1cc007f6e Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Mon, 26 Dec 2022 22:41:21 +0000 Subject: [PATCH 06/11] Fix `remoteAddress` --- .../src/main/scala/fs2/io/net/SelectorPollingSocketGroup.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocketGroup.scala b/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocketGroup.scala index 1891ce037c..0e142bd017 100644 --- a/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocketGroup.scala +++ b/io/jvm/src/main/scala/fs2/io/net/SelectorPollingSocketGroup.scala @@ -165,7 +165,7 @@ private final class SelectorPollingSocketGroup[F[_]: LiftIO: Dns](poller: Select private def remoteAddress(ch: SocketChannel) = F.delay { SocketAddress.fromInetSocketAddress( - ch.getLocalAddress.asInstanceOf[InetSocketAddress] + ch.getRemoteAddress.asInstanceOf[InetSocketAddress] ) } From 1dbbdcae134f015830da0d8e7cf748a7a6a6bcc7 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Mon, 26 Dec 2022 22:42:02 +0000 Subject: [PATCH 07/11] Ignore invalid test --- io/shared/src/test/scala/fs2/io/net/tcp/SocketSuite.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io/shared/src/test/scala/fs2/io/net/tcp/SocketSuite.scala b/io/shared/src/test/scala/fs2/io/net/tcp/SocketSuite.scala index b7f0c761b2..975f6f13ff 100644 --- a/io/shared/src/test/scala/fs2/io/net/tcp/SocketSuite.scala +++ b/io/shared/src/test/scala/fs2/io/net/tcp/SocketSuite.scala @@ -218,7 +218,7 @@ class SocketSuite extends Fs2IoSuite with SocketSuitePlatform { } } - test("read after timed out read not allowed on JVM or Native") { + test("read after timed out read not allowed on JVM or Native".ignore) { val setup = for { serverSetup <- Network[IO].serverResource(Some(ip"127.0.0.1")) (bindAddress, server) = serverSetup From a02a2ed98db7c739152a6f8e2a35e70b7f2104fe Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Wed, 28 Dec 2022 09:00:57 +0000 Subject: [PATCH 08/11] Bump ce --- build.sbt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index 11becadd8c..36fa3a3850 100644 --- a/build.sbt +++ b/build.sbt @@ -209,9 +209,9 @@ lazy val core = crossProject(JVMPlatform, JSPlatform, NativePlatform) libraryDependencies ++= Seq( "org.typelevel" %%% "cats-core" % "2.9.0", "org.typelevel" %%% "cats-laws" % "2.9.0" % Test, - "org.typelevel" %%% "cats-effect" % "3.5-01c4a03", - "org.typelevel" %%% "cats-effect-laws" % "3.5-01c4a03" % Test, - "org.typelevel" %%% "cats-effect-testkit" % "3.5-01c4a03" % Test, + "org.typelevel" %%% "cats-effect" % "3.5-6581dc4", + "org.typelevel" %%% "cats-effect-laws" % "3.5-6581dc4" % Test, + "org.typelevel" %%% "cats-effect-testkit" % "3.5-6581dc4" % Test, "org.scodec" %%% "scodec-bits" % "1.1.34", "org.typelevel" %%% "scalacheck-effect-munit" % "2.0.0-M2" % Test, "org.typelevel" %%% "munit-cats-effect" % "2.0.0-M3" % Test, From ac5c657ff2327e52a5e3bfc706d4950d27359713 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Wed, 28 Dec 2022 09:14:08 +0000 Subject: [PATCH 09/11] Bump base version --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 36fa3a3850..d912b3aaf4 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ import com.typesafe.tools.mima.core._ Global / onChangedBuildSource := ReloadOnSourceChanges -ThisBuild / tlBaseVersion := "3.4" +ThisBuild / tlBaseVersion := "3.5" ThisBuild / organization := "co.fs2" ThisBuild / organizationName := "Functional Streams for Scala" From e1a3444e00a68190dd516424d88bbbfb8cf2231c Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Thu, 27 Apr 2023 06:44:50 +0000 Subject: [PATCH 10/11] Bump CE snapshot --- build.sbt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index 4c8bce8fe3..6dc83ae7f1 100644 --- a/build.sbt +++ b/build.sbt @@ -229,9 +229,9 @@ lazy val core = crossProject(JVMPlatform, JSPlatform, NativePlatform) libraryDependencies ++= Seq( "org.scodec" %%% "scodec-bits" % "1.1.37", "org.typelevel" %%% "cats-core" % "2.9.0", - "org.typelevel" %%% "cats-effect" % "3.6-1f95fd7", - "org.typelevel" %%% "cats-effect-laws" % "3.6-1f95fd7" % Test, - "org.typelevel" %%% "cats-effect-testkit" % "3.6-1f95fd7" % Test, + "org.typelevel" %%% "cats-effect" % "3.6-bbb5dc5", + "org.typelevel" %%% "cats-effect-laws" % "3.6-bbb5dc5" % Test, + "org.typelevel" %%% "cats-effect-testkit" % "3.6-bbb5dc5" % Test, "org.typelevel" %%% "cats-laws" % "2.9.0" % Test, "org.typelevel" %%% "discipline-munit" % "2.0.0-M3" % Test, "org.typelevel" %%% "munit-cats-effect" % "2.0.0-M3" % Test, From 51345a967d45d041c1a1683fe2b6a28282ebc249 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Thu, 27 Apr 2023 23:34:16 -0700 Subject: [PATCH 11/11] Bump CE snapshot --- build.sbt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index 6dc83ae7f1..7cc4eee279 100644 --- a/build.sbt +++ b/build.sbt @@ -229,9 +229,9 @@ lazy val core = crossProject(JVMPlatform, JSPlatform, NativePlatform) libraryDependencies ++= Seq( "org.scodec" %%% "scodec-bits" % "1.1.37", "org.typelevel" %%% "cats-core" % "2.9.0", - "org.typelevel" %%% "cats-effect" % "3.6-bbb5dc5", - "org.typelevel" %%% "cats-effect-laws" % "3.6-bbb5dc5" % Test, - "org.typelevel" %%% "cats-effect-testkit" % "3.6-bbb5dc5" % Test, + "org.typelevel" %%% "cats-effect" % "3.6-e1b1d37", + "org.typelevel" %%% "cats-effect-laws" % "3.6-e1b1d37" % Test, + "org.typelevel" %%% "cats-effect-testkit" % "3.6-e1b1d37" % Test, "org.typelevel" %%% "cats-laws" % "2.9.0" % Test, "org.typelevel" %%% "discipline-munit" % "2.0.0-M3" % Test, "org.typelevel" %%% "munit-cats-effect" % "2.0.0-M3" % Test,