cats.effect.concurrent.Semaphore Scala Examples
The following examples show how to use cats.effect.concurrent.Semaphore.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example.
Example 1
Source File: SftpStore.scala From fs2-blobstore with Apache License 2.0 | 5 votes |
package blobstore package sftp import java.util.Date import com.jcraft.jsch._ import cats.instances.option._ import scala.util.Try import java.io.OutputStream import cats.Traverse import cats.effect.{Blocker, ConcurrentEffect, ContextShift, IO, Resource} import cats.effect.concurrent.{MVar, Semaphore} import fs2.concurrent.Queue final class SftpStore[F[_]]( absRoot: String, session: Session, blocker: Blocker, mVar: MVar[F, ChannelSftp], semaphore: Option[Semaphore[F]], connectTimeout: Int )(implicit F: ConcurrentEffect[F], CS: ContextShift[F]) extends Store[F] { import implicits._ import Path.SEP private val openChannel: F[ChannelSftp] = { val openF = blocker.delay{ val ch = session.openChannel("sftp").asInstanceOf[ChannelSftp] ch.connect(connectTimeout) ch } semaphore.fold(openF){s => F.ifM(s.tryAcquire)(openF, getChannel) } } private val getChannel = F.flatMap(mVar.tryTake) { case Some(channel) => F.pure(channel) case None => openChannel } private def channelResource: Resource[F, ChannelSftp] = Resource.make{ getChannel }{ case ch if ch.isClosed => F.unit case ch => F.ifM(mVar.tryPut(ch))(F.unit, SftpStore.closeChannel(semaphore, blocker)(ch)) } def apply[F[_]]( absRoot: String, fa: F[Session], blocker: Blocker, maxChannels: Option[Long] = None, connectTimeout: Int = 10000 )(implicit F: ConcurrentEffect[F], CS: ContextShift[F]): fs2.Stream[F, SftpStore[F]] = if (maxChannels.exists(_ < 1)) { fs2.Stream.raiseError[F](new IllegalArgumentException(s"maxChannels must be >= 1")) } else { for { session <- fs2.Stream.bracket(fa)(session => F.delay(session.disconnect())) semaphore <- fs2.Stream.eval(Traverse[Option].sequence(maxChannels.map(Semaphore.apply[F]))) mVar <- fs2.Stream.bracket(MVar.empty[F, ChannelSftp])(mVar => F.flatMap(mVar.tryTake)(_.fold(F.unit)(closeChannel[F](semaphore, blocker)))) } yield new SftpStore[F](absRoot, session, blocker, mVar, semaphore, connectTimeout) } private def closeChannel[F[_]](semaphore: Option[Semaphore[F]], blocker: Blocker)(ch: ChannelSftp)(implicit F: ConcurrentEffect[F], CS: ContextShift[F]): F[Unit] = F.productR(semaphore.fold(F.unit)(_.release))(blocker.delay(ch.disconnect())) }
Example 2
Source File: SemaphoreBenchmark.scala From zio with Apache License 2.0 | 5 votes |
package zio.stm import java.util.concurrent.TimeUnit import scala.concurrent.ExecutionContext import cats.effect.{ ContextShift, IO => CIO } import org.openjdk.jmh.annotations._ import zio.IOBenchmarks._ import zio._ @State(Scope.Thread) @BenchmarkMode(Array(Mode.Throughput)) @OutputTimeUnit(TimeUnit.SECONDS) @Measurement(iterations = 15, timeUnit = TimeUnit.SECONDS, time = 10) @Warmup(iterations = 15, timeUnit = TimeUnit.SECONDS, time = 10) @Fork(1) class SemaphoreBenchmark { @Param(Array("10")) var fibers: Int = _ @Param(Array("1000")) var ops: Int = _ @Benchmark def semaphoreContention() = unsafeRun(for { sem <- Semaphore.make(fibers / 2L) fiber <- ZIO.forkAll(List.fill(fibers)(repeat(ops)(sem.withPermit(ZIO.succeedNow(1))))) _ <- fiber.join } yield ()) @Benchmark def tsemaphoreContention() = unsafeRun(for { sem <- TSemaphore.make(fibers / 2L).commit fiber <- ZIO.forkAll(List.fill(fibers)(repeat(ops)(sem.withPermit(STM.succeedNow(1)).commit))) _ <- fiber.join } yield ()) @Benchmark def semaphoreCatsContention() = { import cats.effect.Concurrent import cats.effect.concurrent.Semaphore implicit val contextShift: ContextShift[CIO] = CIO.contextShift(ExecutionContext.global) (for { sem <- Semaphore(fibers / 2L)(Concurrent[CIO]) fiber <- catsForkAll(List.fill(fibers)(catsRepeat(ops)(sem.withPermit(CIO(1))))) _ <- fiber.join } yield ()).unsafeRunSync() } }
Example 3
Source File: ConcurrentScenarios.scala From canoe with MIT License | 5 votes |
package samples import canoe.api._ import canoe.syntax._ import cats.effect.concurrent.Semaphore import cats.effect.{ExitCode, IO, IOApp} import cats.syntax.all._ import fs2.Stream object ConcurrentScenarios extends IOApp { val token: String = "<your telegram token>" def run(args: List[String]): IO[ExitCode] = Stream .resource(TelegramClient.global[IO](token)) .flatMap { implicit client => Stream.eval(Semaphore[IO](0)).flatMap { sem => // Both scenarios use shared semaphore // to achieve the interaction across different chats. Bot.polling[IO].follow(pop(sem), push(sem)) } } .compile.drain.as(ExitCode.Success) def pop[F[_]: TelegramClient](semaphore: Semaphore[F]): Scenario[F, Unit] = for { m <- Scenario.expect(command("pop")) _ <- Scenario.eval(m.chat.send("Waiting for available elements..")) _ <- Scenario.eval(semaphore.acquire) _ <- Scenario.eval(m.reply("Done.")) } yield () def push[F[_]: TelegramClient](semaphore: Semaphore[F]): Scenario[F, Unit] = for { chat <- Scenario.expect(command("push").chat) _ <- Scenario.eval(semaphore.release) _ <- Scenario.eval(chat.send("Pushed one element.")) } yield () }
Example 4
Source File: CassandraSync.scala From kafka-journal with MIT License | 5 votes |
package com.evolutiongaming.kafka.journal.eventual.cassandra import cats.arrow.FunctionK import cats.effect.concurrent.Semaphore import cats.effect.{Concurrent, Sync, Timer} import cats.implicits._ import cats.~> import com.evolutiongaming.cassandra import com.evolutiongaming.cassandra.sync.AutoCreate import com.evolutiongaming.kafka.journal.Origin trait CassandraSync[F[_]] { def apply[A](fa: F[A]): F[A] } object CassandraSync { def empty[F[_]]: CassandraSync[F] = new CassandraSync[F] { def apply[A](fa: F[A]) = fa } def apply[F[_]](implicit F: CassandraSync[F]): CassandraSync[F] = F def apply[F[_] : Sync : Timer : CassandraSession]( config: SchemaConfig, origin: Option[Origin], ): CassandraSync[F] = { val keyspace = config.keyspace val autoCreate = if (keyspace.autoCreate) AutoCreate.Table else AutoCreate.None apply( keyspace = keyspace.name, table = config.locksTable, autoCreate = autoCreate, metadata = origin.map(_.value)) } def apply[F[_] : Sync : Timer : CassandraSession]( keyspace: String, table: String, autoCreate: AutoCreate, metadata: Option[String], ): CassandraSync[F] = { new CassandraSync[F] { def apply[A](fa: F[A]) = { val cassandraSync = cassandra.sync.CassandraSync.of[F]( session = CassandraSession[F].unsafe, keyspace = keyspace, table = table, autoCreate = autoCreate) for { cassandraSync <- cassandraSync result <- cassandraSync(id = "kafka-journal", metadata = metadata)(fa) } yield result } } } def of[F[_] : Concurrent : Timer : CassandraSession]( config: SchemaConfig, origin: Option[Origin] ): F[CassandraSync[F]] = { for { semaphore <- Semaphore[F](1) } yield { val cassandraSync = apply[F](config, origin) val serial = new (F ~> F) { def apply[A](fa: F[A]) = semaphore.withPermit(fa) } cassandraSync.mapK(serial, FunctionK.id) } } implicit class CassandraSyncOps[F[_]](val self: CassandraSync[F]) extends AnyVal { def mapK[G[_]](fg: F ~> G, gf: G ~> F): CassandraSync[G] = new CassandraSync[G] { def apply[A](fa: G[A]) = fg(self(gf(fa))) } } }
Example 5
Source File: SchedulerBuilder.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.joex.scheduler import cats.effect._ import cats.effect.concurrent.Semaphore import cats.implicits._ import fs2.concurrent.SignallingRef import docspell.store.Store import docspell.store.queue.JobQueue case class SchedulerBuilder[F[_]: ConcurrentEffect: ContextShift]( config: SchedulerConfig, tasks: JobTaskRegistry[F], store: Store[F], blocker: Blocker, queue: Resource[F, JobQueue[F]], logSink: LogSink[F] ) { def withConfig(cfg: SchedulerConfig): SchedulerBuilder[F] = copy(config = cfg) def withTaskRegistry(reg: JobTaskRegistry[F]): SchedulerBuilder[F] = copy(tasks = reg) def withTask[A](task: JobTask[F]): SchedulerBuilder[F] = withTaskRegistry(tasks.withTask(task)) def withQueue(queue: Resource[F, JobQueue[F]]): SchedulerBuilder[F] = SchedulerBuilder[F](config, tasks, store, blocker, queue, logSink) def withBlocker(blocker: Blocker): SchedulerBuilder[F] = copy(blocker = blocker) def withLogSink(sink: LogSink[F]): SchedulerBuilder[F] = copy(logSink = sink) def withQueue(queue: JobQueue[F]): SchedulerBuilder[F] = copy(queue = Resource.pure[F, JobQueue[F]](queue)) def serve: Resource[F, Scheduler[F]] = resource.evalMap(sch => ConcurrentEffect[F].start(sch.start.compile.drain).map(_ => sch) ) def resource: Resource[F, Scheduler[F]] = { val scheduler = for { jq <- queue waiter <- Resource.liftF(SignallingRef(true)) state <- Resource.liftF(SignallingRef(SchedulerImpl.emptyState[F])) perms <- Resource.liftF(Semaphore(config.poolSize.toLong)) } yield new SchedulerImpl[F]( config, blocker, jq, tasks, store, logSink, state, waiter, perms ) scheduler.evalTap(_.init).map(s => s: Scheduler[F]) } } object SchedulerBuilder { def apply[F[_]: ConcurrentEffect: ContextShift]( config: SchedulerConfig, blocker: Blocker, store: Store[F] ): SchedulerBuilder[F] = new SchedulerBuilder[F]( config, JobTaskRegistry.empty[F], store, blocker, JobQueue(store), LogSink.db[F](store) ) }
Example 6
Source File: traverse.scala From tofu with Apache License 2.0 | 5 votes |
package tofu package concurrent package syntax import cats.effect.Concurrent import cats.effect.concurrent.Semaphore import cats.syntax.parallel._ import cats.{Parallel, Traverse} import tofu.syntax.monadic._ object traverse { implicit final class TraverseOps[T[_], A](val ta: T[A]) extends AnyVal { @deprecated("Duplicates cats.effect.syntax.ParallelNSyntax of cats-effect 2.0.0", "0.6.3") def limitedTraverse[F[_], B]( batchSize: Int )(f: A => F[B])(implicit T: Traverse[T], F: Concurrent[F], P: Parallel[F]): F[T[B]] = for { semaphore <- Semaphore[F](batchSize.toLong) result <- ta.parTraverse(value => semaphore.withPermit(f(value))) } yield result } }
Example 7
Source File: MakeSemaphore.scala From tofu with Apache License 2.0 | 5 votes |
package tofu.concurrent import cats.effect.concurrent.Semaphore import cats.effect.{Concurrent, Sync} trait MakeSemaphore[I[_], F[_]] { def semaphore(count: Long): I[Semaphore[F]] } object Semaphores { def apply[F[_]](implicit agents: Semaphores[F]): MakeSemaphore.Applier[F, F] = new MakeSemaphore.Applier[F, F](agents) } object MakeSemaphore { def apply[I[_], F[_]](implicit mksem: MakeSemaphore[I, F]) = new Applier[I, F](mksem) final class Applier[I[_], F[_]](private val mksem: MakeSemaphore[I, F]) extends AnyVal { def of(count: Long): I[Semaphore[F]] = mksem.semaphore(count) } implicit def concurrentSemaphore[I[_]: Sync, F[_]: Concurrent]: MakeSemaphore[I, F] = new MakeSemaphore[I, F] { def semaphore(count: Long): I[Semaphore[F]] = Semaphore.in[I, F](count) } }
Example 8
Source File: lift.scala From tofu with Apache License 2.0 | 5 votes |
package tofu.syntax import cats.{Functor, ~>} import cats.effect.concurrent.{Deferred, MVar, Ref, Semaphore} import tofu.lift.{IsoK, Lift, Unlift} import cats.tagless.{FunctorK, InvariantK} import tofu.lift.{IsoK, Lift, Unlift} object lift { implicit final class LiftSyntax[F[_], A](private val fa: F[A]) extends AnyVal { def lift[G[_]](implicit lift: Lift[F, G]): G[A] = lift.lift(fa) } implicit final class MVarLiftSyntax[F[_], A](private val mvar: MVar[F, A]) extends AnyVal { def lift[G[_]](implicit lift: Lift[F, G]): MVar[G, A] = mvar.mapK(lift.liftF) } implicit final class RefLiftSyntax[F[_], A](private val ref: Ref[F, A]) extends AnyVal { def lift[G[_]](implicit lift: Lift[F, G], F: Functor[F]): Ref[G, A] = ref.mapK(lift.liftF) } implicit final class DeferredLiftSyntax[F[_], A](private val deferred: Deferred[F, A]) extends AnyVal { def lift[G[_]](implicit lift: Lift[F, G]): Deferred[G, A] = deferred.mapK(lift.liftF) } implicit final class SemaphoreLiftSyntax[F[_]](private val semaphore: Semaphore[F]) extends AnyVal { def ilift[G[_]](implicit lift: IsoK[F, G]): Semaphore[G] = semaphore.imapK(lift.tof, lift.fromF) def unlift[G[_]](implicit unlift: Unlift[F, G], G: Functor[G]): G[Semaphore[G]] = G.map(unlift.unlift)(backf => semaphore.imapK(unlift.liftF, backf)) } implicit final class CatsTaglessLiftSyntax[T[_[_]], F[_]](private val tf: T[F]) extends AnyVal { def lift[G[_]](implicit lift: Lift[F, G], fk: FunctorK[T]): T[G] = fk.mapK(tf)(lift.liftF) def ilift[G[_]](implicit lift: IsoK[F, G], fk: InvariantK[T]): T[G] = fk.imapK(tf)(lift.tof)(lift.fromF) def unlift[G[_]](implicit unlift: Unlift[F, G], G: Functor[G], fk: InvariantK[T]): G[T[G]] = G.map(unlift.unlift)(backf => fk.imapK(tf)(unlift.liftF)(backf)) } implicit final class CatsTagless1LiftSyntax[T[_[_], _], F[_], A](private val tf: T[F, A]) extends AnyVal { def mapK1[G[_]](f: F ~> G)(implicit fk: FunctorK[T[*[_], A]]): T[G, A] = fk.mapK(tf)(f) def imapK1[G[_]](f: F ~> G)(g: G ~> F)(implicit fk: InvariantK[T[*[_], A]]): T[G, A] = fk.imapK(tf)(f)(g) def lift1[G[_]](implicit lift: Lift[F, G], fk: FunctorK[T[*[_], A]]): T[G, A] = fk.mapK(tf)(lift.liftF) def ilift1[G[_]](implicit lift: IsoK[F, G], fk: InvariantK[T[*[_], A]]): T[G, A] = fk.imapK(tf)(lift.tof)(lift.fromF) def unlift1[G[_]](implicit unlift: Unlift[F, G], G: Functor[G], fk: InvariantK[T[*[_], A]]): G[T[G, A]] = G.map(unlift.unlift)(backf => fk.imapK(tf)(unlift.liftF)(backf)) } implicit final class CatsTagless2LiftSyntax[T[_[_], _, _], F[_], A, B](private val tf: T[F, A, B]) extends AnyVal { def mapK2[G[_]](f: F ~> G)(implicit fk: FunctorK[T[*[_], A, B]]): T[G, A, B] = fk.mapK(tf)(f) def imapK2[G[_]](f: F ~> G)(g: G ~> F)(implicit fk: InvariantK[T[*[_], A, B]]): T[G, A, B] = fk.imapK(tf)(f)(g) def lift2[G[_]](implicit lift: Lift[F, G], fk: FunctorK[T[*[_], A, B]]): T[G, A, B] = fk.mapK(tf)(lift.liftF) def ilift2[G[_]](implicit lift: IsoK[F, G], fk: InvariantK[T[*[_], A, B]]): T[G, A, B] = fk.imapK(tf)(lift.tof)(lift.fromF) def unlift2[G[_]](implicit unlift: Unlift[F, G], G: Functor[G], fk: InvariantK[T[*[_], A, B]]): G[T[G, A, B]] = G.map(unlift.unlift)(backf => fk.imapK(tf)(unlift.liftF)(backf)) } }
Example 9
Source File: CopyFile.scala From cats-effect-tutorial with Apache License 2.0 | 5 votes |
package catsEffectTutorial import cats.effect._ import cats.effect.concurrent.Semaphore import cats.implicits._ import java.io._ object CopyFile extends IOApp { def transmit(origin: InputStream, destination: OutputStream, buffer: Array[Byte], acc: Long): IO[Long] = for { amount <- IO(origin.read(buffer, 0, buffer.length)) count <- if(amount > -1) IO(destination.write(buffer, 0, amount)) >> transmit(origin, destination, buffer, acc + amount) else IO.pure(acc) // End of read stream reached (by java.io.InputStream contract), nothing to write } yield count // Returns the actual amount of bytes transmitted def transfer(origin: InputStream, destination: OutputStream): IO[Long] = for { buffer <- IO{ new Array[Byte](1024 * 10) } // Allocated only when the IO is evaluated total <- transmit(origin, destination, buffer, 0L) } yield total def inputStream(f: File, guard: Semaphore[IO]): Resource[IO, FileInputStream] = Resource.make { IO(new FileInputStream(f)) } { inStream => guard.withPermit { IO(inStream.close()).handleErrorWith(_ => IO.unit) } } def outputStream(f: File, guard: Semaphore[IO]): Resource[IO, FileOutputStream] = Resource.make { IO(new FileOutputStream(f)) } { outStream => guard.withPermit { IO(outStream.close()).handleErrorWith(_ => IO.unit) } } def inputOutputStreams(in: File, out: File, guard: Semaphore[IO]): Resource[IO, (InputStream, OutputStream)] = for { inStream <- inputStream(in, guard) outStream <- outputStream(out, guard) } yield (inStream, outStream) def copy(origin: File, destination: File): IO[Long] = for { guard <- Semaphore[IO](1) count <- inputOutputStreams(origin, destination, guard).use { case (in, out) => guard.withPermit(transfer(in, out)) } } yield count // The 'main' function of IOApp // override def run(args: List[String]): IO[ExitCode] = for { _ <- if(args.length < 2) IO.raiseError(new IllegalArgumentException("Need origin and destination files")) else IO.unit orig = new File(args.head) dest = new File(args.tail.head) count <- copy(orig, dest) _ <- IO(println(s"$count bytes copied from ${orig.getPath} to ${dest.getPath}")) } yield ExitCode.Success }
Example 10
Source File: CopyFilePolymorphic.scala From cats-effect-tutorial with Apache License 2.0 | 5 votes |
package catsEffectTutorial import cats.effect._ import cats.effect.concurrent.Semaphore import cats.implicits._ import java.io._ object CopyFilePolymorphic extends IOApp { def transmit[F[_]: Sync](origin: InputStream, destination: OutputStream, buffer: Array[Byte], acc: Long): F[Long] = for { amount <- Sync[F].delay(origin.read(buffer, 0, buffer.length)) count <- if(amount > -1) Sync[F].delay(destination.write(buffer, 0, amount)) >> transmit(origin, destination, buffer, acc + amount) else Sync[F].pure(acc) // End of read stream reached (by java.io.InputStream contract), nothing to write } yield count // Returns the actual amount of bytes transmitted def transfer[F[_]: Sync](origin: InputStream, destination: OutputStream): F[Long] = for { buffer <- Sync[F].delay( new Array[Byte](1024 * 10) ) // Allocated only when F is evaluated total <- transmit(origin, destination, buffer, 0L) } yield total def inputStream[F[_]: Sync](f: File, guard: Semaphore[F]): Resource[F, FileInputStream] = Resource.make { Sync[F].delay(new FileInputStream(f)) } { inStream => guard.withPermit { Sync[F].delay(inStream.close()).handleErrorWith(_ => Sync[F].unit) } } def outputStream[F[_]: Sync](f: File, guard: Semaphore[F]): Resource[F, FileOutputStream] = Resource.make { Sync[F].delay(new FileOutputStream(f)) } { outStream => guard.withPermit { Sync[F].delay(outStream.close()).handleErrorWith(_ => Sync[F].unit) } } def inputOutputStreams[F[_]: Sync](in: File, out: File, guard: Semaphore[F]): Resource[F, (InputStream, OutputStream)] = for { inStream <- inputStream(in, guard) outStream <- outputStream(out, guard) } yield (inStream, outStream) def copy[F[_]: Concurrent](origin: File, destination: File): F[Long] = for { guard <- Semaphore[F](1) count <- inputOutputStreams(origin, destination, guard).use { case (in, out) => guard.withPermit(transfer(in, out)) } } yield count // The 'main' function of IOApp // override def run(args: List[String]): IO[ExitCode] = for { _ <- if(args.length < 2) IO.raiseError(new IllegalArgumentException("Need origin and destination files")) else IO.unit orig = new File(args.head) dest = new File(args.tail.head) count <- copy[IO](orig, dest) _ <- IO(println(s"$count bytes copied from ${orig.getPath} to ${dest.getPath}")) } yield ExitCode.Success }
Example 11
Source File: Linebacker.scala From linebacker with MIT License | 5 votes |
package io.chrisdavenport.linebacker import cats.effect._ import cats.effect.concurrent.Semaphore import cats.implicits._ import java.util.concurrent.ExecutorService import scala.concurrent.ExecutionContext trait Linebacker[F[_]] { def blockingContext: ExecutionContext final def blockCS[A](fa: F[A])(implicit cs: ContextShift[F]): F[A] = blockContextShift(fa) } object Linebacker { def apply[F[_]](implicit ev: Linebacker[F]): Linebacker[F] = ev def fromExecutorService[F[_]](es: ExecutorService): Linebacker[F] = new Linebacker[F] { def blockingContext = ExecutionContext.fromExecutorService(es) } def fromExecutionContext[F[_]](ec: ExecutionContext): Linebacker[F] = new Linebacker[F] { def blockingContext = ec } def bounded[F[_]: Concurrent](lb: Linebacker[F], bound: Long): F[Linebacker[F]] = Semaphore[F](bound).map(new BoundedLinebacker(lb, _)) private class BoundedLinebacker[F[_]: Concurrent](lb: Linebacker[F], s: Semaphore[F]) extends Linebacker[F]{ def blockingContext: ExecutionContext = lb.blockingContext override def blockContextShift[A](fa: F[A])(implicit cs: ContextShift[F]): F[A] = s.withPermit(lb.blockContextShift(fa)) } }
Example 12
Source File: DualContext.scala From linebacker with MIT License | 5 votes |
package io.chrisdavenport.linebacker import cats.effect._ import cats.effect.concurrent.Semaphore import cats.implicits._ import java.util.concurrent.ExecutorService import scala.concurrent.ExecutionContext trait DualContext[F[_]] extends Linebacker[F] { def blockingContext: ExecutionContext def contextShift: ContextShift[F] def block[A](fa: F[A]): F[A] = contextShift.evalOn(blockingContext)(fa) } object DualContext { def apply[F[_]](implicit ev: DualContext[F]) = ev def fromContexts[F[_]]( cs: ContextShift[F], blocking: ExecutionContext): DualContext[F] = new DualContext[F] { override def blockingContext = blocking override def contextShift = cs } def fromExecutorService[F[_]]( default: ContextShift[F], blocking: ExecutorService): DualContext[F] = fromContexts(default, ExecutionContext.fromExecutor(blocking)) def bounded[F[_]: Concurrent](lb: DualContext[F], bound: Long): F[DualContext[F]] = Semaphore[F](bound).map(new BoundedDualContext(lb, _)) private class BoundedDualContext[F[_]: Concurrent](dc: DualContext[F], s: Semaphore[F]) extends DualContext[F]{ def blockingContext: ExecutionContext = dc.blockingContext def contextShift: ContextShift[F] = dc.contextShift override def blockContextShift[A](fa: F[A])(implicit cs: ContextShift[F]): F[A] = s.withPermit(dc.blockContextShift(fa)) override def block[A](fa: F[A]): F[A] = s.withPermit(dc.block(fa)) } }