Skip to content

Commit

Permalink
Merge pull request #2132 from djspiewak/feature/more-scaladoc
Browse files Browse the repository at this point in the history
More scaladoc
  • Loading branch information
vasilmkd authored Jul 24, 2021
2 parents 639557e + 0c299b1 commit d520e50
Show file tree
Hide file tree
Showing 5 changed files with 495 additions and 11 deletions.
4 changes: 1 addition & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -312,9 +312,7 @@ lazy val core = crossProject(JSPlatform, JVMPlatform)
ProblemFilters.exclude[MissingClassProblem]("cats.effect.SyncIO$Delay"),
ProblemFilters.exclude[DirectMissingMethodProblem]("cats.effect.IO#IOCont.apply"),
ProblemFilters.exclude[DirectMissingMethodProblem]("cats.effect.IO#IOCont.copy"),
ProblemFilters.exclude[DirectMissingMethodProblem]("cats.effect.IO#IOCont.this")
)
)
ProblemFilters.exclude[DirectMissingMethodProblem]("cats.effect.IO#IOCont.this")))
.jvmSettings(
javacOptions ++= Seq("-source", "1.8", "-target", "1.8")
)
Expand Down
164 changes: 163 additions & 1 deletion core/js/src/main/scala/cats/effect/IOApp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,169 @@ import scala.concurrent.CancellationException
import scala.concurrent.duration._
import scala.scalajs.js

