All Downloads are FREE. Search and download functionalities are using the official Maven repository.

coop.MVar.scala Maven / Gradle / Ivy

There is a newer version: 0.7.2
Show newest version
/*
 * Copyright 2020 Daniel Spiewak
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package coop

import cats.{Functor, Monad}
import cats.data.Kleisli
import cats.implicits._
import cats.mtl.ApplicativeAsk

import ThreadF.MonitorId

final class MVar[A] private (monitor: MonitorId) { outer =>

  private[this] val Key = this.asInstanceOf[MVar[Any]]

  def tryRead[F[_]: Functor: MVar.Ask]: F[Option[A]] = getU[F]

  def read[F[_]: Monad: ApplicativeThread: MVar.Ask]: F[A] =
    tryRead[F] flatMap {
      case Some(a) => a.pure[F]
      case None => ApplicativeThread[F].await(monitor) >> read[F]
    }

  def tryPut[F[_]: Monad: ApplicativeThread: MVar.Ask](a: A): F[Boolean] =
    getU[F] flatMap {
      case Some(_) =>
        false.pure[F]

      case None =>
        setU[F](a).as(true)
    }

  def put[F[_]: Monad: ApplicativeThread: MVar.Ask](a: A): F[Unit] =
    tryPut[F](a).ifM(().pure[F], ApplicativeThread[F].await(monitor) >> put[F](a))

  def tryTake[F[_]: Monad: ApplicativeThread: MVar.Ask]: F[Option[A]] =
    getU[F] flatMap {
      case Some(a) =>
        removeU[F].as(Some(a): Option[A])

      case None =>
        (None: Option[A]).pure[F]
    }

  def take[F[_]: Monad: ApplicativeThread: MVar.Ask]: F[A] =
    tryTake[F] flatMap {
      case Some(a) => a.pure[F]
      case None => ApplicativeThread[F].await(monitor) >> take[F]
    }

  def swap[F[_]: Monad: ApplicativeThread: MVar.Ask](a: A): F[A] =
    getU[F] flatMap {
      case Some(oldA) =>
        setU[F](a).as(oldA)

      case None =>
        ApplicativeThread[F].await(monitor) >> swap[F](a)
    }

  def apply[F[_]: Monad: ApplicativeThread: MVar.Ask]: MVarPartiallyApplied[F] =
    new MVarPartiallyApplied[F]

  class MVarPartiallyApplied[F[_]: Monad: ApplicativeThread: MVar.Ask] {

    val tryRead: F[Option[A]] = outer.tryRead[F]

    val read: F[A] = outer.read[F]

    def tryPut(a: A): F[Boolean] = outer.tryPut[F](a)

    def put(a: A): F[Unit] = outer.put[F](a)

    val tryTake: F[Option[A]] = outer.tryTake[F]

    val take: F[A] = outer.take[F]

    def swap(a: A): F[A] = outer.swap[F](a)
  }

  private[this] def getU[F[_]: Functor: MVar.Ask]: F[Option[A]] =
    ApplicativeAsk[F, MVar.Universe].ask.map(_().get(Key).map(_.asInstanceOf[A]))

  private[this] def setU[F[_]: Monad: MVar.Ask: ApplicativeThread](a: A): F[Unit] =
    ApplicativeAsk[F, MVar.Universe].ask.map(_() += (Key -> a.asInstanceOf[Any])) >>
      ApplicativeThread[F].notify(monitor)

  private[this] def removeU[F[_]: Monad: MVar.Ask: ApplicativeThread]: F[Unit] =
    ApplicativeAsk[F, MVar.Universe].ask.map(_() -= Key) >>
      ApplicativeThread[F].notify(monitor)
}

object MVar {
  // we use a kleisli of a ref of a map here rather than StateT to avoid issues with zeros in F
  // the Any(s) are required due to the existentiality of the A types
  type Universe = UnsafeRef[Map[MVar[Any], Any]]
  type Ask[F[_]] = ApplicativeAsk[F, Universe]

  def empty[F[_]: Functor: ApplicativeThread, A]: F[MVar[A]] =
    ApplicativeThread[F].monitor.map(new MVar[A](_))    // not actually pure due to object identity, but whatevs

  def apply[F[_]: Monad: ApplicativeThread: Ask, A](a: A): F[MVar[A]] =
    empty[F, A].flatMap(mv => mv.put[F](a).as(mv))

  def resolve[F[_], A](mvt: Kleisli[F, Universe, A]): F[A] =
    mvt.run(new UnsafeRef(Map[MVar[Any], Any]()))
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy