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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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))
  }
}