/**
* The primary entry point to a Cats Effect application. Extend this
* trait rather than defining your own `main` method. This avoids the
* need to run [[IO.unsafeRunSync]] (or similar) on your own.
*
* `IOApp` takes care of the messy details of properly setting up
* (and tearing down) the [[unsafe.IORuntime]] needed to run the [[IO]]
* which represents your application. All of the associated thread
* pools (if relevant) will be configured with the assumption that
* your application is fully contained within the `IO` produced by
* the [[run]] method. Note that the exact details of how the runtime
* will be configured are very platform-specific. Part of the point
* of `IOApp` is to insulate users from the details of the underlying
* runtime (whether JVM or JavaScript).
*
* {{{
* object MyApplication extends IOApp {
* def run(args: List[String]) =
* for {
* _ <- IO.print("Enter your name: ")
* name <- IO.readln
* _ <- IO.println("Hello, " + name)
* } yield ExitCode.Success
* }
* }}}
*
* In the above example, `MyApplication` will be a runnable class with
* a `main` method, visible to Sbt, IntelliJ, or plain-old `java`. When
* run externally, it will print, read, and print in the obvious way,
* producing a final process exit code of 0. Any exceptions thrown within
* the `IO` will be printed to standard error and the exit code will be
* set to 1. In the event that the main [[Fiber]] (represented by the `IO`
* returned by `run`) is canceled, the runtime will produce an exit code of 1.
*
* Note that exit codes are an implementation-specific feature of the
* underlying runtime, as are process arguments. Naturally, all JVMs
* support these functions, as does NodeJS, but some JavaScript execution
* environments will be unable to replicate these features (or they simply
* may not make sense). In such cases, exit codes may be ignored and/or
* argument lists may be empty.
*
* Note that in the case of the above example, we would actually be
* better off using [[IOApp.Simple]] rather than `IOApp` directly, since
* we are neither using `args` nor are we explicitly producing a custom
* [[ExitCode]]:
*
* {{{
* object MyApplication extends IOApp.Simple {
* val run =
* for {
* _ <- IO.print("Enter your name: ")
* name <- IO.readln
* _ <- IO.println(s"Hello, " + name)
* } yield ()
* }
* }}}
*
* It is valid to define `val run` rather than `def run` because `IO`'s
* evaluation is lazy: it will only run when the `main` method is
* invoked by the runtime.
*
* In the event that the process receives an interrupt signal (`SIGINT`) due
* to Ctrl-C (or any other mechanism), it will immediately `cancel` the main
* fiber. Assuming this fiber is not within an `uncancelable` region, this
* will result in interrupting any current activities and immediately invoking
* any finalizers (see: [[IO.onCancel]] and [[IO.bracket]]). The process will
* not shut down until the finalizers have completed. For example:
*
* {{{
* object InterruptExample extends IOApp.Simple {
* val run =
* IO.bracket(startServer)(
* _ => IO.never)(
* server => IO.println("shutting down") *> server.close)
* }
* }}}
*
* If we assume the `startServer` function has type `IO[Server]` (or similar),
* this kind of pattern is very common. When this process receives a `SIGINT`,
* it will immediately print "shutting down" and run the `server.close` effect.
*
* One consequence of this design is it is possible to build applications which
* will ignore process interrupts. For example, if `server.close` runs forever,
* the process will ignore interrupts and will need to be cleaned up using
* `SIGKILL` (i.e. `kill -9`). This same phenomenon can be demonstrated by using
* [[IO.uncancelable]] to suppress all interruption within the application
* itself:
*
* {{{
* object Zombie extends IOApp.Simple {
* val run = IO.never.uncancelable
* }
* }}}
*
* The above process will run forever and ignore all interrupts. The only way
* it will shut down is if it receives `SIGKILL`.
*
* It is possible (though not necessary) to override various platform-specific
* runtime configuration options, such as `computeWorkerThreadCount` (which only
* exists on the JVM). Please note that the default configurations have been
* extensively benchmarked and are optimal (or close to it) in most conventional
* scenarios.
*
* However, with that said, there really is no substitute to benchmarking your
* own application. Every application and scenario is unique, and you will
* always get the absolute best results by performing your own tuning rather
* than trusting someone else's defaults. `IOApp`'s defaults are very ''good'',
* but they are not perfect in all cases. One common example of this is
* applications which maintain network or file I/O worker threads which are
* under heavy load in steady-state operations. In such a performance profile,
* it is usually better to reduce the number of compute worker threads to
* "make room" for the I/O workers, such that they all sum to the number of
* physical threads exposed by the kernel.
*
* @see [[IO]]
* @see [[run]]
* @see [[ResourceApp]]
* @see [[IOApp.Simple]]
*/
trait IOApp {

private[this] var _runtime: unsafe.IORuntime = null

/**
* The runtime which will be used by `IOApp` to evaluate the
* [[IO]] produced by the `run` method. This may be overridden
* by `IOApp` implementations which have extremely specialized
* needs, but this is highly unlikely to ever be truly needed.
* As an example, if an application wishes to make use of an
* alternative compute thread pool (such as `Executors.fixedThreadPool`),
* it is almost always better to leverage [[IO.evalOn]] on the value
* produced by the `run` method, rather than directly overriding
* `runtime`.
*
* In other words, this method is made available to users, but its
* use is strongly discouraged in favor of other, more precise
* solutions to specific use-cases.
*
* This value is guaranteed to be equal to [[unsafe.IORuntime.global]].
*/
protected def runtime: unsafe.IORuntime = _runtime

/**
* The configuration used to initialize the [[runtime]] which will
* evaluate the [[IO]] produced by `run`. It is very unlikely that
* users will need to override this method.
*/
protected def runtimeConfig: unsafe.IORuntimeConfig = unsafe.IORuntimeConfig()

/**
* The entry point for your application. Will be called by the runtime
* when the process is started. If the underlying runtime supports it,
* any arguments passed to the process will be made available in the
* `args` parameter. The numeric value within the resulting [[ExitCode]]
* will be used as the exit code when the process terminates unless
* terminated exceptionally or by interrupt.
*
* @param args The arguments passed to the process, if supported by the
* underlying runtime. For example, `java com.company.MyApp --foo --bar baz`
* or `node com-mycompany-fastopt.js --foo --bar baz` would each
* result in `List("--foo", "--bar", "baz")`.
* @see [[IOApp.Simple!.run:cats\.effect\.IO[Unit]*]]
*/
def run(args: List[String]): IO[ExitCode]

final def main(args: Array[String]): Unit = {
Expand Down Expand Up @@ -92,9 +248,15 @@ trait IOApp {

object IOApp {

/**
* A simplified version of [[IOApp]] for applications which ignore their
* process arguments and always produces [[ExitCode.Success]] (unless
* terminated exceptionally or interrupted).
*
* @see [[IOApp]]
*/
trait Simple extends IOApp {
def run: IO[Unit]
final def run(args: List[String]): IO[ExitCode] = run.as(ExitCode.Success)
}

}
Loading

0 comments on commit d520e50

Please sign in to comment.