New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add MVar #217
Add MVar #217
Changes from all commits
177f73f
0ee507c
5ec0706
26b8c61
3b6d8d7
88fc481
5a91dcf
7cd538a
a6dfa4b
140458d
87d577b
e27c707
8837970
5c33198
e61bf96
a027384
18d3161
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
/* | ||
* Copyright (c) 2017-2018 The Typelevel Cats-effect Project Developers | ||
* | ||
* 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 cats.effect | ||
package concurrent | ||
|
||
import cats.effect.internals.{MVarAsync, MVarConcurrent} | ||
|
||
/** | ||
* A mutable location, that is either empty or contains | ||
* a value of type `A`. | ||
* | ||
* It has 3 fundamental atomic operations: | ||
* | ||
* - [[put]] which fills the var if empty, or blocks | ||
* (asynchronously) until the var is empty again | ||
* - [[take]] which empties the var if full, returning the contained | ||
* value, or blocks (asynchronously) otherwise until there is | ||
* a value to pull | ||
* - [[read]] which reads the current value without touching it, | ||
* assuming there is one, or otherwise it waits until a value | ||
* is made available via `put` | ||
* | ||
* The `MVar` is appropriate for building synchronization | ||
* primitives and performing simple inter-thread communications. | ||
* If it helps, it's similar with a `BlockingQueue(capacity = 1)`, | ||
* except that it doesn't block any threads, all waiting being | ||
* done asynchronously (via [[Async]] or [[Concurrent]] data types, | ||
* such as [[IO]]). | ||
* | ||
* Given its asynchronous, non-blocking nature, it can be used on | ||
* top of Javascript as well. | ||
* | ||
* Inspired by `Control.Concurrent.MVar` from Haskell and | ||
* by `scalaz.concurrent.MVar`. | ||
*/ | ||
abstract class MVar[F[_], A] { | ||
/** | ||
* Fills the `MVar` if it is empty, or blocks (asynchronously) | ||
* if the `MVar` is full, until the given value is next in | ||
* line to be consumed on [[take]]. | ||
* | ||
* This operation is atomic. | ||
* | ||
* @return a task that on evaluation will complete when the | ||
* `put` operation succeeds in filling the `MVar`, | ||
* with the given value being next in line to | ||
* be consumed | ||
*/ | ||
def put(a: A): F[Unit] | ||
|
||
/** | ||
* Empties the `MVar` if full, returning the contained value, | ||
* or blocks (asynchronously) until a value is available. | ||
* | ||
* This operation is atomic. | ||
* | ||
* @return a task that on evaluation will be completed after | ||
* a value was retrieved | ||
*/ | ||
def take: F[A] | ||
|
||
/** | ||
* Tries reading the current value, or blocks (asynchronously) | ||
* until there is a value available. | ||
* | ||
* This operation is atomic. | ||
* | ||
* @return a task that on evaluation will be completed after | ||
* a value has been read | ||
*/ | ||
def read: F[A] | ||
} | ||
|
||
/** Builders for [[MVar]]. */ | ||
object MVar { | ||
/** | ||
* Builds an [[MVar]] value for `F` data types that are [[Concurrent]]. | ||
* | ||
* Due to `Concurrent`'s capabilities, the yielded values by [[MVar.take]] | ||
* and [[MVar.put]] are cancelable. | ||
* | ||
* This builder uses the | ||
* [[https://typelevel.org/cats/guidelines.html#partially-applied-type-params Partially-Applied Type]] | ||
* technique. | ||
* | ||
* For creating an empty `MVar`: | ||
* {{{ | ||
* MVar[IO].empty[Int] <-> MVar.empty[IO, Int] | ||
* }}} | ||
* | ||
* For creating an `MVar` with an initial value: | ||
* {{{ | ||
* MVar[IO].init("hello") <-> MVar.init[IO, String]("hello") | ||
* }}} | ||
* | ||
* @see [[init]], [[initF]] and [[empty]] | ||
*/ | ||
def apply[F[_]](implicit F: Concurrent[F]): ApplyBuilders[F] = | ||
new ApplyBuilders[F](F) | ||
|
||
/** | ||
* Creates a cancelable `MVar` that starts as empty. | ||
* | ||
* @see [[emptyAsync]] for non-cancelable MVars | ||
* | ||
* @param F is a [[Concurrent]] constraint, needed in order to | ||
* describe cancelable operations | ||
*/ | ||
def empty[F[_], A](implicit F: Concurrent[F]): F[MVar[F, A]] = | ||
F.delay(MVarConcurrent.empty) | ||
|
||
/** | ||
* Creates a non-cancelable `MVar` that starts as empty. | ||
* | ||
* The resulting `MVar` has non-cancelable operations. | ||
* | ||
* @see [[empty]] for creating cancelable MVars | ||
*/ | ||
def emptyAsync[F[_], A](implicit F: Async[F]): F[MVar[F, A]] = | ||
F.delay(MVarAsync.empty) | ||
|
||
/** | ||
* Creates a cancelable `MVar` that's initialized to an `initial` | ||
* value. | ||
* | ||
* @see [[initAsync]] for non-cancelable MVars | ||
* | ||
* @param initial is a value that will be immediately available | ||
* for the first `read` or `take` operation | ||
* | ||
* @param F is a [[Concurrent]] constraint, needed in order to | ||
* describe cancelable operations | ||
*/ | ||
def init[F[_], A](initial: A)(implicit F: Concurrent[F]): F[MVar[F, A]] = | ||
F.delay(MVarConcurrent(initial)) | ||
|
||
/** | ||
* Creates a non-cancelable `MVar` that's initialized to an `initial` | ||
* value. | ||
* | ||
* The resulting `MVar` has non-cancelable operations. | ||
* | ||
* @see [[init]] for creating cancelable MVars | ||
*/ | ||
def initAsync[F[_], A](initial: A)(implicit F: Async[F]): F[MVar[F, A]] = | ||
F.delay(MVarAsync(initial)) | ||
|
||
/** | ||
* Creates a cancelable `MVar` initialized with a value given | ||
* in the `F[A]` context, thus the initial value being lazily evaluated. | ||
* | ||
* @see [[init]] for creating MVars initialized with strict values | ||
* @see [[initAsyncF]] for building non-cancelable MVars | ||
* @param fa is the value that's going to be used as this MVar's | ||
* initial value, available then for the first `take` or `read` | ||
* @param F is a [[Concurrent]] constraint, needed in order to | ||
* describe cancelable operations | ||
*/ | ||
def initF[F[_], A](fa: F[A])(implicit F: Concurrent[F]): F[MVar[F, A]] = | ||
F.map(fa)(MVarConcurrent.apply(_)) | ||
|
||
/** | ||
* Creates a non-cancelable `MVar` initialized with a value given | ||
* in the `F[A]` context, thus the initial value being lazily evaluated. | ||
* | ||
* @see [[initAsync]] for creating MVars initialized with strict values | ||
* @see [[initF]] for building cancelable MVars | ||
* @param fa is the value that's going to be used as this MVar's | ||
* initial value, available then for the first `take` or `read` | ||
*/ | ||
def initAsyncF[F[_], A](fa: F[A])(implicit F: Async[F]): F[MVar[F, A]] = | ||
F.map(fa)(MVarAsync.apply(_)) | ||
|
||
/** | ||
* Returned by the [[apply]] builder. | ||
*/ | ||
final class ApplyBuilders[F[_]](val F: Concurrent[F]) extends AnyVal { | ||
/** | ||
* Builds an `MVar` with an initial value. | ||
* | ||
* @see documentation for [[MVar.init]] | ||
*/ | ||
def init[A](a: A): F[MVar[F, A]] = | ||
MVar.init(a)(F) | ||
|
||
/** | ||
* Builds an `MVar` with an initial value that's lazily evaluated. | ||
* | ||
* @see documentation for [[MVar.initF]] | ||
*/ | ||
def initF[A](fa: F[A]): F[MVar[F, A]] = | ||
MVar.initF(fa)(F) | ||
|
||
/** | ||
* Builds an empty `MVar`. | ||
* | ||
* @see documentation for [[MVar.empty]] | ||
*/ | ||
def empty[A]: F[MVar[F, A]] = | ||
MVar.empty(F) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,10 +41,10 @@ private[effect] object Callback { | |
} | ||
|
||
/** Reusable `Right(())` reference. */ | ||
final val rightUnit = Right(()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why are these not final now? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AFAIK There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @non I thought told me that was only if there is no type annotation, so we could add that but keep the final. But in any case probably not a big deal. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any difference between From the JVM's point of view both will be |
||
val rightUnit = Right(()) | ||
|
||
/** Reusable no-op, side-effectful `Function1` reference. */ | ||
final val dummy1: Any => Unit = _ => () | ||
val dummy1: Any => Unit = _ => () | ||
|
||
/** Builds a callback with async execution. */ | ||
def async[A](cb: Type[A]): Type[A] = | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this pattern - can we use it in
Ref
too? Specifically the notion of having both the regular method on companion and thenApplyBuilders
for the type curried type params.