├── .github
├── .gitignore
└── workflows
│ ├── pkgdown.yaml
│ ├── R-CMD-check.yaml
│ └── test-coverage.yaml
├── vignettes
├── .gitignore
└── comparison-benchmarks-1.png
├── LICENSE
├── man
├── figures
│ ├── logo.png
│ └── parabar-design.png
├── LOGO.Rd
├── Helper.Rd
├── Warning.Rd
├── make_logo.Rd
├── Exception.Rd
├── configure_bar.Rd
├── BarFactory.Rd
├── ContextFactory.Rd
├── clear.Rd
├── BackendFactory.Rd
├── stop_backend.Rd
├── peek.Rd
├── evaluate.Rd
├── export.Rd
├── option.Rd
├── par_lapply.Rd
├── par_sapply.Rd
├── Bar.Rd
├── Specification.Rd
├── BasicBar.Rd
├── ModernBar.Rd
├── par_apply.Rd
├── Backend.Rd
├── Options.Rd
├── start_backend.Rd
├── SessionState.Rd
└── TaskState.Rd
├── pkgdown
├── favicon
│ ├── favicon.ico
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── apple-touch-icon.png
│ ├── apple-touch-icon-60x60.png
│ ├── apple-touch-icon-76x76.png
│ ├── apple-touch-icon-120x120.png
│ ├── apple-touch-icon-152x152.png
│ └── apple-touch-icon-180x180.png
└── extra.scss
├── .gitignore
├── tests
├── testthat
│ ├── test-package-setup.R
│ ├── test-bar-factory.R
│ ├── test-backend-factory.R
│ ├── test-context-factory.R
│ ├── test-service.R
│ ├── test-bar.R
│ ├── test-backend.R
│ ├── test-logo.R
│ ├── test-options.R
│ ├── test-sync-backend.R
│ ├── test-context.R
│ ├── test-backend-finalizer.R
│ ├── test-states.R
│ ├── test-async-backend.R
│ └── test-specification.R
└── testthat.R
├── .Rbuildignore
├── parabar.Rproj
├── man-roxygen
├── make-logo.R
├── get-option.R
├── set-default-options.R
├── clear.R
├── stop-backend.R
├── evaluate.R
├── peek.R
├── export.R
├── set-option.R
├── configure-bar.R
├── par-lapply.R
├── par-sapply.R
├── par-apply.R
└── start-backend.R
├── inst
└── assets
│ └── logo
│ └── parabar-logo.txt
├── LICENSE.md
├── _pkgdown.yml
├── NAMESPACE
├── DESCRIPTION
└── R
├── parabar-package.R
├── BarFactory.R
├── ContextFactory.R
├── BackendFactory.R
├── logo.R
├── Bar.R
├── Backend.R
├── Warning.R
├── Helper.R
├── BasicBar.R
├── ModernBar.R
├── SessionState.R
├── exports.R
└── TaskState.R
/.github/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 |
--------------------------------------------------------------------------------
/vignettes/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 | *.R
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | YEAR: 2022
2 | COPYRIGHT HOLDER: Mihai A. Constantin
3 |
--------------------------------------------------------------------------------
/man/figures/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mihaiconstantin/parabar/HEAD/man/figures/logo.png
--------------------------------------------------------------------------------
/pkgdown/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mihaiconstantin/parabar/HEAD/pkgdown/favicon/favicon.ico
--------------------------------------------------------------------------------
/man/figures/parabar-design.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mihaiconstantin/parabar/HEAD/man/figures/parabar-design.png
--------------------------------------------------------------------------------
/pkgdown/favicon/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mihaiconstantin/parabar/HEAD/pkgdown/favicon/favicon-16x16.png
--------------------------------------------------------------------------------
/pkgdown/favicon/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mihaiconstantin/parabar/HEAD/pkgdown/favicon/favicon-32x32.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mihaiconstantin/parabar/HEAD/pkgdown/favicon/apple-touch-icon.png
--------------------------------------------------------------------------------
/vignettes/comparison-benchmarks-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mihaiconstantin/parabar/HEAD/vignettes/comparison-benchmarks-1.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mihaiconstantin/parabar/HEAD/pkgdown/favicon/apple-touch-icon-60x60.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mihaiconstantin/parabar/HEAD/pkgdown/favicon/apple-touch-icon-76x76.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mihaiconstantin/parabar/HEAD/pkgdown/favicon/apple-touch-icon-120x120.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mihaiconstantin/parabar/HEAD/pkgdown/favicon/apple-touch-icon-152x152.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mihaiconstantin/parabar/HEAD/pkgdown/favicon/apple-touch-icon-180x180.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .Rproj.user
2 | .Rhistory
3 | .DS_Store
4 | .vscode/
5 | parabar.Rcheck/
6 | *.tar.gz
7 | **/temp*
8 | .temp
9 | .cache
10 | docs
11 | inst/doc
12 | /doc/
13 | /Meta/
14 |
--------------------------------------------------------------------------------
/tests/testthat/test-package-setup.R:
--------------------------------------------------------------------------------
1 | # Test `parabar` package setup.
2 |
3 | test_that("'parabar' package attaches correctly", {
4 | # Expect the package to be in the search path.
5 | expect_true("package:parabar" %in% search())
6 |
7 | # Expect the package options to be in the session after loading.
8 | expect_true(is(getOption("parabar"), "Options"))
9 | })
10 |
--------------------------------------------------------------------------------
/.Rbuildignore:
--------------------------------------------------------------------------------
1 | ^.*\.Rproj$
2 | ^\.Rproj\.user$
3 | ^LICENSE.md$
4 | ^\.vscode$
5 | ^inst/debug$
6 | ^inst/design$
7 | ^archive$
8 | ^\.github$
9 | ^man-roxygen$
10 | ^CNAME$
11 | ^temp.*$
12 | ^docs$
13 | ^package\.json$
14 | ^package-lock\.json$
15 | ^yarn\.lock$
16 | ^node_modules$
17 | ^_pkgdown\.yml$
18 | ^pkgdown$
19 | ^vignettes/articles$
20 | ^doc$
21 | ^Meta$
22 | ^vignettes/.*\.Rmd\.orig$
23 |
--------------------------------------------------------------------------------
/parabar.Rproj:
--------------------------------------------------------------------------------
1 | Version: 1.0
2 |
3 | RestoreWorkspace: No
4 | SaveWorkspace: No
5 | AlwaysSaveHistory: Default
6 |
7 | EnableCodeIndexing: Yes
8 | UseSpacesForTab: Yes
9 | NumSpacesForTab: 4
10 | Encoding: UTF-8
11 |
12 | RnwWeave: Sweave
13 | LaTeX: pdfLaTeX
14 |
15 | AutoAppendNewline: Yes
16 | StripTrailingWhitespace: Yes
17 | LineEndingConversion: Posix
18 |
19 | BuildType: Package
20 | PackageUseDevtools: Yes
21 | PackageInstallArgs: --no-multiarch --with-keep.source
22 | PackageRoxygenize: rd,collate,namespace
23 |
--------------------------------------------------------------------------------
/man/LOGO.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/logo.R
3 | \docType{data}
4 | \name{LOGO}
5 | \alias{LOGO}
6 | \title{The Package Logo}
7 | \format{
8 | An object of class \code{character} containing the \code{ASCII} logo.
9 | }
10 | \usage{
11 | LOGO
12 | }
13 | \description{
14 | The logo is generated by \code{\link[=make_logo]{make_logo()}} and displayed on package
15 | attach for interactive \code{R} sessions.
16 | }
17 | \examples{
18 | print(LOGO)
19 |
20 | }
21 | \seealso{
22 | \code{\link[=make_logo]{make_logo()}}
23 | }
24 | \keyword{datasets}
25 |
--------------------------------------------------------------------------------
/tests/testthat/test-bar-factory.R:
--------------------------------------------------------------------------------
1 | # Test `BarFactory` class.
2 |
3 | test_that("'BarFactory' produces the correct instance types", {
4 | # Create a bar factory.
5 | bar_factory <- BarFactory$new()
6 |
7 | # Expect a basic bar.
8 | expect_equal(
9 | Helper$get_class_name(bar_factory$get("basic")),
10 | "BasicBar"
11 | )
12 |
13 | # Expect a modern bar.
14 | expect_equal(
15 | Helper$get_class_name(bar_factory$get("modern")),
16 | "ModernBar"
17 | )
18 |
19 | # Expect error for unsupported bar types.
20 | expect_error(
21 | bar_factory$get("unsupported"),
22 | as_text(Exception$feature_not_developed())
23 | )
24 | })
25 |
--------------------------------------------------------------------------------
/man-roxygen/make-logo.R:
--------------------------------------------------------------------------------
1 | #' @title
2 | #' Generate Package Logo
3 | #'
4 | #' @description
5 | #' This function is meant for generating or updating the logo. After running
6 | #' this procedure we end up with what is stored in the [`parabar::LOGO`]
7 | #' constant.
8 | #'
9 | #' @param template A character string representing the path to the logo
10 | #' template.
11 | #'
12 | #' @param version A numerical vector of three positive integers representing the
13 | #' version of the package to append to the logo.
14 | #'
15 | #' @return The ASCII logo.
16 | #'
17 | #' @examples
18 | #' \dontrun{
19 | #'
20 | #' # Generate the logo.
21 | #' logo <- make_logo()
22 | #'
23 | #' # Print the logo.
24 | #' cat(logo)
25 | #' }
26 | #'
27 | #' @seealso [`parabar::LOGO`]
28 |
--------------------------------------------------------------------------------
/tests/testthat/test-backend-factory.R:
--------------------------------------------------------------------------------
1 | # Test `BackendFactory` class.
2 |
3 | test_that("'BackendFactory' produces the correct instance types", {
4 | # Create a backend factory.
5 | backend_factory <- BackendFactory$new()
6 |
7 | # Expect a synchronous backend.
8 | expect_equal(
9 | Helper$get_class_name(backend_factory$get("sync")),
10 | "SyncBackend"
11 | )
12 |
13 | # Expect an asynchronous backend.
14 | expect_equal(
15 | Helper$get_class_name(backend_factory$get("async")),
16 | "AsyncBackend"
17 | )
18 |
19 | # Expect error for unsupported backend types.
20 | expect_error(
21 | backend_factory$get("unsupported"),
22 | as_text(Exception$feature_not_developed())
23 | )
24 | })
25 |
--------------------------------------------------------------------------------
/man-roxygen/get-option.R:
--------------------------------------------------------------------------------
1 | #' @description
2 | #' The [parabar::get_option()] function is a helper for retrieving the value of
3 | #' [`parabar::parabar`] [`options`][base::options()]. If the
4 | #' [`option`][`parabar::Options`] requested is not available in the session
5 | #' [`base::.Options`] list, the corresponding default value set by the
6 | #' [`parabar::Options`] [`R6::R6`] class is returned instead.
7 | #'
8 | #' @return
9 | #' The [parabar::get_option()] function returns the value of the requested
10 | #' [`option`][`parabar::Options`] present in the [`base::.Options`] list, or its
11 | #' corresponding default value (i.e., see [`parabar::Options`]). If the
12 | #' requested [`option`][`parabar::Options`] is not known, an error is thrown.
13 | #'
14 | #' @rdname option
15 |
--------------------------------------------------------------------------------
/tests/testthat/test-context-factory.R:
--------------------------------------------------------------------------------
1 | # Test `ContextFactory` class.
2 |
3 | test_that("'ContextFactory' produces the correct instance types", {
4 | # Create a context factory.
5 | context_factory <- ContextFactory$new()
6 |
7 | # Expect a regular context.
8 | expect_equal(
9 | Helper$get_class_name(context_factory$get("regular")),
10 | "Context"
11 | )
12 |
13 | # Expect a progress tracking context.
14 | expect_equal(
15 | Helper$get_class_name(context_factory$get("progress")),
16 | "ProgressTrackingContext"
17 | )
18 |
19 | # Expect error for unsupported context types.
20 | expect_error(
21 | context_factory$get("unsupported"),
22 | as_text(Exception$feature_not_developed())
23 | )
24 | })
25 |
--------------------------------------------------------------------------------
/man-roxygen/set-default-options.R:
--------------------------------------------------------------------------------
1 | #' @description
2 | #' The [parabar::set_default_options()] function is used to set the default
3 | #' [`options`][base::options()] values for the [`parabar::parabar`] package. The
4 | #' function is automatically called at package load and the entry created can be
5 | #' retrieved via [`getOption("parabar")`][base::getOption()]. Specific package
6 | #' [`options`][base::options()] can be retrieved using the helper function
7 | #' [parabar::get_option()].
8 | #'
9 | #' @return
10 | #' The [parabar::set_default_options()] function returns void. The
11 | #' [`options`][base::options()] set can be consulted via the [`base::.Options`]
12 | #' list. See the [`parabar::Options`] [`R6::R6`] class for more information on
13 | #' the default values set by this function.
14 | #'
15 | #' @rdname option
16 |
--------------------------------------------------------------------------------
/tests/testthat.R:
--------------------------------------------------------------------------------
1 | # This file is part of the standard setup for testthat.
2 | # It is recommended that you do not modify it.
3 | #
4 | # Where should you do additional test configuration?
5 | # Learn more about the roles of various files in:
6 | # * https://r-pkgs.org/tests.html
7 | # * https://testthat.r-lib.org/reference/test_package.html#special-files
8 |
9 | # Set environmental variable to prevent hang on quitting the `R` session.
10 | # Error message:
11 | # - `Error while shutting down parallel: unable to terminate some child processes`
12 | # See:
13 | # - https://github.com/r-lib/processx/issues/310
14 | # - https://github.com/r-lib/processx/issues/240
15 | # - https://github.com/r-lib/callr/issues/158
16 | Sys.setenv(PROCESSX_NOTIFY_OLD_SIGCHLD = "true")
17 |
18 | library(testthat)
19 | library(parabar)
20 |
21 | test_check("parabar")
22 |
--------------------------------------------------------------------------------
/man/Helper.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/Helper.R
3 | \name{Helper}
4 | \alias{Helper}
5 | \title{Package Helpers}
6 | \format{
7 | \describe{
8 | \item{\code{Helper$get_class_name(object)}}{Helper for getting the class of a given object.}
9 | \item{\code{Helper$is_of_class(object, class)}}{Check if an object is of a certain class.}
10 | \item{\code{Helper$get_option(option)}}{Get package option, or corresponding default value.}
11 | \item{\code{Helper$set_option(option, value)}}{Set package option.}
12 | \item{\code{Helper$check_object_type(object, expected_type)}}{Check the type of a given object.}
13 | \item{\code{Helper$check_array_margins(margins, dimensions)}}{Helper to check array margins for the \code{BackendService$apply} operation.}
14 | }
15 | }
16 | \description{
17 | This class contains static helper methods.
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/inst/assets/logo/parabar-logo.txt:
--------------------------------------------------------------------------------
1 | . . . . . . . . . . . . . . . . . . . . . . . . . . .
2 | . _ v{{major}}.{{minor}}.{{patch}} .
3 | . | | .
4 | . _ __ __ _ _ __ __ _ | |__ __ _ _ __ .
5 | . | '_ \ / _` || '__| / _` || '_ \ / _` || '__| .
6 | . | |_) || (_| || | | (_| || |_) || (_| || | .
7 | . | .__/ \____||_| \____||____/ \____||_| .
8 | . | | .
9 | . |_| .
10 | . .
11 | . . . . . . . . . . . . . . . . . . . . . . . . . . .
12 | . . .
13 | . https://parabar.mihaiconstantin.com .
14 | . . .
15 | . . . . . . . . . . . . . . . . . . . . . . . . . . .
16 |
--------------------------------------------------------------------------------
/tests/testthat/test-service.R:
--------------------------------------------------------------------------------
1 | # Test `BackendService` interface.
2 |
3 | test_that("'BackendService' interface cannot be instantiated", {
4 | # Create a mock object that has the `BackendService` class.
5 | assign("object", "Mock Backend Service", envir = environment())
6 |
7 | # Assign a class to the mock service object.
8 | class(object) <- "BackendService"
9 |
10 | # Expect an error upon instantiation.
11 | expect_error(
12 | BackendService$new(),
13 | as_text(Exception$abstract_class_not_instantiable(object))
14 | )
15 | })
16 |
17 |
18 | test_that("'BackendService' interface methods throw errors", {
19 | # Create an improper backend service implementation.
20 | service <- BackendServiceImplementation$new()
21 |
22 | # Expect unimplemented backend service methods to throw errors.
23 | tests_set_for_unimplemented_service_methods(service)
24 | })
25 |
--------------------------------------------------------------------------------
/man/Warning.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/Warning.R
3 | \name{Warning}
4 | \alias{Warning}
5 | \title{Package Warnings}
6 | \format{
7 | \describe{
8 | \item{\code{Warning$requested_cluster_cores_too_low()}}{Warning for not requesting enough cluster cores.}
9 | \item{\code{Warning$requested_cluster_cores_too_high()}}{Warning for requesting too many cluster cores.}
10 | \item{\code{Warning$requested_cluster_type_not_supported()}}{Warning for requesting an unsupported cluster type.}
11 | \item{\code{Warning$progress_not_supported_for_backend()}}{Warning for using a backend incompatible with progress tracking.}
12 | \item{\code{Warning$error_in_backend_finalizer()}}{Warning for errors in the backend finalizer during garbage collection.}
13 | }
14 | }
15 | \description{
16 | This class contains static methods for throwing warnings with informative
17 | messages.
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/man/make_logo.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/logo.R
3 | \name{make_logo}
4 | \alias{make_logo}
5 | \title{Generate Package Logo}
6 | \usage{
7 | make_logo(
8 | template = "./inst/assets/logo/parabar-logo.txt",
9 | version = c(1, 0, 0)
10 | )
11 | }
12 | \arguments{
13 | \item{template}{A character string representing the path to the logo
14 | template.}
15 |
16 | \item{version}{A numerical vector of three positive integers representing the
17 | version of the package to append to the logo.}
18 | }
19 | \value{
20 | The ASCII logo.
21 | }
22 | \description{
23 | This function is meant for generating or updating the logo. After running
24 | this procedure we end up with what is stored in the \code{\link{LOGO}}
25 | constant.
26 | }
27 | \examples{
28 | \dontrun{
29 |
30 | # Generate the logo.
31 | logo <- make_logo()
32 |
33 | # Print the logo.
34 | cat(logo)
35 | }
36 |
37 | }
38 | \seealso{
39 | \code{\link{LOGO}}
40 | }
41 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # MIT License
2 |
3 | Copyright (c) 2022 Mihai A. Constantin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/man-roxygen/clear.R:
--------------------------------------------------------------------------------
1 | #' @title
2 | #' Clear a Backend
3 | #'
4 | #' @description
5 | #' This function can be used to clear a [`backend`][`parabar::Backend`] created
6 | #' by [parabar::start_backend()].
7 | #'
8 | #' @param backend An object of class [`parabar::Backend`] as returned by the
9 | #' [parabar::start_backend()] function.
10 | #'
11 | #' @details
12 | #' This function is a convenience wrapper around the lower-lever API of
13 | #' [`parabar::parabar`] aimed at developers. More specifically, this function
14 | #' calls the [`clear`][`parabar::BackendService`] method on the provided
15 | #' [`backend`][`parabar::Backend`] instance.
16 | #'
17 | #' @return
18 | #' The function returns void. It throws an error if the value provided for the
19 | #' `backend` argument is not an instance of class [`parabar::Backend`].
20 | #'
21 | #' @inherit start_backend examples
22 | #'
23 | #' @seealso
24 | #' [parabar::start_backend()], [parabar::peek()], [parabar::export()],
25 | #' [parabar::evaluate()], [parabar::configure_bar()], [parabar::par_sapply()],
26 | #' [parabar::par_lapply()], [parabar::par_apply()], [parabar::stop_backend()],
27 | #' and [`parabar::BackendService`].
28 |
--------------------------------------------------------------------------------
/_pkgdown.yml:
--------------------------------------------------------------------------------
1 | url: http://parabar.mihaiconstantin.com/
2 | template:
3 | bootstrap: 5
4 | bootswatch: flatly
5 | reference:
6 | - subtitle: Main Functions
7 | desc: Functions intended for use in interactive `R` sessions.
8 | - contents:
9 | - start_backend
10 | - stop_backend
11 | - export
12 | - peek
13 | - clear
14 | - evaluate
15 | - par_sapply
16 | - par_lapply
17 | - par_apply
18 | - configure_bar
19 | - matches("get_|set_")
20 | - subtitle: Developer Classes
21 | desc: API based on the `R6` class system for package developers.
22 | - contents:
23 | - BackendService
24 | - Backend
25 | - SyncBackend
26 | - AsyncBackend
27 | - BackendFactory
28 | - Specification
29 | - Context
30 | - ProgressTrackingContext
31 | - ContextFactory
32 | - SessionState
33 | - TaskState
34 | - Bar
35 | - BasicBar
36 | - ModernBar
37 | - BarFactory
38 | - Options
39 | - Helper
40 | - Warning
41 | - Exception
42 | - UserApiConsumer
43 | - subtitle: Miscellaneous
44 | desc: Very specific functions that may safely be ignored.
45 | - contents:
46 | - make_logo
47 | - LOGO
48 | authors:
49 | Mihai Constantin:
50 | href: "https://mihaiconstantin.com"
51 |
--------------------------------------------------------------------------------
/man-roxygen/stop-backend.R:
--------------------------------------------------------------------------------
1 | #' @title
2 | #' Stop a Backend
3 | #'
4 | #' @description
5 | #' This function can be used to stop a [`backend`][`parabar::Backend`] created
6 | #' by [parabar::start_backend()].
7 | #'
8 | #' @param backend An object of class [`parabar::Backend`] as returned by the
9 | #' [parabar::start_backend()] function.
10 | #'
11 | #' @details
12 | #' This function is a convenience wrapper around the lower-lever API of
13 | #' [`parabar::parabar`] aimed at developers. More specifically, this function
14 | #' calls the [`stop`][`parabar::BackendService`] method on the provided
15 | #' [`backend`][`parabar::Backend`] instance.
16 | #'
17 | #' @return
18 | #' The function returns void. It throws an error if:
19 | #' - the value provided for the `backend` argument is not an instance of class
20 | #' [`parabar::Backend`].
21 | #' - the [`backend`][`parabar::Backend`] object provided is already stopped
22 | #' (i.e., is not active).
23 | #'
24 | #' @inherit start_backend examples
25 | #'
26 | #' @seealso
27 | #' [parabar::start_backend()], [parabar::peek()], [parabar::export()],
28 | #' [parabar::evaluate()], [parabar::clear()], [parabar::configure_bar()],
29 | #' [parabar::par_sapply()], [parabar::par_apply()], [parabar::par_lapply()], and
30 | #' [`parabar::BackendService`].
31 |
--------------------------------------------------------------------------------
/man-roxygen/evaluate.R:
--------------------------------------------------------------------------------
1 | #' @title
2 | #' Evaluate An Expression On The Backend
3 | #'
4 | #' @description
5 | #' This function can be used to evaluate an arbitrary [base::expression()] a
6 | #' [`backend`][`parabar::Backend`] created by [parabar::start_backend()].
7 | #'
8 | #' @param backend An object of class [`parabar::Backend`] as returned by the
9 | #' [parabar::start_backend()] function.
10 | #'
11 | #' @param expression An unquoted expression to evaluate on the backend.
12 | #'
13 | #' @details
14 | #' This function is a convenience wrapper around the lower-lever API of
15 | #' [`parabar::parabar`] aimed at developers. More specifically, this function
16 | #' calls the [`evaluate`][`parabar::BackendService`] method on the provided
17 | #' [`backend`][`parabar::Backend`] instance.
18 | #'
19 | #' @return
20 | #' This method returns the result of the expression evaluation. It throws an
21 | #' error if the value provided for the `backend` argument is not an instance of
22 | #' class [`parabar::Backend`].
23 | #'
24 | #' @inherit start_backend examples
25 | #'
26 | #' @seealso
27 | #' [parabar::start_backend()], [parabar::peek()], [parabar::export()],
28 | #' [parabar::clear()], [parabar::configure_bar()], [parabar::par_sapply()],
29 | #' [parabar::par_lapply()], [parabar::par_apply()], [parabar::stop_backend()],
30 | #' and [`parabar::BackendService`].
31 |
--------------------------------------------------------------------------------
/tests/testthat/test-bar.R:
--------------------------------------------------------------------------------
1 | # Test `Bar` class.
2 |
3 | test_that("'Bar' class cannot be instantiated", {
4 | # Create a mock object that has the `Service` class.
5 | assign("object", "Mock Bar", envir = environment())
6 |
7 | # Assign a class to the mock service object.
8 | class(object) <- "Bar"
9 |
10 | # Expect an error upon instantiation.
11 | expect_error(Bar$new(), as_text(Exception$abstract_class_not_instantiable(object)))
12 | })
13 |
14 |
15 | test_that("'Bar' abstract class methods throw errors", {
16 | # Create an improper bar implementation.
17 | bar <- BarImplementation$new()
18 |
19 | # Expect an error when calling the `create` method.
20 | expect_error(bar$create(), as_text(Exception$method_not_implemented()))
21 |
22 | # Expect an error when calling the `update` method.
23 | expect_error(bar$update(), as_text(Exception$method_not_implemented()))
24 |
25 | # Expect an error when calling the `terminate` method.
26 | expect_error(bar$terminate(), as_text(Exception$method_not_implemented()))
27 | })
28 |
29 |
30 | test_that("'Bar' abstract class fields have correct default values", {
31 | # Create an improper bar implementation.
32 | bar <- BarImplementation$new()
33 |
34 | # Expect the correct default value for the `engine` field.
35 | expect_null(bar$engine)
36 | })
37 |
--------------------------------------------------------------------------------
/man-roxygen/peek.R:
--------------------------------------------------------------------------------
1 | #' @title
2 | #' Inspect a Backend
3 | #'
4 | #' @description
5 | #' This function can be used to check the names of the variables present on a
6 | #' [`backend`][`parabar::Backend`] created by [parabar::start_backend()].
7 | #'
8 | #' @param backend An object of class [`parabar::Backend`] as returned by the
9 | #' [parabar::start_backend()] function.
10 | #'
11 | #' @details
12 | #' This function is a convenience wrapper around the lower-lever API of
13 | #' [`parabar::parabar`] aimed at developers. More specifically, this function
14 | #' calls the [`peek`][`parabar::BackendService`] method on the provided
15 | #' [`backend`][`parabar::Backend`] instance.
16 | #'
17 | #' @return
18 | #' The function returns a list of character vectors, where each list element
19 | #' corresponds to a node, and each element of the character vector is the name
20 | #' of a variable present on that node. It throws an error if the value provided
21 | #' for the `backend` argument is not an instance of class [`parabar::Backend`].
22 | #'
23 | #' @inherit start_backend examples
24 | #'
25 | #' @seealso
26 | #' [parabar::start_backend()], [parabar::export()], [parabar::evaluate()],
27 | #' [parabar::clear()], [parabar::configure_bar()], [parabar::par_sapply()],
28 | #' [parabar::par_lapply()], [parabar::par_apply()], [parabar::stop_backend()],
29 | #' and [`parabar::BackendService`].
30 |
--------------------------------------------------------------------------------
/tests/testthat/test-backend.R:
--------------------------------------------------------------------------------
1 | # Test `Backend` class.
2 |
3 | test_that("'Backend' class cannot be instantiated", {
4 | # Create a mock object that has the `Service` class.
5 | assign("object", "Mock Backend", envir = environment())
6 |
7 | # Assign a class to the mock service object.
8 | class(object) <- "Backend"
9 |
10 | # Expect an error upon instantiation.
11 | expect_error(
12 | Backend$new(),
13 | as_text(Exception$abstract_class_not_instantiable(object))
14 | )
15 | })
16 |
17 |
18 | test_that("'Backend' abstract class methods throw errors", {
19 | # Create an improper backend implementation.
20 | backend <- BackendImplementation$new()
21 |
22 | # Expect unimplemented service methods to throw errors.
23 | tests_set_for_unimplemented_service_methods(backend)
24 | })
25 |
26 |
27 | test_that("'Backend' abstract class fields have correct default values", {
28 | # Create an improper backend implementation.
29 | backend <- BackendImplementation$new()
30 |
31 | # Expect the correct default value for the `cluster` field.
32 | expect_null(backend$cluster)
33 |
34 | # Expect the correct default value for the `supports_progress` field.
35 | expect_false(backend$supports_progress)
36 |
37 | # Expect the correct default value for the `active` field.
38 | expect_false(backend$active)
39 | })
40 |
--------------------------------------------------------------------------------
/NAMESPACE:
--------------------------------------------------------------------------------
1 | # Generated by roxygen2: do not edit by hand
2 |
3 | S3method(print,parabar)
4 | export(AsyncBackend)
5 | export(Backend)
6 | export(BackendFactory)
7 | export(BackendService)
8 | export(Bar)
9 | export(BarFactory)
10 | export(BasicBar)
11 | export(Context)
12 | export(ContextFactory)
13 | export(Exception)
14 | export(Helper)
15 | export(LOGO)
16 | export(ModernBar)
17 | export(Options)
18 | export(ProgressTrackingContext)
19 | export(SessionState)
20 | export(Specification)
21 | export(SyncBackend)
22 | export(TaskState)
23 | export(UserApiConsumer)
24 | export(Warning)
25 | export(clear)
26 | export(configure_bar)
27 | export(evaluate)
28 | export(export)
29 | export(get_option)
30 | export(make_logo)
31 | export(par_apply)
32 | export(par_lapply)
33 | export(par_sapply)
34 | export(peek)
35 | export(set_default_options)
36 | export(set_option)
37 | export(start_backend)
38 | export(stop_backend)
39 | importFrom(R6,R6Class)
40 | importFrom(callr,r_session)
41 | importFrom(filelock,lock)
42 | importFrom(filelock,unlock)
43 | importFrom(parallel,clusterCall)
44 | importFrom(parallel,clusterEvalQ)
45 | importFrom(parallel,clusterExport)
46 | importFrom(parallel,detectCores)
47 | importFrom(parallel,makeCluster)
48 | importFrom(parallel,parApply)
49 | importFrom(parallel,parLapply)
50 | importFrom(parallel,parSapply)
51 | importFrom(parallel,stopCluster)
52 | importFrom(progress,progress_bar)
53 |
--------------------------------------------------------------------------------
/man-roxygen/export.R:
--------------------------------------------------------------------------------
1 | #' @title
2 | #' Export Objects To a Backend
3 | #'
4 | #' @description
5 | #' This function can be used to export objects to a
6 | #' [`backend`][`parabar::Backend`] created by [parabar::start_backend()].
7 | #'
8 | #' @param backend An object of class [`parabar::Backend`] as returned by the
9 | #' [parabar::start_backend()] function.
10 | #'
11 | #' @param variables A character vector of variable names to export to the
12 | #' backend.
13 | #'
14 | #' @param environment An environment from which to export the variables. If no
15 | #' environment is provided, the `.GlobalEnv` environment is used.
16 | #'
17 | #' @details
18 | #' This function is a convenience wrapper around the lower-lever API of
19 | #' [`parabar::parabar`] aimed at developers. More specifically, this function
20 | #' calls the [`export`][`parabar::BackendService`] method on the provided
21 | #' [`backend`][`parabar::Backend`] instance.
22 | #'
23 | #' @return
24 | #' The function returns void. It throws an error if the value provided for the
25 | #' `backend` argument is not an instance of class [`parabar::Backend`].
26 | #'
27 | #' @inherit start_backend examples
28 | #'
29 | #' @seealso
30 | #' [parabar::start_backend()], [parabar::peek()], [parabar::evaluate()],
31 | #' [parabar::clear()], [parabar::configure_bar()], [parabar::par_sapply()],
32 | #' [parabar::par_lapply()], [parabar::par_apply()], [parabar::stop_backend()],
33 | #' and [`parabar::BackendService`].
34 |
--------------------------------------------------------------------------------
/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Package: parabar
2 | Title: Progress Bar for Parallel Tasks
3 | Version: 1.4.2
4 | Authors@R:
5 | person(given = "Mihai",
6 | family = "Constantin",
7 | role = c("aut", "cre"),
8 | email = "mihai@mihaiconstantin.com",
9 | comment = c(ORCID = "0000-0002-6460-0107"))
10 | Description: A simple interface in the form of R6 classes for executing tasks in
11 | parallel, tracking their progress, and displaying accurate progress bars.
12 | License: MIT + file LICENSE
13 | URL: https://parabar.mihaiconstantin.com
14 | BugReports: https://github.com/mihaiconstantin/parabar/issues
15 | Imports:
16 | R6,
17 | progress,
18 | parallel,
19 | callr,
20 | filelock,
21 | utils
22 | Encoding: UTF-8
23 | Roxygen: list(markdown = TRUE, r6 = TRUE)
24 | RoxygenNote: 7.3.1
25 | Collate:
26 | 'TaskState.R'
27 | 'Options.R'
28 | 'Helper.R'
29 | 'Exception.R'
30 | 'Specification.R'
31 | 'BackendService.R'
32 | 'Backend.R'
33 | 'AsyncBackend.R'
34 | 'SyncBackend.R'
35 | 'BackendFactory.R'
36 | 'Bar.R'
37 | 'ModernBar.R'
38 | 'BasicBar.R'
39 | 'BarFactory.R'
40 | 'Context.R'
41 | 'ProgressTrackingContext.R'
42 | 'ContextFactory.R'
43 | 'SessionState.R'
44 | 'Warning.R'
45 | 'UserApiConsumer.R'
46 | 'exports.R'
47 | 'logo.R'
48 | 'parabar-package.R'
49 | Suggests:
50 | knitr,
51 | rmarkdown,
52 | testthat (>= 3.0.0)
53 | Config/testthat/edition: 3
54 | VignetteBuilder: knitr
55 |
--------------------------------------------------------------------------------
/tests/testthat/test-logo.R:
--------------------------------------------------------------------------------
1 | # Test for logo-related objects.
2 |
3 | test_that("'make_logo' function correctly sets the package version", {
4 | # Create a temporary file.
5 | path <- tempfile("parabar-logo")
6 |
7 | # Create a mock logo template.
8 | mock_logo_template <- c("parabar {{major}}.{{minor}}.{{patch}}")
9 |
10 | # Write mock logo template to the temporary file.
11 | writeLines(mock_logo_template, path)
12 |
13 | # Remove the file on exit.
14 | on.exit({
15 | # Remove.
16 | unlink(path)
17 | })
18 |
19 | # Expect that the logo is produced with the correct version.
20 | expect_equal(
21 | make_logo(template = path, version = c(1, 2, 3)),
22 | c("parabar 1.2.3")
23 | )
24 |
25 | # Expect that the logo version can also handle character.
26 | expect_equal(
27 | make_logo(template = path, version = c(1, "x", "x")),
28 | c("parabar 1.x.x")
29 | )
30 | })
31 |
32 |
33 | test_that("'LOGO' constant exists and is of correct type", {
34 | # Expect that the `LOGO` constant is not null.
35 | expect_false(is.null(LOGO))
36 |
37 | # Expect that the `LOGO` constant has the correct type.
38 | expect_true(is(LOGO, "parabar"))
39 | })
40 |
41 |
42 | test_that("'LOGO' is printed correctly", {
43 | # Capture the output of the `S3` method.
44 | output <- capture.output(print(LOGO))
45 |
46 | # Expect the logo to contain a given package version.
47 | expect_true(grepl("v\\d\\.x\\.x", output[2], perl = TRUE))
48 | })
49 |
--------------------------------------------------------------------------------
/man-roxygen/set-option.R:
--------------------------------------------------------------------------------
1 | #' @title
2 | #' Get or Set Package Option
3 | #'
4 | #' @description
5 | #' The [parabar::set_option()] function is a helper for setting
6 | #' [`parabar::parabar`] [`options`][base::options()]. The function adjusts the
7 | #' fields of the [`parabar::Options`] instance stored in the [`base::.Options`]
8 | #' list. If no [`parabar::Options`] instance is present in the
9 | #' [`base::.Options`] list, a new one is created.
10 | #'
11 | #' @param option A character string representing the name of the option to
12 | #' retrieve or adjust. See the public fields of [`R6::R6`] class
13 | #' [`parabar::Options`] for the list of available [`parabar::parabar`]
14 | #' [`options`][base::options()].
15 | #'
16 | #' @param value The value to set the [`option`][`parabar::Options`] to.
17 | #'
18 | #' @return
19 | #' The [parabar::set_option()] function returns void. It throws an error if the
20 | #' requested [`option`][`parabar::Options`] to be adjusted is not known.
21 | #'
22 | #' @examples
23 | #' # Get the status of progress tracking.
24 | #' get_option("progress_track")
25 | #'
26 | #' # Set the status of progress tracking to `FALSE`.
27 | #' set_option("progress_track", FALSE)
28 | #'
29 | #' # Get the status of progress tracking again.
30 | #' get_option("progress_track")
31 | #'
32 | #' # Restore default options.
33 | #' set_default_options()
34 | #'
35 | #' # Get the status of progress tracking yet again.
36 | #' get_option("progress_track")
37 | #'
38 | #' @seealso
39 | #' [`parabar::Options`], [parabar::set_default_options()], [base::options()],
40 | #' and [base::getOption()].
41 | #'
42 | #' @rdname option
43 |
--------------------------------------------------------------------------------
/R/parabar-package.R:
--------------------------------------------------------------------------------
1 | # # # # # # # # # # # # # # # # # # # # # # # # # # # #
2 | # _ #
3 | # | | #
4 | # _ __ __ _ _ __ __ _ | |__ __ _ _ __ #
5 | # | '_ \ / _` || '__| / _` || '_ \ / _` || '__| #
6 | # | |_) || (_| || | | (_| || |_) || (_| || | #
7 | # | .__/ \____||_| \____||____/ \____||_| #
8 | # | | #
9 | # |_| #
10 | # #
11 | # # # # # # # # # # # # # # # # # # # # # # # # # # # #
12 | # Author: Mihai A. Constantin #
13 | # Documentation: https://parabar.mihaiconstantin.com #
14 | # Contact: mihai@mihaiconstantin.com #
15 | # # # # # # # # # # # # # # # # # # # # # # # # # # # #
16 |
17 |
18 | # Imports.
19 | #' @importFrom parallel detectCores makeCluster stopCluster clusterExport
20 | #' @importFrom parallel clusterEvalQ parSapply parLapply parApply clusterCall
21 | #' @importFrom R6 R6Class
22 | #' @importFrom progress progress_bar
23 | #' @importFrom callr r_session
24 | #' @importFrom filelock lock unlock
25 |
26 | #' @template parabar
27 | "_PACKAGE"
28 |
29 |
30 | # On package load.
31 | .onLoad <- function(libname, pkgname) {
32 | # Set package options.
33 | set_default_options()
34 | }
35 |
36 |
37 | # On package attach.
38 | .onAttach <- function(libname, pkgname) {
39 | # If there this is an interactive session.
40 | if (interactive()) {
41 | # Print package information.
42 | packageStartupMessage(LOGO)
43 | }
44 | }
45 |
46 |
47 | # On package unload.
48 | .onUnload <- function(libpath) {
49 | # Remove package options.
50 | options(parabar = NULL)
51 | }
52 |
--------------------------------------------------------------------------------
/.github/workflows/pkgdown.yaml:
--------------------------------------------------------------------------------
1 | # `pkgdown` workflow.
2 | name: pkgdown
3 |
4 | # Set workflow permissions.
5 | permissions: read-all
6 |
7 | # Events.
8 | on:
9 | push:
10 | branches:
11 | - main
12 | pull_request:
13 | branches:
14 | - main
15 | release:
16 | types: [published]
17 | # Trigger manually via the GitHub UI.
18 | workflow_dispatch:
19 |
20 | # Jobs to run.
21 | jobs:
22 |
23 | # `pkgdown` job.
24 | pkgdown:
25 | runs-on: ubuntu-latest
26 | concurrency:
27 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }}
28 | env:
29 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
30 | permissions:
31 | contents: write
32 |
33 | # Actions.
34 | steps:
35 | # Checkout.
36 | - name: Checkout repository
37 | uses: actions/checkout@v4
38 |
39 | # Setup `pandoc`.
40 | - name: Setup pandoc
41 | uses: r-lib/actions/setup-pandoc@v2
42 |
43 | # Setup `R`.
44 | - name: Setup R
45 | uses: r-lib/actions/setup-r@v2
46 | with:
47 | use-public-rspm: true
48 |
49 | # Setup package dependencies and extras.
50 | - name: Install dependencies
51 | uses: r-lib/actions/setup-r-dependencies@v2
52 | with:
53 | extra-packages: any::pkgdown, local::.
54 | needs: website
55 |
56 | # Build the site.
57 | - name: Build site
58 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)
59 | shell: Rscript {0}
60 |
61 | # Deploy to GitHub pages.
62 | - name: Deploy to GitHub pages 🚀
63 | if: github.event_name != 'pull_request'
64 | uses: JamesIves/github-pages-deploy-action@v4.5.0
65 | with:
66 | clean: false
67 | branch: gh-pages
68 | folder: docs
69 |
--------------------------------------------------------------------------------
/R/BarFactory.R:
--------------------------------------------------------------------------------
1 | #' @include Exception.R BasicBar.R ModernBar.R
2 |
3 | #' @title
4 | #' BackendFactory
5 | #'
6 | #' @description
7 | #' This class is a factory that provides concrete implementations of the
8 | #' [`parabar::Bar`] abstract class.
9 | #'
10 | #' @examples
11 | #' # Create a bar factory.
12 | #' bar_factory <- BarFactory$new()
13 | #'
14 | #' # Get a modern bar instance.
15 | #' bar <- bar_factory$get("modern")
16 | #'
17 | #' # Check the class of the bar instance.
18 | #' class(bar)
19 | #'
20 | #' # Get a basic bar instance.
21 | #' bar <- bar_factory$get("basic")
22 | #'
23 | #' # Check the class of the bar instance.
24 | #' class(bar)
25 | #'
26 | #' @seealso
27 | #' [`parabar::Bar`], [`parabar::BasicBar`], and [`parabar::ModernBar`].
28 | #'
29 | #' @export
30 | BarFactory <- R6::R6Class("BarFactory",
31 | public = list(
32 | #' @description
33 | #' Obtain a concrete implementation of the abstract [`parabar::Bar`]
34 | #' class of the specified type.
35 | #'
36 | #' @param type A character string specifying the type of the
37 | #' [`parabar::Bar`] to instantiate. Possible values are `"modern"` and
38 | #' `"basic"`. See the **Details** section for more information.
39 | #'
40 | #' @details
41 | #' When `type = "modern"` a [`parabar::ModernBar`] instance is created
42 | #' and returned. When `type = "basic"` a [`parabar::BasicBar`] instance
43 | #' is provided instead.
44 | #'
45 | #' @return
46 | #' A concrete implementation of the class [`parabar::Bar`]. It throws an
47 | #' error if the requested bar `type` is not supported.
48 | get = function(type) {
49 | return(
50 | switch(type,
51 | basic = BasicBar$new(),
52 | modern = ModernBar$new(),
53 | Exception$feature_not_developed()
54 | )
55 | )
56 | }
57 | )
58 | )
59 |
--------------------------------------------------------------------------------
/R/ContextFactory.R:
--------------------------------------------------------------------------------
1 | #' @include Exception.R Context.R ProgressTrackingContext.R
2 |
3 | #' @title ContextFactory
4 | #'
5 | #' @description
6 | #' This class is a factory that provides instances of the [`parabar::Context`]
7 | #' class.
8 | #'
9 | #' @examples
10 | #' # Create a context factory.
11 | #' context_factory <- ContextFactory$new()
12 | #'
13 | #' # Get a regular context instance.
14 | #' context <- context_factory$get("regular")
15 | #'
16 | #' # Check the class of the context instance.
17 | #' class(context)
18 | #'
19 | #' # Get a progress context instance.
20 | #' context <- context_factory$get("progress")
21 | #' class(context)
22 | #'
23 | #' @seealso
24 | #' [`parabar::Context`], [`parabar::ProgressTrackingContext`],
25 | #' [`parabar::BackendService`], and [`parabar::Backend`]
26 | #'
27 | #' @export
28 | ContextFactory <- R6::R6Class("ContextFactory",
29 | public = list(
30 | #' @description
31 | #' Obtain instances of the [`parabar::Context`] class.
32 | #'
33 | #' @param type A character string specifying the type of the
34 | #' [`parabar::Context`] to instantiate. Possible values are `"regular"`
35 | #' and `"progress"`. See the **Details** section for more information.
36 | #'
37 | #' @details
38 | #' When `type = "regular"` a [`parabar::Context`] instance is created
39 | #' and returned. When `type = "progress"` a
40 | #' [`parabar::ProgressTrackingContext`] instance is provided instead.
41 | #'
42 | #' @return
43 | #' An object of type [`parabar::Context`]. It throws an error if the
44 | #' requested context `type` is not supported.
45 | get = function(type) {
46 | return(
47 | switch(type,
48 | regular = Context$new(),
49 | progress = ProgressTrackingContext$new(),
50 | Exception$feature_not_developed()
51 | )
52 | )
53 | }
54 | )
55 | )
56 |
--------------------------------------------------------------------------------
/tests/testthat/test-options.R:
--------------------------------------------------------------------------------
1 | # Test `Options` class.
2 |
3 | test_that("'Options' class fields work correctly", {
4 | # Create an instance of the Options class.
5 | options <- Options$new()
6 |
7 | # Expect that the public fields have the correct defaults.
8 | expect_true(options$progress_track)
9 | expect_equal(options$progress_timeout, 0.001)
10 | expect_equal(options$progress_bar_type, "modern")
11 | expect_equal(options$stop_forceful, FALSE)
12 |
13 | # Expect the correct default config for the modern progress bar.
14 | expect_equal(
15 | options$progress_bar_config$modern,
16 | list(
17 | show_after = 0,
18 | format = " > completed :current out of :total tasks [:percent] [:elapsed]"
19 | )
20 | )
21 |
22 | # Expect the correct default config for the basic progress bar.
23 | expect_equal(
24 | options$progress_bar_config$basic,
25 | list()
26 | )
27 |
28 | # Create temporary paths for the progress log.
29 | log_path_1 <- options$progress_log_path
30 | log_path_2 <- options$progress_log_path
31 |
32 | # Expect the temporary paths to be different.
33 | expect_false(log_path_1 == log_path_2)
34 |
35 | # Set a custom path for the progress log.
36 | log_path_custom <- "custom_path.log"
37 |
38 | # Set the path on the options instance.
39 | options$progress_log_path <- log_path_custom
40 |
41 | # Expect that the log path is not fixed to the custom one.
42 | expect_equal(options$progress_log_path, log_path_custom)
43 |
44 | # Expect that subsequent calls to the log path yield the fixed path.
45 | expect_equal(options$progress_log_path, log_path_custom)
46 |
47 | # Reset the progress_log_path to default (i.e., enabling temporary paths).
48 | options$progress_log_path <- NULL
49 |
50 | # Generate a temporary log path.
51 | log_path_3 <- options$progress_log_path
52 |
53 | # Expect the log path is a temporary one again.
54 | expect_false(log_path_3 == log_path_custom)
55 | })
56 |
--------------------------------------------------------------------------------
/.github/workflows/R-CMD-check.yaml:
--------------------------------------------------------------------------------
1 | # R CMD check workflow.
2 | name: R-CMD-check
3 |
4 | # Set workflow permissions.
5 | permissions: read-all
6 |
7 | # Run on pushes and pull requests to the `main` branch.
8 | on:
9 | push:
10 | branches:
11 | - main
12 | pull_request:
13 | branches:
14 | - main
15 | # Trigger manually via the GitHub UI.
16 | workflow_dispatch:
17 |
18 | # Jobs to run.
19 | jobs:
20 |
21 | # CRAN check job
22 | R-CMD-check:
23 | name: ${{ matrix.config.os }} (${{ matrix.config.r }})
24 | runs-on: ${{ matrix.config.os }}
25 | strategy:
26 | fail-fast: false
27 | matrix:
28 | config:
29 | - {os: macos-latest, r: 'release'}
30 | - {os: windows-latest, r: 'release'}
31 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'}
32 | - {os: ubuntu-latest, r: 'release'}
33 | - {os: ubuntu-latest, r: 'oldrel-1'}
34 | env:
35 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
36 | R_KEEP_PKG_SOURCE: yes
37 |
38 | # Actions.
39 | steps:
40 | # Checkout the repository.
41 | - name: Checkout repository
42 | uses: actions/checkout@v4
43 |
44 | # Setup `pandoc`.
45 | - name: Setup pandoc
46 | uses: r-lib/actions/setup-pandoc@v2
47 |
48 | # Setup `R`.
49 | - name: Setup R
50 | uses: r-lib/actions/setup-r@v2
51 | with:
52 | r-version: ${{ matrix.config.r }}
53 | http-user-agent: ${{ matrix.config.http-user-agent }}
54 | use-public-rspm: true
55 |
56 | # Setup package dependencies and extras.
57 | - name: Install dependencies
58 | uses: r-lib/actions/setup-r-dependencies@v2
59 | with:
60 | extra-packages: any::rcmdcheck
61 | needs: check
62 |
63 | # Perform the CRAN check.
64 | - name: Check package
65 | uses: r-lib/actions/check-r-package@v2
66 | with:
67 | upload-snapshots: true
68 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")'
69 |
--------------------------------------------------------------------------------
/tests/testthat/test-sync-backend.R:
--------------------------------------------------------------------------------
1 | # Test `SyncBackend` class.
2 |
3 | test_that("'SyncBackend' creates and manages clusters correctly", {
4 | # Create a specification.
5 | specification <- Specification$new()
6 |
7 | # Set the number of cores.
8 | specification$set_cores(cores = 2)
9 |
10 | # Pick a cluster type.
11 | cluster_type <- pick_cluster_type(specification$types)
12 |
13 | # Set the cluster type.
14 | specification$set_type(type = cluster_type)
15 |
16 | # Create a synchronous backend object.
17 | backend <- SyncBackend$new()
18 |
19 | # Expect the backend to not support progress tracking.
20 | expect_false(backend$supports_progress)
21 |
22 | # Start the cluster on the backend.
23 | backend$start(specification)
24 |
25 | # Expect the cluster to be an object of `parallel` class.
26 | expect_true(is(backend$cluster, "cluster"))
27 |
28 | # Expect that the cluster is of correct size.
29 | expect_equal(length(backend$cluster), specification$cores)
30 |
31 | # Expect that the cluster is of correct type.
32 | switch(cluster_type,
33 | "psock" = expect_true(all(tolower(summary(backend$cluster)[, 2]) == "socknode")),
34 | "fork" = expect_true(all(tolower(summary(backend$cluster)[, 2]) == "forknode"))
35 | )
36 |
37 | # Test backend states.
38 | tests_set_for_backend_states(backend, specification)
39 | })
40 |
41 |
42 | test_that("'SyncBackend' performs operations on the cluster correctly", {
43 | # Create a specification.
44 | specification <- Specification$new()
45 |
46 | # Set the number of cores.
47 | specification$set_cores(cores = 2)
48 |
49 | # Determine the cluster type.
50 | cluster_type <- pick_cluster_type(specification$types)
51 |
52 | # Set the cluster type.
53 | specification$set_type(type = cluster_type)
54 |
55 | # Create a synchronous backend object.
56 | backend <- SyncBackend$new()
57 |
58 | # Perform tests for synchronous backend operations.
59 | tests_set_for_synchronous_backend_operations(backend, specification, test_task)
60 | })
61 |
--------------------------------------------------------------------------------
/R/BackendFactory.R:
--------------------------------------------------------------------------------
1 | #' @include Exception.R SyncBackend.R AsyncBackend.R
2 |
3 | #' @title BackendFactory
4 | #'
5 | #' @description
6 | #' This class is a factory that provides concrete implementations of the
7 | #' [`parabar::Backend`] abstract class.
8 | #'
9 | #' @examples
10 | #' # Create a backend factory.
11 | #' backend_factory <- BackendFactory$new()
12 | #'
13 | #' # Get a synchronous backend instance.
14 | #' backend <- backend_factory$get("sync")
15 | #'
16 | #' # Check the class of the backend instance.
17 | #' class(backend)
18 | #'
19 | #' # Get an asynchronous backend instance.
20 | #' backend <- backend_factory$get("async")
21 | #'
22 | #' # Check the class of the backend instance.
23 | #' class(backend)
24 | #'
25 | #' @seealso
26 | #' [`parabar::BackendService`], [`parabar::Backend`], [`parabar::SyncBackend`],
27 | #' [`parabar::AsyncBackend`], and [`parabar::ContextFactory`].
28 | #'
29 | #' @export
30 | BackendFactory <- R6::R6Class("BackendFactory",
31 | public = list(
32 | #' @description
33 | #' Obtain a concrete implementation of the abstract [`parabar::Backend`]
34 | #' class of the specified type.
35 | #'
36 | #' @param type A character string specifying the type of the
37 | #' [`parabar::Backend`] to instantiate. Possible values are `"sync"` and
38 | #' `"async"`. See the **Details** section for more information.
39 | #'
40 | #' @details
41 | #' When `type = "sync"` a [`parabar::SyncBackend`] instance is created
42 | #' and returned. When `type = "async"` an [`parabar::AsyncBackend`]
43 | #' instance is provided instead.
44 | #'
45 | #' @return
46 | #' A concrete implementation of the class [`parabar::Backend`]. It
47 | #' throws an error if the requested backend `type` is not supported.
48 | get = function(type) {
49 | return(
50 | switch(type,
51 | sync = SyncBackend$new(),
52 | async = AsyncBackend$new(),
53 | Exception$feature_not_developed()
54 | )
55 | )
56 | }
57 | )
58 | )
59 |
--------------------------------------------------------------------------------
/man-roxygen/configure-bar.R:
--------------------------------------------------------------------------------
1 | #' @title
2 | #' Configure The Progress Bar
3 | #'
4 | #' @description
5 | #' This function can be used to conveniently configure the progress bar by
6 | #' adjusting the `progress_bar_config` field of the
7 | #' [`Options`][`parabar::Options`] instance in the [`base::.Options`] list.
8 | #'
9 | #' @param type A character string specifying the type of progress bar to be used
10 | #' with compatible [`backends`][`parabar::Backend`]. Possible values are
11 | #' `"modern"` and `"basic"`. The default value is `"modern"`.
12 | #'
13 | #' @param ... A list of named arguments used to configure the progress bar. See
14 | #' the **Details** section for more information.
15 | #'
16 | #' @details
17 | #' The optional `...` named arguments depend on the `type` of progress bar being
18 | #' configured. When `type = "modern"`, the `...` take the named arguments of the
19 | #' [`progress::progress_bar`] class. When `type = "basic"`, the `...` take the
20 | #' named arguments of the [utils::txtProgressBar()] built-in function. See the
21 | #' **Examples** section for a demonstration.
22 | #'
23 | #' @return
24 | #' The function returns void. It throws an error if the requested bar `type` is
25 | #' not supported.
26 | #'
27 | #' @examples
28 | #' # Set the default package options.
29 | #' set_default_options()
30 | #'
31 | #' # Get the progress bar type from options.
32 | #' get_option("progress_bar_type")
33 | #'
34 | #' # Get the progress bar configuration from options.
35 | #' get_option("progress_bar_config")
36 | #'
37 | #' # Adjust the format of the `modern` progress bar.
38 | #' configure_bar(type = "modern", format = "[:bar] :percent")
39 | #'
40 | #' # Check that the configuration has been updated in the options.
41 | #' get_option("progress_bar_config")
42 | #'
43 | #' # Change to and adjust the style of the `basic` progress bar.
44 | #' configure_bar(type = "basic", style = 3)
45 | #'
46 | #' # Check that the configuration has been updated in the options.
47 | #' get_option("progress_bar_type")
48 | #' get_option("progress_bar_config")
49 | #'
50 | #' @seealso
51 | #' [`progress::progress_bar`], [utils::txtProgressBar()],
52 | #' [parabar::set_default_options()], [parabar::get_option()],
53 | #' [parabar::set_option()]
54 |
--------------------------------------------------------------------------------
/pkgdown/extra.scss:
--------------------------------------------------------------------------------
1 | @media (min-width: 1400px) {
2 | body {
3 | font-size: var(--bs-body-font-size);
4 | }
5 | }
6 |
7 | .container {
8 | main {
9 | margin-top: 0;
10 |
11 | // Hide redundant logo inserted by `pkgdown`.
12 | .page-header {
13 | img {
14 | display: none;
15 | }
16 | }
17 |
18 | div.sourceCode pre, .citation pre {
19 | box-shadow: 0 3px 6px rgba(0,0,0,0.16),0 3px 6px rgba(0,0,0,0.23);
20 | border-radius: .5rem;
21 | padding-left: 1rem;
22 | padding-right: 1rem;
23 | }
24 |
25 | .section {
26 | img {
27 | box-shadow: 0 3px 6px rgba(0,0,0,0.16),0 3px 6px rgba(0,0,0,0.23);
28 | padding: 1rem;
29 | border-radius: 1rem;
30 | }
31 |
32 | .license-cc img {
33 | padding: 0;
34 | box-shadow: none;
35 | border-radius: 0;
36 | margin-left: 3px;
37 | }
38 | }
39 |
40 | #additional-operations ~ table {
41 | display: table;
42 |
43 | col {
44 | &:first-of-type {
45 | width: 55%;
46 | }
47 |
48 | &:last-of-type {
49 | width: 45%;
50 | }
51 | }
52 | }
53 | }
54 |
55 | .row {
56 | display: flex;
57 | justify-content: space-between;
58 | box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
59 | border-radius: 1rem;
60 | padding: 2rem;
61 | margin-top: 5.25rem;
62 | }
63 | }
64 |
65 | .row > aside {
66 | margin-top: 0;
67 | }
68 |
69 | aside {
70 | div {
71 | box-shadow: 0 3px 6px rgba(0,0,0,0.16),0 3px 6px rgba(0,0,0,0.23);
72 | padding: 1rem 1rem;
73 | border-radius: 1rem;
74 | margin-bottom: 1.5rem;
75 |
76 | h2 {
77 | margin-top: 0;
78 | }
79 |
80 | ul {
81 | margin-bottom: 0;
82 |
83 | li:last-of-type {
84 | margin-bottom: 0;
85 | }
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/man/Exception.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/Exception.R
3 | \name{Exception}
4 | \alias{Exception}
5 | \title{Package Exceptions}
6 | \format{
7 | \describe{
8 | \item{\code{Exception$abstract_class_not_instantiable(object)}}{Exception for instantiating abstract classes or interfaces.}
9 | \item{\code{Exception$method_not_implemented()}}{Exception for calling methods without an implementation.}
10 | \item{\code{Exception$feature_not_developed()}}{Exception for running into things not yet developed.}
11 | \item{\code{Exception$not_enough_cores()}}{Exception for requesting more cores than available on the machine.}
12 | \item{\code{Exception$cluster_active()}}{Exception for attempting to start a cluster while another one is active.}
13 | \item{\code{Exception$cluster_not_active()}}{Exception for attempting to stop a cluster while not active.}
14 | \item{\code{Exception$async_task_not_started()}}{Exception for reading results while an asynchronous task has not yet started.}
15 | \item{\code{Exception$async_task_running()}}{Exception for reading results while an asynchronous task is running.}
16 | \item{\code{Exception$async_task_completed()}}{Exception for reading results while a completed asynchronous task has unread results.}
17 | \item{\code{Exception$async_task_error(error)}}{Exception for errors while running an asynchronous task.}
18 | \item{\code{Exception$stop_busy_backend_not_allowed()}}{Exception for stopping a busy backend without intent.}
19 | \item{\code{Exception$temporary_file_creation_failed()}}{Exception for reading results while an asynchronous task is running.}
20 | \item{\code{Exception$type_not_assignable(actual, expected)}}{Exception for when providing incorrect object types.}
21 | \item{\code{Exception$unknown_package_option(option)}}{Exception for when requesting unknown package options.}
22 | \item{\code{Exception$primitive_as_task_not_allowed()}}{Exception for when decorating primitive functions with progress tracking.}
23 | \item{\code{Exception$array_margins_not_compatible(actual, allowed)}}{Exception for using improper margins in the \code{BackendService$apply} operation.}
24 | }
25 | }
26 | \description{
27 | This class contains static methods for throwing exceptions with informative
28 | messages.
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/R/logo.R:
--------------------------------------------------------------------------------
1 | #' @template make-logo
2 | #' @export
3 | make_logo <- function(template = "./inst/assets/logo/parabar-logo.txt", version = c(1, 0, 0)) {
4 | # Load the ASCII logo.
5 | logo <- readLines(template)
6 |
7 | # Create a text connection.
8 | connection <- textConnection(NULL, open = "w", local = TRUE)
9 |
10 | # Close the connection on exit.
11 | on.exit({
12 | # Close.
13 | close(connection)
14 | })
15 |
16 | # Redirect console output.
17 | sink(connection, type = "output")
18 |
19 | # Parse the logo.
20 | logo <- dput(logo)
21 |
22 | # Remove output redirection.
23 | sink(NULL)
24 |
25 | # Update versioning.
26 | logo <- gsub("{{major}}", version[1], logo, perl = TRUE)
27 | logo <- gsub("{{minor}}", version[2], logo, perl = TRUE)
28 | logo <- gsub("{{patch}}", version[3], logo, perl = TRUE)
29 |
30 | # Condensed version.
31 | logo <- paste(logo, collapse = "\n")
32 |
33 | return(logo)
34 | }
35 |
36 |
37 | #' @title
38 | #' The Package Logo
39 | #'
40 | #' @description
41 | #' The logo is generated by [parabar::make_logo()] and displayed on package
42 | #' attach for interactive `R` sessions.
43 | #'
44 | #' @format
45 | #' An object of class `character` containing the `ASCII` logo.
46 | #'
47 | #' @examples
48 | #' print(LOGO)
49 | #'
50 | #' @seealso [parabar::make_logo()]
51 | #'
52 | #' @export
53 | LOGO = ". . . . . . . . . . . . . . . . . . . . . . . . . . .\n. _ v1.x.x .\n. | | .\n. _ __ __ _ _ __ __ _ | |__ __ _ _ __ .\n. | '_ \\ / _` || '__| / _` || '_ \\ / _` || '__| .\n. | |_) || (_| || | | (_| || |_) || (_| || | .\n. | .__/ \\____||_| \\____||____/ \\____||_| .\n. | | .\n. |_| .\n. .\n. . . . . . . . . . . . . . . . . . . . . . . . . . .\n. . .\n. https://parabar.mihaiconstantin.com .\n. . .\n. . . . . . . . . . . . . . . . . . . . . . . . . . ."
54 |
55 | # Add package logo class.
56 | class(LOGO) <- "parabar"
57 |
58 | # Add S3 method for properly printing the logo.
59 | #' @export
60 | print.parabar <- function(x, ...) {
61 | # Print the logo.
62 | cat(LOGO, sep = "")
63 | }
64 |
--------------------------------------------------------------------------------
/tests/testthat/test-context.R:
--------------------------------------------------------------------------------
1 | # Test `Context` class.
2 |
3 | test_that("'Context' sets the backend correctly", {
4 | # Create a backend factory.
5 | backend_factory <- BackendFactory$new()
6 |
7 | # Create a base context object.
8 | context <- Context$new()
9 |
10 | # Expect the context to start with no backend.
11 | expect_null(context$backend)
12 |
13 | # Get a synchronous backend instance.
14 | backend <- backend_factory$get("sync")
15 |
16 | # Register the backend with the context.
17 | context$set_backend(backend)
18 |
19 | # Expect the registered backend to be of correct type.
20 | expect_equal(
21 | Helper$get_class_name(context$backend),
22 | Helper$get_class_name(backend)
23 | )
24 |
25 | # Get an asynchronous backend instance.
26 | backend <- backend_factory$get("async")
27 |
28 | # Register the backend with the same context object.
29 | context$set_backend(backend)
30 |
31 | # Expect the registered backend to be of correct type.
32 | expect_equal(
33 | Helper$get_class_name(context$backend),
34 | Helper$get_class_name(backend)
35 | )
36 | })
37 |
38 |
39 | test_that("'Context' performs operations on the cluster correctly", {
40 | # Create a specification.
41 | specification <- Specification$new()
42 |
43 | # Set the number of cores.
44 | specification$set_cores(cores = 2)
45 |
46 | # Select a cluster type.
47 | cluster_type <- pick_cluster_type(specification$types)
48 |
49 | # Ser the cluster type.
50 | specification$set_type(type = cluster_type)
51 |
52 | # Create a backend factory.
53 | backend_factory <- BackendFactory$new()
54 |
55 | # Create a base context object.
56 | context <- Context$new()
57 |
58 | # Get a synchronous backend instance.
59 | backend <- backend_factory$get("sync")
60 |
61 | # Register the synchronous backend with the context.
62 | context$set_backend(backend)
63 |
64 | # Perform tests for synchronous backend operations.
65 | tests_set_for_synchronous_backend_operations(context, specification, test_task)
66 |
67 | # Get an asynchronous backend instance.
68 | backend <- backend_factory$get("async")
69 |
70 | # Register the asynchronous backend with the context.
71 | context$set_backend(backend)
72 |
73 | # Perform tests for synchronous backend operations.
74 | tests_set_for_asynchronous_backend_operations(context, specification, test_task)
75 | })
76 |
--------------------------------------------------------------------------------
/.github/workflows/test-coverage.yaml:
--------------------------------------------------------------------------------
1 | # Workflow name.
2 | name: test-coverage
3 |
4 | # Set workflow permissions.
5 | permissions: read-all
6 |
7 | # Events.
8 | on:
9 | push:
10 | branches:
11 | - main
12 | pull_request:
13 | branches:
14 | - main
15 | # Trigger manually via the GitHub UI.
16 | workflow_dispatch:
17 |
18 | # Jobs.
19 | jobs:
20 |
21 | # Test coverage job.
22 | test-coverage:
23 | runs-on: ubuntu-latest
24 | env:
25 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
26 |
27 | # Actions.
28 | steps:
29 | # Checkout.
30 | - name: Checkout repository
31 | uses: actions/checkout@v4
32 |
33 | # Setup `R`.
34 | - name: Setup R
35 | uses: r-lib/actions/setup-r@v2
36 | with:
37 | use-public-rspm: true
38 |
39 | # Install package dependencies and `covr` package.
40 | - name: Install dependencies
41 | uses: r-lib/actions/setup-r-dependencies@v2
42 | with:
43 | extra-packages: any::covr, any::xml2
44 | needs: coverage
45 |
46 | # Run the test coverage excluding specific lines.
47 | - name: Test coverage
48 | run: |
49 | cov <- covr::package_coverage(
50 | quiet = FALSE,
51 | clean = FALSE,
52 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package"),
53 | line_exclusions = list("R/parabar-package.R", "R/UserApiConsumer.R" = c(118:136))
54 | )
55 | covr::to_cobertura(cov)
56 | shell: Rscript {0}
57 |
58 | # Upload the coverage report to `Codecov``.
59 | - name: Upload coverage
60 | uses: codecov/codecov-action@v4
61 | with:
62 | fail_ci_if_error: ${{ github.event_name != 'pull_request' && true || false }}
63 | file: ./cobertura.xml
64 | plugin: noop
65 | disable_search: true
66 | token: ${{ secrets.CODECOV_TOKEN }}
67 |
68 | # Show the output of `testthat`.
69 | - name: Show testthat output
70 | if: always()
71 | run: |
72 | ## --------------------------------------------------------------------
73 | find ${{ runner.temp }}/package -name 'testthat.Rout*' -exec cat '{}' \; || true
74 | shell: bash
75 |
76 | # Upload results on failure.
77 | - name: Upload test results
78 | if: failure()
79 | uses: actions/upload-artifact@v4
80 | with:
81 | name: coverage-test-failures
82 | path: ${{ runner.temp }}/package
83 |
--------------------------------------------------------------------------------
/man/configure_bar.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/exports.R
3 | \name{configure_bar}
4 | \alias{configure_bar}
5 | \title{Configure The Progress Bar}
6 | \usage{
7 | configure_bar(type = "modern", ...)
8 | }
9 | \arguments{
10 | \item{type}{A character string specifying the type of progress bar to be used
11 | with compatible \code{\link[=Backend]{backends}}. Possible values are
12 | \code{"modern"} and \code{"basic"}. The default value is \code{"modern"}.}
13 |
14 | \item{...}{A list of named arguments used to configure the progress bar. See
15 | the \strong{Details} section for more information.}
16 | }
17 | \value{
18 | The function returns void. It throws an error if the requested bar \code{type} is
19 | not supported.
20 | }
21 | \description{
22 | This function can be used to conveniently configure the progress bar by
23 | adjusting the \code{progress_bar_config} field of the
24 | \code{\link[=Options]{Options}} instance in the \code{\link[base:options]{base::.Options}} list.
25 | }
26 | \details{
27 | The optional \code{...} named arguments depend on the \code{type} of progress bar being
28 | configured. When \code{type = "modern"}, the \code{...} take the named arguments of the
29 | \code{\link[progress:progress_bar]{progress::progress_bar}} class. When \code{type = "basic"}, the \code{...} take the
30 | named arguments of the \code{\link[utils:txtProgressBar]{utils::txtProgressBar()}} built-in function. See the
31 | \strong{Examples} section for a demonstration.
32 | }
33 | \examples{
34 | # Set the default package options.
35 | set_default_options()
36 |
37 | # Get the progress bar type from options.
38 | get_option("progress_bar_type")
39 |
40 | # Get the progress bar configuration from options.
41 | get_option("progress_bar_config")
42 |
43 | # Adjust the format of the `modern` progress bar.
44 | configure_bar(type = "modern", format = "[:bar] :percent")
45 |
46 | # Check that the configuration has been updated in the options.
47 | get_option("progress_bar_config")
48 |
49 | # Change to and adjust the style of the `basic` progress bar.
50 | configure_bar(type = "basic", style = 3)
51 |
52 | # Check that the configuration has been updated in the options.
53 | get_option("progress_bar_type")
54 | get_option("progress_bar_config")
55 |
56 | }
57 | \seealso{
58 | \code{\link[progress:progress_bar]{progress::progress_bar}}, \code{\link[utils:txtProgressBar]{utils::txtProgressBar()}},
59 | \code{\link[=set_default_options]{set_default_options()}}, \code{\link[=get_option]{get_option()}},
60 | \code{\link[=set_option]{set_option()}}
61 | }
62 |
--------------------------------------------------------------------------------
/R/Bar.R:
--------------------------------------------------------------------------------
1 | #' @include Exception.R
2 |
3 | #' @title
4 | #' Bar
5 | #'
6 | #' @description
7 | #' This is an abstract class that defines the pure virtual methods a concrete
8 | #' bar must implement.
9 | #'
10 | #' @details
11 | #' This class cannot be instantiated. It needs to be extended by concrete
12 | #' subclasses that implement the pure virtual methods. Instances of concrete
13 | #' backend implementations can be conveniently obtained using the
14 | #' [`parabar::BarFactory`] class.
15 | #'
16 | #' @seealso
17 | #' [`parabar::BasicBar`], [`parabar::ModernBar`], and [`parabar::BarFactory`].
18 | #'
19 | #' @export
20 | Bar <- R6::R6Class("Bar",
21 | private = list(
22 | .bar = NULL
23 | ),
24 |
25 | public = list(
26 | #' @description
27 | #' Create a new [`parabar::Bar`] object.
28 | #'
29 | #' @return
30 | #' Instantiating this class will throw an error.
31 | initialize = function() {
32 | Exception$abstract_class_not_instantiable(self)
33 | },
34 |
35 | #' @description
36 | #' Create a progress bar.
37 | #'
38 | #' @param total The total number of times the progress bar should tick.
39 | #'
40 | #' @param initial The starting point of the progress bar.
41 | #'
42 | #' @param ... Additional arguments for the bar creation. See the
43 | #' **Details** section for more information.
44 | #'
45 | #' @details
46 | #' The optional `...` named arguments depend on the specific concrete
47 | #' implementation (i.e., [`parabar::BasicBar`] or
48 | #' [`parabar::ModernBar`]).
49 | #'
50 | #' @return
51 | #' This method returns void. The resulting bar is stored in the private
52 | #' field `.bar`, accessible via the active binding `engine`.
53 | create = function(total, initial, ...) {
54 | Exception$method_not_implemented()
55 | },
56 |
57 | #' @description
58 | #' Update the progress bar.
59 | #'
60 | #' @param current The position the progress bar should be at (e.g., 30
61 | #' out of 100), usually the index in a loop.
62 | update = function(current) {
63 | Exception$method_not_implemented()
64 | },
65 |
66 | #' @description
67 | #' Terminate the progress bar.
68 | terminate = function() {
69 | Exception$method_not_implemented()
70 | }
71 | ),
72 |
73 | active = list(
74 | #' @field engine The bar engine.
75 | engine = function() { return(private$.bar) }
76 | )
77 | )
78 |
--------------------------------------------------------------------------------
/man/BarFactory.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/BarFactory.R
3 | \name{BarFactory}
4 | \alias{BarFactory}
5 | \title{BackendFactory}
6 | \description{
7 | This class is a factory that provides concrete implementations of the
8 | \code{\link{Bar}} abstract class.
9 | }
10 | \examples{
11 | # Create a bar factory.
12 | bar_factory <- BarFactory$new()
13 |
14 | # Get a modern bar instance.
15 | bar <- bar_factory$get("modern")
16 |
17 | # Check the class of the bar instance.
18 | class(bar)
19 |
20 | # Get a basic bar instance.
21 | bar <- bar_factory$get("basic")
22 |
23 | # Check the class of the bar instance.
24 | class(bar)
25 |
26 | }
27 | \seealso{
28 | \code{\link{Bar}}, \code{\link{BasicBar}}, and \code{\link{ModernBar}}.
29 | }
30 | \section{Methods}{
31 | \subsection{Public methods}{
32 | \itemize{
33 | \item \href{#method-BarFactory-get}{\code{BarFactory$get()}}
34 | \item \href{#method-BarFactory-clone}{\code{BarFactory$clone()}}
35 | }
36 | }
37 | \if{html}{\out{
}}
38 | \if{html}{\out{}}
39 | \if{latex}{\out{\hypertarget{method-BarFactory-get}{}}}
40 | \subsection{Method \code{get()}}{
41 | Obtain a concrete implementation of the abstract \code{\link{Bar}}
42 | class of the specified type.
43 | \subsection{Usage}{
44 | \if{html}{\out{}}\preformatted{BarFactory$get(type)}\if{html}{\out{
}}
45 | }
46 |
47 | \subsection{Arguments}{
48 | \if{html}{\out{}}
49 | \describe{
50 | \item{\code{type}}{A character string specifying the type of the
51 | \code{\link{Bar}} to instantiate. Possible values are \code{"modern"} and
52 | \code{"basic"}. See the \strong{Details} section for more information.}
53 | }
54 | \if{html}{\out{
}}
55 | }
56 | \subsection{Details}{
57 | When \code{type = "modern"} a \code{\link{ModernBar}} instance is created
58 | and returned. When \code{type = "basic"} a \code{\link{BasicBar}} instance
59 | is provided instead.
60 | }
61 |
62 | \subsection{Returns}{
63 | A concrete implementation of the class \code{\link{Bar}}. It throws an
64 | error if the requested bar \code{type} is not supported.
65 | }
66 | }
67 | \if{html}{\out{
}}
68 | \if{html}{\out{}}
69 | \if{latex}{\out{\hypertarget{method-BarFactory-clone}{}}}
70 | \subsection{Method \code{clone()}}{
71 | The objects of this class are cloneable with this method.
72 | \subsection{Usage}{
73 | \if{html}{\out{}}\preformatted{BarFactory$clone(deep = FALSE)}\if{html}{\out{
}}
74 | }
75 |
76 | \subsection{Arguments}{
77 | \if{html}{\out{}}
78 | \describe{
79 | \item{\code{deep}}{Whether to make a deep clone.}
80 | }
81 | \if{html}{\out{
}}
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/R/Backend.R:
--------------------------------------------------------------------------------
1 | #' @include Exception.R BackendService.R
2 |
3 | #' @title
4 | #' Backend
5 | #'
6 | #' @description
7 | #' This is an abstract class that serves as a base class for all concrete
8 | #' backend implementations. It defines the common properties that all concrete
9 | #' backends require.
10 | #'
11 | #' @details
12 | #' This class cannot be instantiated. It needs to be extended by concrete
13 | #' subclasses that implement the pure virtual methods. Instances of concrete
14 | #' backend implementations can be conveniently obtained using the
15 | #' [`parabar::BackendFactory`] class.
16 | #'
17 | #' @seealso
18 | #' [`parabar::BackendService`], [`parabar::SyncBackend`],
19 | #' [`parabar::AsyncBackend`], [`parabar::BackendFactory`], and
20 | #' [`parabar::Context`].
21 | #'
22 | #' @export
23 | Backend <- R6::R6Class("Backend",
24 | inherit = BackendService,
25 |
26 | private = list(
27 | # The engine used to dispatch the tasks.
28 | .cluster = NULL,
29 |
30 | # The progress tracking capabilities of the backend implementation.
31 | .supports_progress = FALSE,
32 |
33 | # The results of running the task on the backend.
34 | .output = NULL,
35 |
36 | # Whether the backend contains an active cluster.
37 | .active = FALSE,
38 |
39 | # Toggle active flag.
40 | .toggle_active_state = function() {
41 | # The to the opposite state.
42 | private$.active <- !private$.active
43 | }
44 | ),
45 |
46 | public = list(
47 | #' @description
48 | #' Create a new [`parabar::Backend`] object.
49 | #'
50 | #' @return
51 | #' Instantiating this class will throw an error.
52 | initialize = function() {
53 | Exception$abstract_class_not_instantiable(self)
54 | }
55 | ),
56 |
57 | active = list(
58 | #' @field cluster The cluster object used by the backend. For
59 | #' [`parabar::SyncBackend`] objects, this is a cluster object created by
60 | #' [parallel::makeCluster()]. For [`parabar::AsyncBackend`] objects,
61 | #' this is a permanent `R` session created by [`callr::r_session`] that
62 | #' contains the [parallel::makeCluster()] cluster object.
63 | cluster = function() { return(private$.cluster) },
64 |
65 | #' @field supports_progress A boolean value indicating whether the
66 | #' backend implementation supports progress tracking.
67 | supports_progress = function() { return(private$.supports_progress) },
68 |
69 | #' @field active A boolean value indicating whether the backend
70 | #' implementation has an active cluster.
71 | active = function() { return(private$.active) }
72 | )
73 | )
74 |
--------------------------------------------------------------------------------
/man/ContextFactory.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/ContextFactory.R
3 | \name{ContextFactory}
4 | \alias{ContextFactory}
5 | \title{ContextFactory}
6 | \description{
7 | This class is a factory that provides instances of the \code{\link{Context}}
8 | class.
9 | }
10 | \examples{
11 | # Create a context factory.
12 | context_factory <- ContextFactory$new()
13 |
14 | # Get a regular context instance.
15 | context <- context_factory$get("regular")
16 |
17 | # Check the class of the context instance.
18 | class(context)
19 |
20 | # Get a progress context instance.
21 | context <- context_factory$get("progress")
22 | class(context)
23 |
24 | }
25 | \seealso{
26 | \code{\link{Context}}, \code{\link{ProgressTrackingContext}},
27 | \code{\link{BackendService}}, and \code{\link{Backend}}
28 | }
29 | \section{Methods}{
30 | \subsection{Public methods}{
31 | \itemize{
32 | \item \href{#method-ContextFactory-get}{\code{ContextFactory$get()}}
33 | \item \href{#method-ContextFactory-clone}{\code{ContextFactory$clone()}}
34 | }
35 | }
36 | \if{html}{\out{
}}
37 | \if{html}{\out{}}
38 | \if{latex}{\out{\hypertarget{method-ContextFactory-get}{}}}
39 | \subsection{Method \code{get()}}{
40 | Obtain instances of the \code{\link{Context}} class.
41 | \subsection{Usage}{
42 | \if{html}{\out{}}\preformatted{ContextFactory$get(type)}\if{html}{\out{
}}
43 | }
44 |
45 | \subsection{Arguments}{
46 | \if{html}{\out{}}
47 | \describe{
48 | \item{\code{type}}{A character string specifying the type of the
49 | \code{\link{Context}} to instantiate. Possible values are \code{"regular"}
50 | and \code{"progress"}. See the \strong{Details} section for more information.}
51 | }
52 | \if{html}{\out{
}}
53 | }
54 | \subsection{Details}{
55 | When \code{type = "regular"} a \code{\link{Context}} instance is created
56 | and returned. When \code{type = "progress"} a
57 | \code{\link{ProgressTrackingContext}} instance is provided instead.
58 | }
59 |
60 | \subsection{Returns}{
61 | An object of type \code{\link{Context}}. It throws an error if the
62 | requested context \code{type} is not supported.
63 | }
64 | }
65 | \if{html}{\out{
}}
66 | \if{html}{\out{}}
67 | \if{latex}{\out{\hypertarget{method-ContextFactory-clone}{}}}
68 | \subsection{Method \code{clone()}}{
69 | The objects of this class are cloneable with this method.
70 | \subsection{Usage}{
71 | \if{html}{\out{}}\preformatted{ContextFactory$clone(deep = FALSE)}\if{html}{\out{
}}
72 | }
73 |
74 | \subsection{Arguments}{
75 | \if{html}{\out{}}
76 | \describe{
77 | \item{\code{deep}}{Whether to make a deep clone.}
78 | }
79 | \if{html}{\out{
}}
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/man/clear.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/exports.R
3 | \name{clear}
4 | \alias{clear}
5 | \title{Clear a Backend}
6 | \usage{
7 | clear(backend)
8 | }
9 | \arguments{
10 | \item{backend}{An object of class \code{\link{Backend}} as returned by the
11 | \code{\link[=start_backend]{start_backend()}} function.}
12 | }
13 | \value{
14 | The function returns void. It throws an error if the value provided for the
15 | \code{backend} argument is not an instance of class \code{\link{Backend}}.
16 | }
17 | \description{
18 | This function can be used to clear a \code{\link[=Backend]{backend}} created
19 | by \code{\link[=start_backend]{start_backend()}}.
20 | }
21 | \details{
22 | This function is a convenience wrapper around the lower-lever API of
23 | \code{\link{parabar}} aimed at developers. More specifically, this function
24 | calls the \code{\link[=BackendService]{clear}} method on the provided
25 | \code{\link[=Backend]{backend}} instance.
26 | }
27 | \examples{
28 | # Create an asynchronous backend.
29 | backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "async")
30 |
31 | # Check that the backend is active.
32 | backend$active
33 |
34 | # Check if there is anything on the backend.
35 | peek(backend)
36 |
37 | # Create a dummy variable.
38 | name <- "parabar"
39 |
40 | # Export the `name` variable in the current environment to the backend.
41 | export(backend, "name", environment())
42 |
43 | # Remove the dummy variable from the current environment.
44 | rm(name)
45 |
46 | # Check the backend to see that the variable has been exported.
47 | peek(backend)
48 |
49 | # Run an expression on the backend.
50 | # Note that the symbols in the expression are resolved on the backend.
51 | evaluate(backend, {
52 | # Print the name.
53 | print(paste0("Hello, ", name, "!"))
54 | })
55 |
56 | # Clear the backend.
57 | clear(backend)
58 |
59 | # Check that there is nothing on the backend.
60 | peek(backend)
61 |
62 | # Use a basic progress bar (i.e., see `parabar::Bar`).
63 | configure_bar(type = "basic", style = 3)
64 |
65 | # Run a task in parallel (i.e., approx. 1.25 seconds).
66 | output <- par_sapply(backend, x = 1:10, fun = function(x) {
67 | # Sleep a bit.
68 | Sys.sleep(0.25)
69 |
70 | # Compute and return.
71 | return(x + 1)
72 | })
73 |
74 | # Print the output.
75 | print(output)
76 |
77 | # Stop the backend.
78 | stop_backend(backend)
79 |
80 | # Check that the backend is not active.
81 | backend$active
82 |
83 | }
84 | \seealso{
85 | \code{\link[=start_backend]{start_backend()}}, \code{\link[=peek]{peek()}}, \code{\link[=export]{export()}},
86 | \code{\link[=evaluate]{evaluate()}}, \code{\link[=configure_bar]{configure_bar()}}, \code{\link[=par_sapply]{par_sapply()}},
87 | \code{\link[=par_lapply]{par_lapply()}}, \code{\link[=par_apply]{par_apply()}}, \code{\link[=stop_backend]{stop_backend()}},
88 | and \code{\link{BackendService}}.
89 | }
90 |
--------------------------------------------------------------------------------
/man/BackendFactory.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/BackendFactory.R
3 | \name{BackendFactory}
4 | \alias{BackendFactory}
5 | \title{BackendFactory}
6 | \description{
7 | This class is a factory that provides concrete implementations of the
8 | \code{\link{Backend}} abstract class.
9 | }
10 | \examples{
11 | # Create a backend factory.
12 | backend_factory <- BackendFactory$new()
13 |
14 | # Get a synchronous backend instance.
15 | backend <- backend_factory$get("sync")
16 |
17 | # Check the class of the backend instance.
18 | class(backend)
19 |
20 | # Get an asynchronous backend instance.
21 | backend <- backend_factory$get("async")
22 |
23 | # Check the class of the backend instance.
24 | class(backend)
25 |
26 | }
27 | \seealso{
28 | \code{\link{BackendService}}, \code{\link{Backend}}, \code{\link{SyncBackend}},
29 | \code{\link{AsyncBackend}}, and \code{\link{ContextFactory}}.
30 | }
31 | \section{Methods}{
32 | \subsection{Public methods}{
33 | \itemize{
34 | \item \href{#method-BackendFactory-get}{\code{BackendFactory$get()}}
35 | \item \href{#method-BackendFactory-clone}{\code{BackendFactory$clone()}}
36 | }
37 | }
38 | \if{html}{\out{
}}
39 | \if{html}{\out{}}
40 | \if{latex}{\out{\hypertarget{method-BackendFactory-get}{}}}
41 | \subsection{Method \code{get()}}{
42 | Obtain a concrete implementation of the abstract \code{\link{Backend}}
43 | class of the specified type.
44 | \subsection{Usage}{
45 | \if{html}{\out{}}\preformatted{BackendFactory$get(type)}\if{html}{\out{
}}
46 | }
47 |
48 | \subsection{Arguments}{
49 | \if{html}{\out{}}
50 | \describe{
51 | \item{\code{type}}{A character string specifying the type of the
52 | \code{\link{Backend}} to instantiate. Possible values are \code{"sync"} and
53 | \code{"async"}. See the \strong{Details} section for more information.}
54 | }
55 | \if{html}{\out{
}}
56 | }
57 | \subsection{Details}{
58 | When \code{type = "sync"} a \code{\link{SyncBackend}} instance is created
59 | and returned. When \code{type = "async"} an \code{\link{AsyncBackend}}
60 | instance is provided instead.
61 | }
62 |
63 | \subsection{Returns}{
64 | A concrete implementation of the class \code{\link{Backend}}. It
65 | throws an error if the requested backend \code{type} is not supported.
66 | }
67 | }
68 | \if{html}{\out{
}}
69 | \if{html}{\out{}}
70 | \if{latex}{\out{\hypertarget{method-BackendFactory-clone}{}}}
71 | \subsection{Method \code{clone()}}{
72 | The objects of this class are cloneable with this method.
73 | \subsection{Usage}{
74 | \if{html}{\out{}}\preformatted{BackendFactory$clone(deep = FALSE)}\if{html}{\out{
}}
75 | }
76 |
77 | \subsection{Arguments}{
78 | \if{html}{\out{}}
79 | \describe{
80 | \item{\code{deep}}{Whether to make a deep clone.}
81 | }
82 | \if{html}{\out{
}}
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/man/stop_backend.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/exports.R
3 | \name{stop_backend}
4 | \alias{stop_backend}
5 | \title{Stop a Backend}
6 | \usage{
7 | stop_backend(backend)
8 | }
9 | \arguments{
10 | \item{backend}{An object of class \code{\link{Backend}} as returned by the
11 | \code{\link[=start_backend]{start_backend()}} function.}
12 | }
13 | \value{
14 | The function returns void. It throws an error if:
15 | \itemize{
16 | \item the value provided for the \code{backend} argument is not an instance of class
17 | \code{\link{Backend}}.
18 | \item the \code{\link[=Backend]{backend}} object provided is already stopped
19 | (i.e., is not active).
20 | }
21 | }
22 | \description{
23 | This function can be used to stop a \code{\link[=Backend]{backend}} created
24 | by \code{\link[=start_backend]{start_backend()}}.
25 | }
26 | \details{
27 | This function is a convenience wrapper around the lower-lever API of
28 | \code{\link{parabar}} aimed at developers. More specifically, this function
29 | calls the \code{\link[=BackendService]{stop}} method on the provided
30 | \code{\link[=Backend]{backend}} instance.
31 | }
32 | \examples{
33 | # Create an asynchronous backend.
34 | backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "async")
35 |
36 | # Check that the backend is active.
37 | backend$active
38 |
39 | # Check if there is anything on the backend.
40 | peek(backend)
41 |
42 | # Create a dummy variable.
43 | name <- "parabar"
44 |
45 | # Export the `name` variable in the current environment to the backend.
46 | export(backend, "name", environment())
47 |
48 | # Remove the dummy variable from the current environment.
49 | rm(name)
50 |
51 | # Check the backend to see that the variable has been exported.
52 | peek(backend)
53 |
54 | # Run an expression on the backend.
55 | # Note that the symbols in the expression are resolved on the backend.
56 | evaluate(backend, {
57 | # Print the name.
58 | print(paste0("Hello, ", name, "!"))
59 | })
60 |
61 | # Clear the backend.
62 | clear(backend)
63 |
64 | # Check that there is nothing on the backend.
65 | peek(backend)
66 |
67 | # Use a basic progress bar (i.e., see `parabar::Bar`).
68 | configure_bar(type = "basic", style = 3)
69 |
70 | # Run a task in parallel (i.e., approx. 1.25 seconds).
71 | output <- par_sapply(backend, x = 1:10, fun = function(x) {
72 | # Sleep a bit.
73 | Sys.sleep(0.25)
74 |
75 | # Compute and return.
76 | return(x + 1)
77 | })
78 |
79 | # Print the output.
80 | print(output)
81 |
82 | # Stop the backend.
83 | stop_backend(backend)
84 |
85 | # Check that the backend is not active.
86 | backend$active
87 |
88 | }
89 | \seealso{
90 | \code{\link[=start_backend]{start_backend()}}, \code{\link[=peek]{peek()}}, \code{\link[=export]{export()}},
91 | \code{\link[=evaluate]{evaluate()}}, \code{\link[=clear]{clear()}}, \code{\link[=configure_bar]{configure_bar()}},
92 | \code{\link[=par_sapply]{par_sapply()}}, \code{\link[=par_apply]{par_apply()}}, \code{\link[=par_lapply]{par_lapply()}}, and
93 | \code{\link{BackendService}}.
94 | }
95 |
--------------------------------------------------------------------------------
/man/peek.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/exports.R
3 | \name{peek}
4 | \alias{peek}
5 | \title{Inspect a Backend}
6 | \usage{
7 | peek(backend)
8 | }
9 | \arguments{
10 | \item{backend}{An object of class \code{\link{Backend}} as returned by the
11 | \code{\link[=start_backend]{start_backend()}} function.}
12 | }
13 | \value{
14 | The function returns a list of character vectors, where each list element
15 | corresponds to a node, and each element of the character vector is the name
16 | of a variable present on that node. It throws an error if the value provided
17 | for the \code{backend} argument is not an instance of class \code{\link{Backend}}.
18 | }
19 | \description{
20 | This function can be used to check the names of the variables present on a
21 | \code{\link[=Backend]{backend}} created by \code{\link[=start_backend]{start_backend()}}.
22 | }
23 | \details{
24 | This function is a convenience wrapper around the lower-lever API of
25 | \code{\link{parabar}} aimed at developers. More specifically, this function
26 | calls the \code{\link[=BackendService]{peek}} method on the provided
27 | \code{\link[=Backend]{backend}} instance.
28 | }
29 | \examples{
30 | # Create an asynchronous backend.
31 | backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "async")
32 |
33 | # Check that the backend is active.
34 | backend$active
35 |
36 | # Check if there is anything on the backend.
37 | peek(backend)
38 |
39 | # Create a dummy variable.
40 | name <- "parabar"
41 |
42 | # Export the `name` variable in the current environment to the backend.
43 | export(backend, "name", environment())
44 |
45 | # Remove the dummy variable from the current environment.
46 | rm(name)
47 |
48 | # Check the backend to see that the variable has been exported.
49 | peek(backend)
50 |
51 | # Run an expression on the backend.
52 | # Note that the symbols in the expression are resolved on the backend.
53 | evaluate(backend, {
54 | # Print the name.
55 | print(paste0("Hello, ", name, "!"))
56 | })
57 |
58 | # Clear the backend.
59 | clear(backend)
60 |
61 | # Check that there is nothing on the backend.
62 | peek(backend)
63 |
64 | # Use a basic progress bar (i.e., see `parabar::Bar`).
65 | configure_bar(type = "basic", style = 3)
66 |
67 | # Run a task in parallel (i.e., approx. 1.25 seconds).
68 | output <- par_sapply(backend, x = 1:10, fun = function(x) {
69 | # Sleep a bit.
70 | Sys.sleep(0.25)
71 |
72 | # Compute and return.
73 | return(x + 1)
74 | })
75 |
76 | # Print the output.
77 | print(output)
78 |
79 | # Stop the backend.
80 | stop_backend(backend)
81 |
82 | # Check that the backend is not active.
83 | backend$active
84 |
85 | }
86 | \seealso{
87 | \code{\link[=start_backend]{start_backend()}}, \code{\link[=export]{export()}}, \code{\link[=evaluate]{evaluate()}},
88 | \code{\link[=clear]{clear()}}, \code{\link[=configure_bar]{configure_bar()}}, \code{\link[=par_sapply]{par_sapply()}},
89 | \code{\link[=par_lapply]{par_lapply()}}, \code{\link[=par_apply]{par_apply()}}, \code{\link[=stop_backend]{stop_backend()}},
90 | and \code{\link{BackendService}}.
91 | }
92 |
--------------------------------------------------------------------------------
/man/evaluate.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/exports.R
3 | \name{evaluate}
4 | \alias{evaluate}
5 | \title{Evaluate An Expression On The Backend}
6 | \usage{
7 | evaluate(backend, expression)
8 | }
9 | \arguments{
10 | \item{backend}{An object of class \code{\link{Backend}} as returned by the
11 | \code{\link[=start_backend]{start_backend()}} function.}
12 |
13 | \item{expression}{An unquoted expression to evaluate on the backend.}
14 | }
15 | \value{
16 | This method returns the result of the expression evaluation. It throws an
17 | error if the value provided for the \code{backend} argument is not an instance of
18 | class \code{\link{Backend}}.
19 | }
20 | \description{
21 | This function can be used to evaluate an arbitrary \code{\link[base:expression]{base::expression()}} a
22 | \code{\link[=Backend]{backend}} created by \code{\link[=start_backend]{start_backend()}}.
23 | }
24 | \details{
25 | This function is a convenience wrapper around the lower-lever API of
26 | \code{\link{parabar}} aimed at developers. More specifically, this function
27 | calls the \code{\link[=BackendService]{evaluate}} method on the provided
28 | \code{\link[=Backend]{backend}} instance.
29 | }
30 | \examples{
31 | # Create an asynchronous backend.
32 | backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "async")
33 |
34 | # Check that the backend is active.
35 | backend$active
36 |
37 | # Check if there is anything on the backend.
38 | peek(backend)
39 |
40 | # Create a dummy variable.
41 | name <- "parabar"
42 |
43 | # Export the `name` variable in the current environment to the backend.
44 | export(backend, "name", environment())
45 |
46 | # Remove the dummy variable from the current environment.
47 | rm(name)
48 |
49 | # Check the backend to see that the variable has been exported.
50 | peek(backend)
51 |
52 | # Run an expression on the backend.
53 | # Note that the symbols in the expression are resolved on the backend.
54 | evaluate(backend, {
55 | # Print the name.
56 | print(paste0("Hello, ", name, "!"))
57 | })
58 |
59 | # Clear the backend.
60 | clear(backend)
61 |
62 | # Check that there is nothing on the backend.
63 | peek(backend)
64 |
65 | # Use a basic progress bar (i.e., see `parabar::Bar`).
66 | configure_bar(type = "basic", style = 3)
67 |
68 | # Run a task in parallel (i.e., approx. 1.25 seconds).
69 | output <- par_sapply(backend, x = 1:10, fun = function(x) {
70 | # Sleep a bit.
71 | Sys.sleep(0.25)
72 |
73 | # Compute and return.
74 | return(x + 1)
75 | })
76 |
77 | # Print the output.
78 | print(output)
79 |
80 | # Stop the backend.
81 | stop_backend(backend)
82 |
83 | # Check that the backend is not active.
84 | backend$active
85 |
86 | }
87 | \seealso{
88 | \code{\link[=start_backend]{start_backend()}}, \code{\link[=peek]{peek()}}, \code{\link[=export]{export()}},
89 | \code{\link[=clear]{clear()}}, \code{\link[=configure_bar]{configure_bar()}}, \code{\link[=par_sapply]{par_sapply()}},
90 | \code{\link[=par_lapply]{par_lapply()}}, \code{\link[=par_apply]{par_apply()}}, \code{\link[=stop_backend]{stop_backend()}},
91 | and \code{\link{BackendService}}.
92 | }
93 |
--------------------------------------------------------------------------------
/man/export.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/exports.R
3 | \name{export}
4 | \alias{export}
5 | \title{Export Objects To a Backend}
6 | \usage{
7 | export(backend, variables, environment)
8 | }
9 | \arguments{
10 | \item{backend}{An object of class \code{\link{Backend}} as returned by the
11 | \code{\link[=start_backend]{start_backend()}} function.}
12 |
13 | \item{variables}{A character vector of variable names to export to the
14 | backend.}
15 |
16 | \item{environment}{An environment from which to export the variables. If no
17 | environment is provided, the \code{.GlobalEnv} environment is used.}
18 | }
19 | \value{
20 | The function returns void. It throws an error if the value provided for the
21 | \code{backend} argument is not an instance of class \code{\link{Backend}}.
22 | }
23 | \description{
24 | This function can be used to export objects to a
25 | \code{\link[=Backend]{backend}} created by \code{\link[=start_backend]{start_backend()}}.
26 | }
27 | \details{
28 | This function is a convenience wrapper around the lower-lever API of
29 | \code{\link{parabar}} aimed at developers. More specifically, this function
30 | calls the \code{\link[=BackendService]{export}} method on the provided
31 | \code{\link[=Backend]{backend}} instance.
32 | }
33 | \examples{
34 | # Create an asynchronous backend.
35 | backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "async")
36 |
37 | # Check that the backend is active.
38 | backend$active
39 |
40 | # Check if there is anything on the backend.
41 | peek(backend)
42 |
43 | # Create a dummy variable.
44 | name <- "parabar"
45 |
46 | # Export the `name` variable in the current environment to the backend.
47 | export(backend, "name", environment())
48 |
49 | # Remove the dummy variable from the current environment.
50 | rm(name)
51 |
52 | # Check the backend to see that the variable has been exported.
53 | peek(backend)
54 |
55 | # Run an expression on the backend.
56 | # Note that the symbols in the expression are resolved on the backend.
57 | evaluate(backend, {
58 | # Print the name.
59 | print(paste0("Hello, ", name, "!"))
60 | })
61 |
62 | # Clear the backend.
63 | clear(backend)
64 |
65 | # Check that there is nothing on the backend.
66 | peek(backend)
67 |
68 | # Use a basic progress bar (i.e., see `parabar::Bar`).
69 | configure_bar(type = "basic", style = 3)
70 |
71 | # Run a task in parallel (i.e., approx. 1.25 seconds).
72 | output <- par_sapply(backend, x = 1:10, fun = function(x) {
73 | # Sleep a bit.
74 | Sys.sleep(0.25)
75 |
76 | # Compute and return.
77 | return(x + 1)
78 | })
79 |
80 | # Print the output.
81 | print(output)
82 |
83 | # Stop the backend.
84 | stop_backend(backend)
85 |
86 | # Check that the backend is not active.
87 | backend$active
88 |
89 | }
90 | \seealso{
91 | \code{\link[=start_backend]{start_backend()}}, \code{\link[=peek]{peek()}}, \code{\link[=evaluate]{evaluate()}},
92 | \code{\link[=clear]{clear()}}, \code{\link[=configure_bar]{configure_bar()}}, \code{\link[=par_sapply]{par_sapply()}},
93 | \code{\link[=par_lapply]{par_lapply()}}, \code{\link[=par_apply]{par_apply()}}, \code{\link[=stop_backend]{stop_backend()}},
94 | and \code{\link{BackendService}}.
95 | }
96 |
--------------------------------------------------------------------------------
/R/Warning.R:
--------------------------------------------------------------------------------
1 | #' @include Helper.R
2 |
3 | #' @title
4 | #' Package Warnings
5 | #'
6 | #' @description
7 | #' This class contains static methods for throwing warnings with informative
8 | #' messages.
9 | #'
10 | #' @format
11 | #' \describe{
12 | #' \item{\code{Warning$requested_cluster_cores_too_low()}}{Warning for not requesting enough cluster cores.}
13 | #' \item{\code{Warning$requested_cluster_cores_too_high()}}{Warning for requesting too many cluster cores.}
14 | #' \item{\code{Warning$requested_cluster_type_not_supported()}}{Warning for requesting an unsupported cluster type.}
15 | #' \item{\code{Warning$progress_not_supported_for_backend()}}{Warning for using a backend incompatible with progress tracking.}
16 | #' \item{\code{Warning$error_in_backend_finalizer()}}{Warning for errors in the backend finalizer during garbage collection.}
17 | #' }
18 | #'
19 | #' @export
20 | Warning <- R6::R6Class("Warning",
21 | cloneable = FALSE
22 | )
23 |
24 | # Warning for not requesting enough cluster cores.
25 | Warning$requested_cluster_cores_too_low <- function() {
26 | # Construct the message.
27 | message <- paste0("Argument `cores` too low. Setting to 1.")
28 |
29 | # Issue the warning.
30 | warning(message, call. = FALSE)
31 | }
32 |
33 | # Warning for requesting too many cluster cores.
34 | Warning$requested_cluster_cores_too_high <- function(max_cores) {
35 | # Construct the message.
36 | message <- paste0("Argument `cores` cannot be larger than ", max_cores, ". Setting to ", max_cores, ".")
37 |
38 | # Issue the warning.
39 | warning(message, call. = FALSE)
40 | }
41 |
42 | # Warning for requesting an unsupported cluster type.
43 | Warning$requested_cluster_type_not_supported <- function(supported_types) {
44 | # Issue the warning.
45 | warning(
46 | paste0(
47 | "Argument `type` must be ",
48 | paste0("'", supported_types, "'", collapse = " or ", sep = ""), ". Defaulting to '", supported_types["windows"], "'."
49 | ),
50 | call. = FALSE
51 | )
52 | }
53 |
54 | # Warning for requesting an incompatible cluster type.
55 | Warning$requested_cluster_type_not_compatible <- function(supported_types) {
56 | # Issue the warning.
57 | warning(
58 | paste0(
59 | "Requested cluster type not compatible. Defaulting to '", supported_types["windows"], "'."
60 | ),
61 | call. = FALSE
62 | )
63 | }
64 |
65 | # Warning for using a backend incompatible with progress tracking.
66 | Warning$progress_not_supported_for_backend <- function(backend) {
67 | # Get backend type.
68 | type <- Helper$get_class_name(backend)
69 |
70 | # Construct warning message.
71 | message <- paste0("Progress tracking not supported for backend of type '", type, "'.")
72 |
73 | # Throw the error.
74 | warning(message, call. = FALSE)
75 | }
76 |
77 | # Warning for errors in the backend finalizer during garbage collection.
78 | Warning$error_in_backend_finalizer <- function(backend, error) {
79 | # Get the backend type.
80 | backend_type <- Helper$get_class_name(backend)
81 |
82 | # Construct the message.
83 | message <- paste0("Caught error in '", backend_type ,"' finalizer: '", error$message, "'.")
84 |
85 | # Issue the warning.
86 | warning(message, call. = FALSE)
87 | }
88 |
--------------------------------------------------------------------------------
/tests/testthat/test-backend-finalizer.R:
--------------------------------------------------------------------------------
1 | # Test automatic finalizers for `SyncBackend` and `AsyncBackend` objects.
2 |
3 | test_that("'SyncBackend' finalizer executes without throwing", {
4 | # Start a synchronous backend.
5 | backend <- start_backend(cores = 2, backend_type = "sync")
6 |
7 | # Run a task on the backend.
8 | backend$sapply(1:100, function(x) x)
9 |
10 | # Remove the backend.
11 | rm(backend)
12 |
13 | # Trigger the garbage collection.
14 | gc(verbose = FALSE)
15 |
16 | # Check that no `backend` variable is present.
17 | expect_false(exists("backend"))
18 | })
19 |
20 |
21 | test_that("'AsyncBackend' finalizer executes without throwing", {
22 | # Define a simple task with non-trivial computation time.
23 | task <- function(x) {
24 | # Sleep a bit.
25 | Sys.sleep(0.001)
26 |
27 | # Return the value.
28 | return(x)
29 | }
30 |
31 | # Ensure forceful stop is disabled.
32 | set_option("stop_forceful", FALSE)
33 |
34 | # #region Test finalizer with a running task.
35 |
36 | # Start an asynchronous backend.
37 | backend <- start_backend(cores = 2, backend_type = "async")
38 |
39 | # Run a task on the backend.
40 | backend$sapply(1:100, task)
41 |
42 | # Remove the backend.
43 | rm(backend)
44 |
45 | # Trigger the garbage collection.
46 | gc(verbose = FALSE)
47 |
48 | # #endregion
49 |
50 | # #region Test finalizer with unread results.
51 |
52 | # Start an asynchronous backend.
53 | backend <- start_backend(cores = 2, backend_type = "async")
54 |
55 | # Run a task on the backend.
56 | backend$sapply(1:100, function(x) x)
57 |
58 | # Block the main session until the task is completed.
59 | block_until_async_task_finished(backend)
60 |
61 | # Remove the backend.
62 | rm(backend)
63 |
64 | # Trigger the garbage collection.
65 | gc(verbose = FALSE)
66 |
67 | # #endregion
68 |
69 | # Enable forceful stop.
70 | set_option("stop_forceful", TRUE)
71 |
72 | # #region Test finalizer with forceful stop while task is running.
73 |
74 | # Start an asynchronous backend.
75 | backend <- start_backend(cores = 2, backend_type = "async")
76 |
77 | # Run a task on the backend.
78 | backend$sapply(1:100, task)
79 |
80 | # Remove the backend.
81 | rm(backend)
82 |
83 | # Trigger the garbage collection.
84 | gc(verbose = FALSE)
85 |
86 | # #endregion
87 |
88 | # #region Test finalizer with forceful stop while task has unread results.
89 |
90 | # Start an asynchronous backend.
91 | backend <- start_backend(cores = 2, backend_type = "async")
92 |
93 | # Run a task on the backend.
94 | backend$sapply(1:100, function(x) x)
95 |
96 | # Block the main session until the task is completed.
97 | block_until_async_task_finished(backend)
98 |
99 | # Remove the backend.
100 | rm(backend)
101 |
102 | # Trigger the garbage collection.
103 | gc(verbose = FALSE)
104 |
105 | # #endregion
106 |
107 | # Check that no `backend` variable is present.
108 | expect_false(exists("backend"))
109 |
110 | # Restore options defaults.
111 | set_default_options()
112 | })
113 |
--------------------------------------------------------------------------------
/man-roxygen/par-lapply.R:
--------------------------------------------------------------------------------
1 | #' @title
2 | #' Run a Task in Parallel
3 | #'
4 | #' @description
5 | #' This function can be used to run a task in parallel. The task is executed in
6 | #' parallel on the specified backend, similar to [`parallel::parLapply()`]. If
7 | #' `backend = NULL`, the task is executed sequentially using [`base::lapply()`].
8 | #' See the **Details** section for more information on how this function works.
9 | #'
10 | #' @param backend An object of class [`parabar::Backend`] as returned by the
11 | #' [parabar::start_backend()] function. It can also be `NULL` to run the task
12 | #' sequentially via [`base::lapply()`]. The default value is `NULL`.
13 | #'
14 | #' @param x An atomic vector or list to pass to the `fun` function.
15 | #'
16 | #' @param fun A function to apply to each element of `x`.
17 | #'
18 | #' @param ... Additional arguments to pass to the `fun` function.
19 | #'
20 | #' @details
21 | #' This function uses the [`parabar::UserApiConsumer`] class that acts like an
22 | #' interface for the developer API of the [`parabar::parabar`] package.
23 | #'
24 | #' @return
25 | #' A list of the same length as `x` containing the results of the `fun`. The
26 | #' output format resembles that of [`base::lapply()`].
27 | #'
28 | #' @examples
29 | #' \donttest{
30 | #'
31 | #' # Define a simple task.
32 | #' task <- function(x) {
33 | #' # Perform computations.
34 | #' Sys.sleep(0.01)
35 | #'
36 | #' # Return the result.
37 | #' return(x + 1)
38 | #' }
39 | #'
40 | #' # Start an asynchronous backend.
41 | #' backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "async")
42 | #'
43 | #' # Run a task in parallel.
44 | #' results <- par_lapply(backend, x = 1:300, fun = task)
45 | #'
46 | #' # Disable progress tracking.
47 | #' set_option("progress_track", FALSE)
48 | #'
49 | #' # Run a task in parallel.
50 | #' results <- par_lapply(backend, x = 1:300, fun = task)
51 | #'
52 | #' # Enable progress tracking.
53 | #' set_option("progress_track", TRUE)
54 | #'
55 | #' # Change the progress bar options.
56 | #' configure_bar(type = "modern", format = "[:bar] :percent")
57 | #'
58 | #' # Run a task in parallel.
59 | #' results <- par_lapply(backend, x = 1:300, fun = task)
60 | #'
61 | #' # Stop the backend.
62 | #' stop_backend(backend)
63 | #'
64 | #' # Start a synchronous backend.
65 | #' backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "sync")
66 | #'
67 | #' # Run a task in parallel.
68 | #' results <- par_lapply(backend, x = 1:300, fun = task)
69 | #'
70 | #' # Disable progress tracking to remove the warning that progress is not supported.
71 | #' set_option("progress_track", FALSE)
72 | #'
73 | #' # Run a task in parallel.
74 | #' results <- par_lapply(backend, x = 1:300, fun = task)
75 | #'
76 | #' # Stop the backend.
77 | #' stop_backend(backend)
78 | #'
79 | #' # Run the task using the `base::lapply` (i.e., non-parallel).
80 | #' results <- par_lapply(NULL, x = 1:300, fun = task)
81 | #'
82 | #' }
83 | #'
84 | #' @seealso
85 | #' [parabar::start_backend()], [parabar::peek()], [parabar::export()],
86 | #' [parabar::evaluate()], [parabar::clear()], [parabar::configure_bar()],
87 | #' [parabar::par_sapply()], [parabar::par_apply()], [parabar::stop_backend()],
88 | #' [parabar::set_option()], [parabar::get_option()], [`parabar::Options`],
89 | #' [`parabar::UserApiConsumer`], and [`parabar::BackendService`].
90 |
--------------------------------------------------------------------------------
/man-roxygen/par-sapply.R:
--------------------------------------------------------------------------------
1 | #' @title
2 | #' Run a Task in Parallel
3 | #'
4 | #' @description
5 | #' This function can be used to run a task in parallel. The task is executed in
6 | #' parallel on the specified backend, similar to [`parallel::parSapply()`]. If
7 | #' `backend = NULL`, the task is executed sequentially using [`base::sapply()`].
8 | #' See the **Details** section for more information on how this function works.
9 | #'
10 | #' @param backend An object of class [`parabar::Backend`] as returned by the
11 | #' [parabar::start_backend()] function. It can also be `NULL` to run the task
12 | #' sequentially via [`base::sapply()`]. The default value is `NULL`.
13 | #'
14 | #' @param x An atomic vector or list to pass to the `fun` function.
15 | #'
16 | #' @param fun A function to apply to each element of `x`.
17 | #'
18 | #' @param ... Additional arguments to pass to the `fun` function.
19 | #'
20 | #' @details
21 | #' This function uses the [`parabar::UserApiConsumer`] class that acts like an
22 | #' interface for the developer API of the [`parabar::parabar`] package.
23 | #'
24 | #' @return
25 | #' A vector of the same length as `x` containing the results of the `fun`. The
26 | #' output format resembles that of [`base::sapply()`].
27 | #'
28 | #' @examples
29 | #' \donttest{
30 | #'
31 | #' # Define a simple task.
32 | #' task <- function(x) {
33 | #' # Perform computations.
34 | #' Sys.sleep(0.01)
35 | #'
36 | #' # Return the result.
37 | #' return(x + 1)
38 | #' }
39 | #'
40 | #' # Start an asynchronous backend.
41 | #' backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "async")
42 | #'
43 | #' # Run a task in parallel.
44 | #' results <- par_sapply(backend, x = 1:300, fun = task)
45 | #'
46 | #' # Disable progress tracking.
47 | #' set_option("progress_track", FALSE)
48 | #'
49 | #' # Run a task in parallel.
50 | #' results <- par_sapply(backend, x = 1:300, fun = task)
51 | #'
52 | #' # Enable progress tracking.
53 | #' set_option("progress_track", TRUE)
54 | #'
55 | #' # Change the progress bar options.
56 | #' configure_bar(type = "modern", format = "[:bar] :percent")
57 | #'
58 | #' # Run a task in parallel.
59 | #' results <- par_sapply(backend, x = 1:300, fun = task)
60 | #'
61 | #' # Stop the backend.
62 | #' stop_backend(backend)
63 | #'
64 | #' # Start a synchronous backend.
65 | #' backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "sync")
66 | #'
67 | #' # Run a task in parallel.
68 | #' results <- par_sapply(backend, x = 1:300, fun = task)
69 | #'
70 | #' # Disable progress tracking to remove the warning that progress is not supported.
71 | #' set_option("progress_track", FALSE)
72 | #'
73 | #' # Run a task in parallel.
74 | #' results <- par_sapply(backend, x = 1:300, fun = task)
75 | #'
76 | #' # Stop the backend.
77 | #' stop_backend(backend)
78 | #'
79 | #' # Run the task using the `base::sapply` (i.e., non-parallel).
80 | #' results <- par_sapply(NULL, x = 1:300, fun = task)
81 | #'
82 | #' }
83 | #'
84 | #' @seealso
85 | #' [parabar::start_backend()], [parabar::peek()], [parabar::export()],
86 | #' [parabar::evaluate()], [parabar::clear()], [parabar::configure_bar()],
87 | #' [parabar::par_lapply()], [parabar::par_apply()], [parabar::stop_backend()],
88 | #' [parabar::set_option()], [parabar::get_option()], [`parabar::Options`],
89 | #' [`parabar::UserApiConsumer`], and [`parabar::BackendService`].
90 |
--------------------------------------------------------------------------------
/R/Helper.R:
--------------------------------------------------------------------------------
1 | #' @include Options.R
2 |
3 | #' @title
4 | #' Package Helpers
5 | #'
6 | #' @description
7 | #' This class contains static helper methods.
8 | #'
9 | #' @format
10 | #' \describe{
11 | #' \item{\code{Helper$get_class_name(object)}}{Helper for getting the class of a given object.}
12 | #' \item{\code{Helper$is_of_class(object, class)}}{Check if an object is of a certain class.}
13 | #' \item{\code{Helper$get_option(option)}}{Get package option, or corresponding default value.}
14 | #' \item{\code{Helper$set_option(option, value)}}{Set package option.}
15 | #' \item{\code{Helper$check_object_type(object, expected_type)}}{Check the type of a given object.}
16 | #' \item{\code{Helper$check_array_margins(margins, dimensions)}}{Helper to check array margins for the `BackendService$apply` operation.}
17 | #' }
18 | #'
19 | #' @export
20 | Helper <- R6::R6Class("Helper",
21 | cloneable = FALSE
22 | )
23 |
24 | # Helper for getting the class of a given instance.
25 | Helper$get_class_name <- function(object) {
26 | return(class(object)[1])
27 | }
28 |
29 | # Helper to check if object is of certain class.
30 | Helper$is_of_class <- function(object, class) {
31 | return(class(object)[1] == class)
32 | }
33 |
34 | # Get package option, or corresponding default value.
35 | Helper$get_option <- function(option) {
36 | # Get the `Options` instance from the global options, or create a new one.
37 | options <- getOption("parabar", default = Options$new())
38 |
39 | # If the requested option is unknown.
40 | if (!option %in% ls(options)) {
41 | # Throw an error.
42 | Exception$unknown_package_option(option)
43 | }
44 |
45 | # Return the value.
46 | return(options[[option]])
47 | }
48 |
49 | # Set package option.
50 | Helper$set_option <- function(option, value) {
51 | # Get the `Options` instance from the global options, or create a new one.
52 | options <- getOption("parabar", default = Options$new())
53 |
54 | # If the requested option is unknown.
55 | if (!option %in% ls(options)) {
56 | # Throw an error.
57 | Exception$unknown_package_option(option)
58 | }
59 |
60 | # Set the value.
61 | options[[option]] <- value
62 |
63 | # Set the `Options` instance in the global options.
64 | options(parabar = options)
65 | }
66 |
67 | # Helper for performing a type check on a given object.
68 | Helper$check_object_type <- function(object, expected_type) {
69 | # If the object does not inherit from the expected type.
70 | if (!inherits(object, expected_type)) {
71 | # Get object class name.
72 | type <- Helper$get_class_name(object)
73 |
74 | # Throw incorrect type error.
75 | Exception$type_not_assignable(type, expected_type)
76 | }
77 | }
78 |
79 | # Helper for checking the array margins provided for the `apply` operation.
80 | Helper$check_array_margins <- function(margins, dimensions) {
81 | # Conditions to ensure the margins are valid.
82 | violations <- c(
83 | # Ensure all margins are unique.
84 | duplicated(margins),
85 |
86 | # Ensure all margins are within the array dimensions.
87 | margins > length(dimensions)
88 | )
89 |
90 | # If any violations are found.
91 | if (any(violations)) {
92 | # Throw an error.
93 | Exception$array_margins_not_compatible(margins, dimensions)
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/R/BasicBar.R:
--------------------------------------------------------------------------------
1 | #' @include Bar.R
2 |
3 | #' @title
4 | #' BasicBar
5 | #'
6 | #' @description
7 | #' This is a concrete implementation of the abstract class [`parabar::Bar`]
8 | #' using the [`utils::txtProgressBar()`] as engine for the progress bar.
9 | #'
10 | #' @examples
11 | #' # Create a basic bar instance.
12 | #' bar <- BasicBar$new()
13 | #'
14 | #' # Specify the number of ticks to be performed.
15 | #' total <- 100
16 | #'
17 | #' # Create the progress bar.
18 | #' bar$create(total = total, initial = 0)
19 | #'
20 | #' # Use the progress bar.
21 | #' for (i in 1:total) {
22 | #' # Sleep a bit.
23 | #' Sys.sleep(0.02)
24 | #'
25 | #' # Update the progress bar.
26 | #' bar$update(i)
27 | #' }
28 | #'
29 | #' # Terminate the progress bar.
30 | #' bar$terminate()
31 | #'
32 | #' @seealso
33 | #' [`parabar::Bar`], [`parabar::ModernBar`], and [`parabar::BarFactory`].
34 | #'
35 | #' @export
36 | BasicBar <- R6::R6Class("BasicBar",
37 | inherit = Bar,
38 |
39 | private = list(
40 | # Create bar.
41 | .create = function(total, initial, ...) {
42 | # Create and store the bar object.
43 | private$.bar <- do.call(
44 | utils::txtProgressBar,
45 | utils::modifyList(
46 | list(min = 0, max = total, initial = initial), list(...)
47 | )
48 | )
49 | },
50 |
51 | # Update bar.
52 | .update = function(current) {
53 | # Perform the update.
54 | utils::setTxtProgressBar(private$.bar, current)
55 | },
56 |
57 | # Terminate the bar.
58 | .terminate = function() {
59 | # Close the bar.
60 | close(private$.bar)
61 | }
62 | ),
63 |
64 | public = list(
65 | #' @description
66 | #' Create a new [`parabar::BasicBar`] object.
67 | #'
68 | #' @return
69 | #' An object of class [`parabar::BasicBar`].
70 | initialize = function() {},
71 |
72 | #' @description
73 | #' Create a progress bar.
74 | #'
75 | #' @param total The total number of times the progress bar should tick.
76 | #'
77 | #' @param initial The starting point of the progress bar.
78 | #'
79 | #' @param ... Additional arguments for the bar creation passed to
80 | #' [`utils::txtProgressBar()`].
81 | #'
82 | #' @return
83 | #' This method returns void. The resulting bar is stored in the private
84 | #' field `.bar`, accessible via the active binding `engine`. Both the
85 | #' private field and the active binding are defined in the super class
86 | #' [`parabar::Bar`].
87 | create = function(total, initial, ...) {
88 | private$.create(total, initial, ...)
89 | },
90 |
91 | #' @description
92 | #' Update the progress bar by calling [`utils::setTxtProgressBar()`].
93 | #'
94 | #' @param current The position the progress bar should be at (e.g., 30
95 | #' out of 100), usually the index in a loop.
96 | update = function(current) {
97 | private$.update(current)
98 | },
99 |
100 | #' @description
101 | #' Terminate the progress bar by calling [`base::close()`] on the
102 | #' private field `.bar`.
103 | terminate = function() {
104 | private$.terminate()
105 | }
106 | )
107 | )
108 |
--------------------------------------------------------------------------------
/tests/testthat/test-states.R:
--------------------------------------------------------------------------------
1 | # Test `SessionState` and `TaskState` classes.
2 |
3 | test_that("'SessionState' correctly reports the state given the backend operations", {
4 | # Create a specification.
5 | specification <- Specification$new()
6 |
7 | # Set the number of cores.
8 | specification$set_cores(cores = 2)
9 |
10 | # Pick a cluster type.
11 | cluster_type <- pick_cluster_type(specification$types)
12 |
13 | # Let the specification determine the cluster type.
14 | specification$set_type(type = cluster_type)
15 |
16 | # Create an asynchronous backend object.
17 | backend <- AsyncBackend$new()
18 |
19 | # Start the cluster on the backend.
20 | backend$start(specification)
21 |
22 | # Expect the session to have been started (i.e., due to the wait timeout).
23 | expect_false(backend$session_state$session_is_starting)
24 |
25 | # Expect the session to be idle (i.e., it starts with a wait timeout).
26 | expect_true(backend$session_state$session_is_idle)
27 |
28 | # Run a task on the backend.
29 | backend$sapply(1:100, function(x) {
30 | # Sleep a bit.
31 | Sys.sleep(0.005)
32 |
33 | # Return the value.
34 | return(x)
35 | })
36 |
37 | # Expect the session to be busy (i.e., it is running a task).
38 | expect_true(backend$session_state$session_is_busy)
39 |
40 | # Wait to read the results into the main session.
41 | results <- backend$get_output(wait = TRUE)
42 |
43 | # Expect the session to be idle (i.e., it has finished with read results).
44 | expect_true(backend$session_state$session_is_idle)
45 |
46 | # Manually close the session.
47 | backend$cluster$close()
48 |
49 | # Expect the session to be finished (i.e., it has been closed).
50 | expect_true(backend$session_state$session_is_finished)
51 |
52 | # Stop the backend.
53 | backend$stop()
54 | })
55 |
56 |
57 | test_that("'TaskState' correctly reports the state given the backend operations", {
58 | # Create a specification.
59 | specification <- Specification$new()
60 |
61 | # Set the number of cores.
62 | specification$set_cores(cores = 2)
63 |
64 | # Pick a cluster type.
65 | cluster_type <- pick_cluster_type(specification$types)
66 |
67 | # Let the specification determine the cluster type.
68 | specification$set_type(type = cluster_type)
69 |
70 | # Create an asynchronous backend object.
71 | backend <- AsyncBackend$new()
72 |
73 | # Start the cluster on the backend.
74 | backend$start(specification)
75 |
76 | # Expect the task to not have started.
77 | expect_true(backend$task_state$task_not_started)
78 |
79 | # Run a task on the backend.
80 | backend$sapply(1:100, function(x) {
81 | # Sleep a bit.
82 | Sys.sleep(0.005)
83 |
84 | # Return the value.
85 | return(x)
86 | })
87 |
88 | # Expect the task to be running.
89 | expect_true(backend$task_state$task_is_running)
90 |
91 | # Block the main session until the task is completed.
92 | block_until_async_task_finished(backend)
93 |
94 | # Expect the task to be completed (i.e., with unread results).
95 | expect_true(backend$task_state$task_is_completed)
96 |
97 | # Read the results into the main session.
98 | results <- backend$get_output()
99 |
100 | # Expect the task to not have started.
101 | expect_true(backend$task_state$task_not_started)
102 |
103 | # Stop the backend.
104 | backend$stop()
105 | })
106 |
--------------------------------------------------------------------------------
/man/option.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/exports.R
3 | \name{get_option}
4 | \alias{get_option}
5 | \alias{set_option}
6 | \alias{set_default_options}
7 | \title{Get or Set Package Option}
8 | \usage{
9 | get_option(option)
10 |
11 | set_option(option, value)
12 |
13 | set_default_options()
14 | }
15 | \arguments{
16 | \item{option}{A character string representing the name of the option to
17 | retrieve or adjust. See the public fields of \code{\link[R6:R6Class]{R6::R6}} class
18 | \code{\link{Options}} for the list of available \code{\link{parabar}}
19 | \code{\link[base:options]{options}}.}
20 |
21 | \item{value}{The value to set the \code{\link[=Options]{option}} to.}
22 | }
23 | \value{
24 | The \code{\link[=get_option]{get_option()}} function returns the value of the requested
25 | \code{\link[=Options]{option}} present in the \code{\link[base:options]{base::.Options}} list, or its
26 | corresponding default value (i.e., see \code{\link{Options}}). If the
27 | requested \code{\link[=Options]{option}} is not known, an error is thrown.
28 |
29 | The \code{\link[=set_option]{set_option()}} function returns void. It throws an error if the
30 | requested \code{\link[=Options]{option}} to be adjusted is not known.
31 |
32 | The \code{\link[=set_default_options]{set_default_options()}} function returns void. The
33 | \code{\link[base:options]{options}} set can be consulted via the \code{\link[base:options]{base::.Options}}
34 | list. See the \code{\link{Options}} \code{\link[R6:R6Class]{R6::R6}} class for more information on
35 | the default values set by this function.
36 | }
37 | \description{
38 | The \code{\link[=get_option]{get_option()}} function is a helper for retrieving the value of
39 | \code{\link{parabar}} \code{\link[base:options]{options}}. If the
40 | \code{\link[=Options]{option}} requested is not available in the session
41 | \code{\link[base:options]{base::.Options}} list, the corresponding default value set by the
42 | \code{\link{Options}} \code{\link[R6:R6Class]{R6::R6}} class is returned instead.
43 |
44 | The \code{\link[=set_option]{set_option()}} function is a helper for setting
45 | \code{\link{parabar}} \code{\link[base:options]{options}}. The function adjusts the
46 | fields of the \code{\link{Options}} instance stored in the \code{\link[base:options]{base::.Options}}
47 | list. If no \code{\link{Options}} instance is present in the
48 | \code{\link[base:options]{base::.Options}} list, a new one is created.
49 |
50 | The \code{\link[=set_default_options]{set_default_options()}} function is used to set the default
51 | \code{\link[base:options]{options}} values for the \code{\link{parabar}} package. The
52 | function is automatically called at package load and the entry created can be
53 | retrieved via \code{\link[base:options]{getOption("parabar")}}. Specific package
54 | \code{\link[base:options]{options}} can be retrieved using the helper function
55 | \code{\link[=get_option]{get_option()}}.
56 | }
57 | \examples{
58 | # Get the status of progress tracking.
59 | get_option("progress_track")
60 |
61 | # Set the status of progress tracking to `FALSE`.
62 | set_option("progress_track", FALSE)
63 |
64 | # Get the status of progress tracking again.
65 | get_option("progress_track")
66 |
67 | # Restore default options.
68 | set_default_options()
69 |
70 | # Get the status of progress tracking yet again.
71 | get_option("progress_track")
72 |
73 | }
74 | \seealso{
75 | \code{\link{Options}}, \code{\link[=set_default_options]{set_default_options()}}, \code{\link[base:options]{base::options()}},
76 | and \code{\link[base:options]{base::getOption()}}.
77 | }
78 |
--------------------------------------------------------------------------------
/man/par_lapply.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/exports.R
3 | \name{par_lapply}
4 | \alias{par_lapply}
5 | \title{Run a Task in Parallel}
6 | \usage{
7 | par_lapply(backend = NULL, x, fun, ...)
8 | }
9 | \arguments{
10 | \item{backend}{An object of class \code{\link{Backend}} as returned by the
11 | \code{\link[=start_backend]{start_backend()}} function. It can also be \code{NULL} to run the task
12 | sequentially via \code{\link[base:lapply]{base::lapply()}}. The default value is \code{NULL}.}
13 |
14 | \item{x}{An atomic vector or list to pass to the \code{fun} function.}
15 |
16 | \item{fun}{A function to apply to each element of \code{x}.}
17 |
18 | \item{...}{Additional arguments to pass to the \code{fun} function.}
19 | }
20 | \value{
21 | A list of the same length as \code{x} containing the results of the \code{fun}. The
22 | output format resembles that of \code{\link[base:lapply]{base::lapply()}}.
23 | }
24 | \description{
25 | This function can be used to run a task in parallel. The task is executed in
26 | parallel on the specified backend, similar to \code{\link[parallel:clusterApply]{parallel::parLapply()}}. If
27 | \code{backend = NULL}, the task is executed sequentially using \code{\link[base:lapply]{base::lapply()}}.
28 | See the \strong{Details} section for more information on how this function works.
29 | }
30 | \details{
31 | This function uses the \code{\link{UserApiConsumer}} class that acts like an
32 | interface for the developer API of the \code{\link{parabar}} package.
33 | }
34 | \examples{
35 | \donttest{
36 |
37 | # Define a simple task.
38 | task <- function(x) {
39 | # Perform computations.
40 | Sys.sleep(0.01)
41 |
42 | # Return the result.
43 | return(x + 1)
44 | }
45 |
46 | # Start an asynchronous backend.
47 | backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "async")
48 |
49 | # Run a task in parallel.
50 | results <- par_lapply(backend, x = 1:300, fun = task)
51 |
52 | # Disable progress tracking.
53 | set_option("progress_track", FALSE)
54 |
55 | # Run a task in parallel.
56 | results <- par_lapply(backend, x = 1:300, fun = task)
57 |
58 | # Enable progress tracking.
59 | set_option("progress_track", TRUE)
60 |
61 | # Change the progress bar options.
62 | configure_bar(type = "modern", format = "[:bar] :percent")
63 |
64 | # Run a task in parallel.
65 | results <- par_lapply(backend, x = 1:300, fun = task)
66 |
67 | # Stop the backend.
68 | stop_backend(backend)
69 |
70 | # Start a synchronous backend.
71 | backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "sync")
72 |
73 | # Run a task in parallel.
74 | results <- par_lapply(backend, x = 1:300, fun = task)
75 |
76 | # Disable progress tracking to remove the warning that progress is not supported.
77 | set_option("progress_track", FALSE)
78 |
79 | # Run a task in parallel.
80 | results <- par_lapply(backend, x = 1:300, fun = task)
81 |
82 | # Stop the backend.
83 | stop_backend(backend)
84 |
85 | # Run the task using the `base::lapply` (i.e., non-parallel).
86 | results <- par_lapply(NULL, x = 1:300, fun = task)
87 |
88 | }
89 |
90 | }
91 | \seealso{
92 | \code{\link[=start_backend]{start_backend()}}, \code{\link[=peek]{peek()}}, \code{\link[=export]{export()}},
93 | \code{\link[=evaluate]{evaluate()}}, \code{\link[=clear]{clear()}}, \code{\link[=configure_bar]{configure_bar()}},
94 | \code{\link[=par_sapply]{par_sapply()}}, \code{\link[=par_apply]{par_apply()}}, \code{\link[=stop_backend]{stop_backend()}},
95 | \code{\link[=set_option]{set_option()}}, \code{\link[=get_option]{get_option()}}, \code{\link{Options}},
96 | \code{\link{UserApiConsumer}}, and \code{\link{BackendService}}.
97 | }
98 |
--------------------------------------------------------------------------------
/man/par_sapply.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/exports.R
3 | \name{par_sapply}
4 | \alias{par_sapply}
5 | \title{Run a Task in Parallel}
6 | \usage{
7 | par_sapply(backend = NULL, x, fun, ...)
8 | }
9 | \arguments{
10 | \item{backend}{An object of class \code{\link{Backend}} as returned by the
11 | \code{\link[=start_backend]{start_backend()}} function. It can also be \code{NULL} to run the task
12 | sequentially via \code{\link[base:lapply]{base::sapply()}}. The default value is \code{NULL}.}
13 |
14 | \item{x}{An atomic vector or list to pass to the \code{fun} function.}
15 |
16 | \item{fun}{A function to apply to each element of \code{x}.}
17 |
18 | \item{...}{Additional arguments to pass to the \code{fun} function.}
19 | }
20 | \value{
21 | A vector of the same length as \code{x} containing the results of the \code{fun}. The
22 | output format resembles that of \code{\link[base:lapply]{base::sapply()}}.
23 | }
24 | \description{
25 | This function can be used to run a task in parallel. The task is executed in
26 | parallel on the specified backend, similar to \code{\link[parallel:clusterApply]{parallel::parSapply()}}. If
27 | \code{backend = NULL}, the task is executed sequentially using \code{\link[base:lapply]{base::sapply()}}.
28 | See the \strong{Details} section for more information on how this function works.
29 | }
30 | \details{
31 | This function uses the \code{\link{UserApiConsumer}} class that acts like an
32 | interface for the developer API of the \code{\link{parabar}} package.
33 | }
34 | \examples{
35 | \donttest{
36 |
37 | # Define a simple task.
38 | task <- function(x) {
39 | # Perform computations.
40 | Sys.sleep(0.01)
41 |
42 | # Return the result.
43 | return(x + 1)
44 | }
45 |
46 | # Start an asynchronous backend.
47 | backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "async")
48 |
49 | # Run a task in parallel.
50 | results <- par_sapply(backend, x = 1:300, fun = task)
51 |
52 | # Disable progress tracking.
53 | set_option("progress_track", FALSE)
54 |
55 | # Run a task in parallel.
56 | results <- par_sapply(backend, x = 1:300, fun = task)
57 |
58 | # Enable progress tracking.
59 | set_option("progress_track", TRUE)
60 |
61 | # Change the progress bar options.
62 | configure_bar(type = "modern", format = "[:bar] :percent")
63 |
64 | # Run a task in parallel.
65 | results <- par_sapply(backend, x = 1:300, fun = task)
66 |
67 | # Stop the backend.
68 | stop_backend(backend)
69 |
70 | # Start a synchronous backend.
71 | backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "sync")
72 |
73 | # Run a task in parallel.
74 | results <- par_sapply(backend, x = 1:300, fun = task)
75 |
76 | # Disable progress tracking to remove the warning that progress is not supported.
77 | set_option("progress_track", FALSE)
78 |
79 | # Run a task in parallel.
80 | results <- par_sapply(backend, x = 1:300, fun = task)
81 |
82 | # Stop the backend.
83 | stop_backend(backend)
84 |
85 | # Run the task using the `base::sapply` (i.e., non-parallel).
86 | results <- par_sapply(NULL, x = 1:300, fun = task)
87 |
88 | }
89 |
90 | }
91 | \seealso{
92 | \code{\link[=start_backend]{start_backend()}}, \code{\link[=peek]{peek()}}, \code{\link[=export]{export()}},
93 | \code{\link[=evaluate]{evaluate()}}, \code{\link[=clear]{clear()}}, \code{\link[=configure_bar]{configure_bar()}},
94 | \code{\link[=par_lapply]{par_lapply()}}, \code{\link[=par_apply]{par_apply()}}, \code{\link[=stop_backend]{stop_backend()}},
95 | \code{\link[=set_option]{set_option()}}, \code{\link[=get_option]{get_option()}}, \code{\link{Options}},
96 | \code{\link{UserApiConsumer}}, and \code{\link{BackendService}}.
97 | }
98 |
--------------------------------------------------------------------------------
/R/ModernBar.R:
--------------------------------------------------------------------------------
1 | #' @include Bar.R
2 |
3 | #' @title
4 | #' ModernBar
5 | #'
6 | #' @description
7 | #' This is a concrete implementation of the abstract class [`parabar::Bar`]
8 | #' using the [`progress::progress_bar`] as engine for the progress bar.
9 | #'
10 | #' @examples
11 | #' # Create a modern bar instance.
12 | #' bar <- ModernBar$new()
13 | #'
14 | #' # Specify the number of ticks to be performed.
15 | #' total <- 100
16 | #'
17 | #' # Create the progress bar.
18 | #' bar$create(total = total, initial = 0)
19 | #'
20 | #' # Use the progress bar.
21 | #' for (i in 1:total) {
22 | #' # Sleep a bit.
23 | #' Sys.sleep(0.02)
24 | #'
25 | #' # Update the progress bar.
26 | #' bar$update(i)
27 | #' }
28 | #'
29 | #' # Terminate the progress bar.
30 | #' bar$terminate()
31 | #'
32 | #' @seealso
33 | #' [`parabar::Bar`], [`parabar::BasicBar`], and [`parabar::BarFactory`].
34 | #'
35 | #' @export
36 | ModernBar <- R6::R6Class("ModernBar",
37 | inherit = Bar,
38 |
39 | private = list(
40 | .total = NULL,
41 |
42 | # Create bar.
43 | .create = function(total, initial, ...) {
44 | # Store the total ticks to be performed.
45 | private$.total <- total
46 |
47 | # Create and store the bar object.
48 | private$.bar <- do.call(
49 | progress::progress_bar$new,
50 | utils::modifyList(
51 | list(total = private$.total), list(...)
52 | )
53 | )
54 |
55 | # Perform the initial tick.
56 | private$.bar$tick(initial)
57 |
58 | # Stay silent.
59 | invisible()
60 | },
61 |
62 | # Update bar.
63 | .update = function(current) {
64 | # Perform the update.
65 | private$.bar$update(current / private$.total)
66 |
67 | # Stay silent.
68 | invisible()
69 | },
70 |
71 | # Terminate the bar.
72 | .terminate = function() {
73 | # Close the bar.
74 | private$.bar$terminate()
75 |
76 | # Stay silent.
77 | invisible()
78 | }
79 | ),
80 |
81 | public = list(
82 | #' @description
83 | #' Create a new [`parabar::ModernBar`] object.
84 | #'
85 | #' @return
86 | #' An object of class [`parabar::ModernBar`].
87 | initialize = function() {},
88 |
89 | #' @description
90 | #' Create a progress bar.
91 | #'
92 | #' @param total The total number of times the progress bar should tick.
93 | #'
94 | #' @param initial The starting point of the progress bar.
95 | #'
96 | #' @param ... Additional arguments for the bar creation passed to
97 | #' [`progress::progress_bar$new()`][`progress::progress_bar`].
98 | #'
99 | #' @return
100 | #' This method returns void. The resulting bar is stored in the private
101 | #' field `.bar`, accessible via the active binding `engine`. Both the
102 | #' private field and the active binding are defined in the super class
103 | #' [`parabar::Bar`].
104 | create = function(total, initial, ...) {
105 | private$.create(total, initial, ...)
106 | },
107 |
108 | #' @description
109 | #' Update the progress bar by calling
110 | #' [`progress::progress_bar$update()`][`progress::progress_bar`].
111 | #'
112 | #' @param current The position the progress bar should be at (e.g., 30
113 | #' out of 100), usually the index in a loop.
114 | update = function(current) {
115 | private$.update(current)
116 | },
117 |
118 | #' @description
119 | #' Terminate the progress bar by calling
120 | #' [`progress::progress_bar$terminate()`][`progress::progress_bar`].
121 | terminate = function() {
122 | private$.terminate()
123 | }
124 | )
125 | )
126 |
--------------------------------------------------------------------------------
/man/Bar.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/Bar.R
3 | \name{Bar}
4 | \alias{Bar}
5 | \title{Bar}
6 | \description{
7 | This is an abstract class that defines the pure virtual methods a concrete
8 | bar must implement.
9 | }
10 | \details{
11 | This class cannot be instantiated. It needs to be extended by concrete
12 | subclasses that implement the pure virtual methods. Instances of concrete
13 | backend implementations can be conveniently obtained using the
14 | \code{\link{BarFactory}} class.
15 | }
16 | \seealso{
17 | \code{\link{BasicBar}}, \code{\link{ModernBar}}, and \code{\link{BarFactory}}.
18 | }
19 | \section{Active bindings}{
20 | \if{html}{\out{}}
21 | \describe{
22 | \item{\code{engine}}{The bar engine.}
23 | }
24 | \if{html}{\out{
}}
25 | }
26 | \section{Methods}{
27 | \subsection{Public methods}{
28 | \itemize{
29 | \item \href{#method-Bar-new}{\code{Bar$new()}}
30 | \item \href{#method-Bar-create}{\code{Bar$create()}}
31 | \item \href{#method-Bar-update}{\code{Bar$update()}}
32 | \item \href{#method-Bar-terminate}{\code{Bar$terminate()}}
33 | \item \href{#method-Bar-clone}{\code{Bar$clone()}}
34 | }
35 | }
36 | \if{html}{\out{
}}
37 | \if{html}{\out{}}
38 | \if{latex}{\out{\hypertarget{method-Bar-new}{}}}
39 | \subsection{Method \code{new()}}{
40 | Create a new \code{\link{Bar}} object.
41 | \subsection{Usage}{
42 | \if{html}{\out{}}\preformatted{Bar$new()}\if{html}{\out{
}}
43 | }
44 |
45 | \subsection{Returns}{
46 | Instantiating this class will throw an error.
47 | }
48 | }
49 | \if{html}{\out{
}}
50 | \if{html}{\out{}}
51 | \if{latex}{\out{\hypertarget{method-Bar-create}{}}}
52 | \subsection{Method \code{create()}}{
53 | Create a progress bar.
54 | \subsection{Usage}{
55 | \if{html}{\out{}}\preformatted{Bar$create(total, initial, ...)}\if{html}{\out{
}}
56 | }
57 |
58 | \subsection{Arguments}{
59 | \if{html}{\out{}}
60 | \describe{
61 | \item{\code{total}}{The total number of times the progress bar should tick.}
62 |
63 | \item{\code{initial}}{The starting point of the progress bar.}
64 |
65 | \item{\code{...}}{Additional arguments for the bar creation. See the
66 | \strong{Details} section for more information.}
67 | }
68 | \if{html}{\out{
}}
69 | }
70 | \subsection{Details}{
71 | The optional \code{...} named arguments depend on the specific concrete
72 | implementation (i.e., \code{\link{BasicBar}} or
73 | \code{\link{ModernBar}}).
74 | }
75 |
76 | \subsection{Returns}{
77 | This method returns void. The resulting bar is stored in the private
78 | field \code{.bar}, accessible via the active binding \code{engine}.
79 | }
80 | }
81 | \if{html}{\out{
}}
82 | \if{html}{\out{}}
83 | \if{latex}{\out{\hypertarget{method-Bar-update}{}}}
84 | \subsection{Method \code{update()}}{
85 | Update the progress bar.
86 | \subsection{Usage}{
87 | \if{html}{\out{}}\preformatted{Bar$update(current)}\if{html}{\out{
}}
88 | }
89 |
90 | \subsection{Arguments}{
91 | \if{html}{\out{}}
92 | \describe{
93 | \item{\code{current}}{The position the progress bar should be at (e.g., 30
94 | out of 100), usually the index in a loop.}
95 | }
96 | \if{html}{\out{
}}
97 | }
98 | }
99 | \if{html}{\out{
}}
100 | \if{html}{\out{}}
101 | \if{latex}{\out{\hypertarget{method-Bar-terminate}{}}}
102 | \subsection{Method \code{terminate()}}{
103 | Terminate the progress bar.
104 | \subsection{Usage}{
105 | \if{html}{\out{}}\preformatted{Bar$terminate()}\if{html}{\out{
}}
106 | }
107 |
108 | }
109 | \if{html}{\out{
}}
110 | \if{html}{\out{}}
111 | \if{latex}{\out{\hypertarget{method-Bar-clone}{}}}
112 | \subsection{Method \code{clone()}}{
113 | The objects of this class are cloneable with this method.
114 | \subsection{Usage}{
115 | \if{html}{\out{}}\preformatted{Bar$clone(deep = FALSE)}\if{html}{\out{
}}
116 | }
117 |
118 | \subsection{Arguments}{
119 | \if{html}{\out{}}
120 | \describe{
121 | \item{\code{deep}}{Whether to make a deep clone.}
122 | }
123 | \if{html}{\out{
}}
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/man-roxygen/par-apply.R:
--------------------------------------------------------------------------------
1 | #' @title
2 | #' Run a Task in Parallel
3 | #'
4 | #' @description
5 | #' This function can be used to run a task in parallel. The task is executed in
6 | #' parallel on the specified backend, similar to [`parallel::parApply()`]. If
7 | #' `backend = NULL`, the task is executed sequentially using [`base::apply()`].
8 | #' See the **Details** section for more information on how this function works.
9 | #'
10 | #' @param backend An object of class [`parabar::Backend`] as returned by the
11 | #' [parabar::start_backend()] function. It can also be `NULL` to run the task
12 | #' sequentially via [`base::apply()`]. The default value is `NULL`.
13 | #'
14 | #' @param x An array to pass to the `fun` function.
15 | #'
16 | #' @param margin A numeric vector indicating the dimensions of `x` the
17 | #' `fun` function should be applied over. For example, for a matrix,
18 | #' `margin = 1` indicates applying `fun` rows-wise, `margin = 2`
19 | #' indicates applying `fun` columns-wise, and `margin = c(1, 2)`
20 | #' indicates applying `fun` element-wise. Named dimensions are also
21 | #' possible depending on `x`. See [`parallel::parApply()`] and
22 | #' [`base::apply()`] for more details.
23 | #'
24 | #' @param fun A function to apply to `x` according to the `margin`.
25 | #'
26 | #' @param ... Additional arguments to pass to the `fun` function.
27 | #'
28 | #' @details
29 | #' This function uses the [`parabar::UserApiConsumer`] class that acts like an
30 | #' interface for the developer API of the [`parabar::parabar`] package.
31 | #'
32 | #' @return
33 | #' The dimensions of the output vary according to the `margin` argument. Consult
34 | #' the documentation of [`base::apply()`] for a detailed explanation on how the
35 | #' output is structured.
36 | #'
37 | #' @examples
38 | #' \donttest{
39 | #'
40 | #' # Define a simple task.
41 | #' task <- function(x) {
42 | #' # Perform computations.
43 | #' Sys.sleep(0.01)
44 | #'
45 | #' # Return the result.
46 | #' mean(x)
47 | #' }
48 | #'
49 | #' # Define a matrix for the task.
50 | #' x <- matrix(rnorm(100^2, mean = 10, sd = 0.5), nrow = 100, ncol = 100)
51 | #'
52 | #' # Start an asynchronous backend.
53 | #' backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "async")
54 | #'
55 | #' # Run a task in parallel over the rows of `x`.
56 | #' results <- par_apply(backend, x = x, margin = 1, fun = task)
57 | #'
58 | #' # Run a task in parallel over the columns of `x`.
59 | #' results <- par_apply(backend, x = x, margin = 2, fun = task)
60 | #'
61 | #' # The task can also be run over all elements of `x` using `margin = c(1, 2)`.
62 | #' # Improper dimensions will throw an error.
63 | #' try(par_apply(backend, x = x, margin = c(1, 2, 3), fun = task))
64 | #'
65 | #' # Disable progress tracking.
66 | #' set_option("progress_track", FALSE)
67 | #'
68 | #' # Run a task in parallel.
69 | #' results <- par_apply(backend, x = x, margin = 1, fun = task)
70 | #'
71 | #' # Enable progress tracking.
72 | #' set_option("progress_track", TRUE)
73 | #'
74 | #' # Change the progress bar options.
75 | #' configure_bar(type = "modern", format = "[:bar] :percent")
76 | #'
77 | #' # Run a task in parallel.
78 | #' results <- par_apply(backend, x = x, margin = 1, fun = task)
79 | #'
80 | #' # Stop the backend.
81 | #' stop_backend(backend)
82 | #'
83 | #' # Start a synchronous backend.
84 | #' backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "sync")
85 | #'
86 | #' # Run a task in parallel.
87 | #' results <- par_apply(backend, x = x, margin = 1, fun = task)
88 | #'
89 | #' # Disable progress tracking to remove the warning that progress is not supported.
90 | #' set_option("progress_track", FALSE)
91 | #'
92 | #' # Run a task in parallel.
93 | #' results <- par_apply(backend, x = x, margin = 1, fun = task)
94 | #'
95 | #' # Stop the backend.
96 | #' stop_backend(backend)
97 | #'
98 | #' # Run the task using the `base::lapply` (i.e., non-parallel).
99 | #' results <- par_apply(NULL, x = x, margin = 1, fun = task)
100 | #'
101 | #' }
102 | #'
103 | #' @seealso
104 | #' [parabar::start_backend()], [parabar::peek()], [parabar::export()],
105 | #' [parabar::evaluate()], [parabar::clear()], [parabar::configure_bar()],
106 | #' [parabar::par_sapply()], [parabar::par_lapply()], [parabar::stop_backend()],
107 | #' [parabar::set_option()], [parabar::get_option()], [`parabar::Options`],
108 | #' [`parabar::UserApiConsumer`], and [`parabar::BackendService`].
109 |
--------------------------------------------------------------------------------
/man/Specification.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/Specification.R
3 | \name{Specification}
4 | \alias{Specification}
5 | \title{Specification}
6 | \description{
7 | This class contains the information required to start a backend. An instance
8 | of this class is used by the \code{start} method of the
9 | \code{\link{BackendService}} interface.
10 | }
11 | \examples{
12 | # Create a specification object.
13 | specification <- Specification$new()
14 |
15 | # Set the number of cores.
16 | specification$set_cores(cores = 4)
17 |
18 | # Set the cluster type.
19 | specification$set_type(type = "psock")
20 |
21 | # Get the number of cores.
22 | specification$cores
23 |
24 | # Get the cluster type.
25 | specification$type
26 |
27 | # Attempt to set too many cores.
28 | specification$set_cores(cores = 100)
29 |
30 | # Check that the cores were reasonably set.
31 | specification$cores
32 |
33 | # Allow the object to determine the adequate cluster type.
34 | specification$set_type(type = NULL)
35 |
36 | # Check the type determined.
37 | specification$type
38 |
39 | # Attempt to set an invalid cluster type.
40 | specification$set_type(type = "invalid")
41 |
42 | # Check that the type was set to `psock`.
43 | specification$type
44 |
45 | }
46 | \seealso{
47 | \code{\link{BackendService}}, \code{\link{Backend}}, \code{\link{SyncBackend}},
48 | and \code{\link{AsyncBackend}}.
49 | }
50 | \section{Active bindings}{
51 | \if{html}{\out{}}
52 | \describe{
53 | \item{\code{cores}}{The number of nodes to use in the cluster creation.}
54 |
55 | \item{\code{type}}{The type of cluster to create.}
56 |
57 | \item{\code{types}}{The supported cluster types.}
58 | }
59 | \if{html}{\out{
}}
60 | }
61 | \section{Methods}{
62 | \subsection{Public methods}{
63 | \itemize{
64 | \item \href{#method-Specification-set_cores}{\code{Specification$set_cores()}}
65 | \item \href{#method-Specification-set_type}{\code{Specification$set_type()}}
66 | \item \href{#method-Specification-clone}{\code{Specification$clone()}}
67 | }
68 | }
69 | \if{html}{\out{
}}
70 | \if{html}{\out{}}
71 | \if{latex}{\out{\hypertarget{method-Specification-set_cores}{}}}
72 | \subsection{Method \code{set_cores()}}{
73 | Set the number of nodes to use in the cluster.
74 | \subsection{Usage}{
75 | \if{html}{\out{}}\preformatted{Specification$set_cores(cores)}\if{html}{\out{
}}
76 | }
77 |
78 | \subsection{Arguments}{
79 | \if{html}{\out{}}
80 | \describe{
81 | \item{\code{cores}}{The number of nodes to use in the cluster.}
82 | }
83 | \if{html}{\out{
}}
84 | }
85 | \subsection{Details}{
86 | This method also performs a validation of the requested number of
87 | cores, ensuring that the the value lies between \code{1} and
88 | \code{parallel::detectCores() - 1}.
89 | }
90 |
91 | }
92 | \if{html}{\out{
}}
93 | \if{html}{\out{}}
94 | \if{latex}{\out{\hypertarget{method-Specification-set_type}{}}}
95 | \subsection{Method \code{set_type()}}{
96 | Set the type of cluster to create.
97 | \subsection{Usage}{
98 | \if{html}{\out{}}\preformatted{Specification$set_type(type)}\if{html}{\out{
}}
99 | }
100 |
101 | \subsection{Arguments}{
102 | \if{html}{\out{}}
103 | \describe{
104 | \item{\code{type}}{The type of cluster to create. Possible values are
105 | \code{"fork"} and \code{"psock"}. Defaults to \code{"psock"}.}
106 | }
107 | \if{html}{\out{
}}
108 | }
109 | \subsection{Details}{
110 | If no type is explicitly requested (i.e., \code{type = NULL}), the type is
111 | determined based on the operating system. On Unix-like systems, the
112 | type is set to \code{"fork"}, while on Windows systems, the type is set to
113 | \code{"psock"}. If an unknown type is requested, a warning is issued and
114 | the type is set to \code{"psock"}.
115 | }
116 |
117 | }
118 | \if{html}{\out{
}}
119 | \if{html}{\out{}}
120 | \if{latex}{\out{\hypertarget{method-Specification-clone}{}}}
121 | \subsection{Method \code{clone()}}{
122 | The objects of this class are cloneable with this method.
123 | \subsection{Usage}{
124 | \if{html}{\out{}}\preformatted{Specification$clone(deep = FALSE)}\if{html}{\out{
}}
125 | }
126 |
127 | \subsection{Arguments}{
128 | \if{html}{\out{}}
129 | \describe{
130 | \item{\code{deep}}{Whether to make a deep clone.}
131 | }
132 | \if{html}{\out{
}}
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/man/BasicBar.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/BasicBar.R
3 | \name{BasicBar}
4 | \alias{BasicBar}
5 | \title{BasicBar}
6 | \description{
7 | This is a concrete implementation of the abstract class \code{\link{Bar}}
8 | using the \code{\link[utils:txtProgressBar]{utils::txtProgressBar()}} as engine for the progress bar.
9 | }
10 | \examples{
11 | # Create a basic bar instance.
12 | bar <- BasicBar$new()
13 |
14 | # Specify the number of ticks to be performed.
15 | total <- 100
16 |
17 | # Create the progress bar.
18 | bar$create(total = total, initial = 0)
19 |
20 | # Use the progress bar.
21 | for (i in 1:total) {
22 | # Sleep a bit.
23 | Sys.sleep(0.02)
24 |
25 | # Update the progress bar.
26 | bar$update(i)
27 | }
28 |
29 | # Terminate the progress bar.
30 | bar$terminate()
31 |
32 | }
33 | \seealso{
34 | \code{\link{Bar}}, \code{\link{ModernBar}}, and \code{\link{BarFactory}}.
35 | }
36 | \section{Super class}{
37 | \code{\link[parabar:Bar]{parabar::Bar}} -> \code{BasicBar}
38 | }
39 | \section{Methods}{
40 | \subsection{Public methods}{
41 | \itemize{
42 | \item \href{#method-BasicBar-new}{\code{BasicBar$new()}}
43 | \item \href{#method-BasicBar-create}{\code{BasicBar$create()}}
44 | \item \href{#method-BasicBar-update}{\code{BasicBar$update()}}
45 | \item \href{#method-BasicBar-terminate}{\code{BasicBar$terminate()}}
46 | \item \href{#method-BasicBar-clone}{\code{BasicBar$clone()}}
47 | }
48 | }
49 | \if{html}{\out{
}}
50 | \if{html}{\out{}}
51 | \if{latex}{\out{\hypertarget{method-BasicBar-new}{}}}
52 | \subsection{Method \code{new()}}{
53 | Create a new \code{\link{BasicBar}} object.
54 | \subsection{Usage}{
55 | \if{html}{\out{}}\preformatted{BasicBar$new()}\if{html}{\out{
}}
56 | }
57 |
58 | \subsection{Returns}{
59 | An object of class \code{\link{BasicBar}}.
60 | }
61 | }
62 | \if{html}{\out{
}}
63 | \if{html}{\out{}}
64 | \if{latex}{\out{\hypertarget{method-BasicBar-create}{}}}
65 | \subsection{Method \code{create()}}{
66 | Create a progress bar.
67 | \subsection{Usage}{
68 | \if{html}{\out{}}\preformatted{BasicBar$create(total, initial, ...)}\if{html}{\out{
}}
69 | }
70 |
71 | \subsection{Arguments}{
72 | \if{html}{\out{}}
73 | \describe{
74 | \item{\code{total}}{The total number of times the progress bar should tick.}
75 |
76 | \item{\code{initial}}{The starting point of the progress bar.}
77 |
78 | \item{\code{...}}{Additional arguments for the bar creation passed to
79 | \code{\link[utils:txtProgressBar]{utils::txtProgressBar()}}.}
80 | }
81 | \if{html}{\out{
}}
82 | }
83 | \subsection{Returns}{
84 | This method returns void. The resulting bar is stored in the private
85 | field \code{.bar}, accessible via the active binding \code{engine}. Both the
86 | private field and the active binding are defined in the super class
87 | \code{\link{Bar}}.
88 | }
89 | }
90 | \if{html}{\out{
}}
91 | \if{html}{\out{}}
92 | \if{latex}{\out{\hypertarget{method-BasicBar-update}{}}}
93 | \subsection{Method \code{update()}}{
94 | Update the progress bar by calling \code{\link[utils:txtProgressBar]{utils::setTxtProgressBar()}}.
95 | \subsection{Usage}{
96 | \if{html}{\out{}}\preformatted{BasicBar$update(current)}\if{html}{\out{
}}
97 | }
98 |
99 | \subsection{Arguments}{
100 | \if{html}{\out{}}
101 | \describe{
102 | \item{\code{current}}{The position the progress bar should be at (e.g., 30
103 | out of 100), usually the index in a loop.}
104 | }
105 | \if{html}{\out{
}}
106 | }
107 | }
108 | \if{html}{\out{
}}
109 | \if{html}{\out{}}
110 | \if{latex}{\out{\hypertarget{method-BasicBar-terminate}{}}}
111 | \subsection{Method \code{terminate()}}{
112 | Terminate the progress bar by calling \code{\link[base:connections]{base::close()}} on the
113 | private field \code{.bar}.
114 | \subsection{Usage}{
115 | \if{html}{\out{}}\preformatted{BasicBar$terminate()}\if{html}{\out{
}}
116 | }
117 |
118 | }
119 | \if{html}{\out{
}}
120 | \if{html}{\out{}}
121 | \if{latex}{\out{\hypertarget{method-BasicBar-clone}{}}}
122 | \subsection{Method \code{clone()}}{
123 | The objects of this class are cloneable with this method.
124 | \subsection{Usage}{
125 | \if{html}{\out{}}\preformatted{BasicBar$clone(deep = FALSE)}\if{html}{\out{
}}
126 | }
127 |
128 | \subsection{Arguments}{
129 | \if{html}{\out{}}
130 | \describe{
131 | \item{\code{deep}}{Whether to make a deep clone.}
132 | }
133 | \if{html}{\out{
}}
134 | }
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/tests/testthat/test-async-backend.R:
--------------------------------------------------------------------------------
1 | # Test `AsyncBackend` class.
2 |
3 | test_that("'AsyncBackend' creates and manages clusters correctly", {
4 | # Create a specification.
5 | specification <- Specification$new()
6 |
7 | # Set the number of cores.
8 | specification$set_cores(cores = 2)
9 |
10 | # Pick a cluster type.
11 | cluster_type <- pick_cluster_type(specification$types)
12 |
13 | # Let the specification determine the cluster type.
14 | specification$set_type(type = cluster_type)
15 |
16 | # Create a synchronous backend object.
17 | backend <- AsyncBackend$new()
18 |
19 | # Expect the backend to support progress tracking.
20 | expect_true(backend$supports_progress)
21 |
22 | # Start the cluster on the backend.
23 | backend$start(specification)
24 |
25 | # Expect the cluster to be an object of `callr` class.
26 | expect_true(is(backend$cluster, "r_session"))
27 |
28 | # Expect that the cluster is of correct size.
29 | expect_equal(backend$cluster$run(function() length(cluster)), specification$cores)
30 |
31 | # Expect that the cluster is of correct type.
32 | switch(cluster_type,
33 | "psock" = expect_true(all(tolower(backend$cluster$run(function() summary(cluster))[, 2]) == "socknode")),
34 | "fork" = expect_true(all(tolower(backend$cluster$run(function() summary(cluster))[, 2]) == "forknode"))
35 | )
36 |
37 | # Test backend states.
38 | tests_set_for_backend_states(backend, specification)
39 |
40 | # Expect error attempting to get the task state for an inactive backend.
41 | expect_error(backend$task_state, as_text(Exception$cluster_not_active()))
42 |
43 | # Expect error attempting to get the session for an inactive backend.
44 | expect_error(backend$session_state, as_text(Exception$cluster_not_active()))
45 | })
46 |
47 |
48 | test_that("'AsyncBackend' stops busy clusters correctly", {
49 | # Create a specification.
50 | specification <- Specification$new()
51 |
52 | # Set the number of cores.
53 | specification$set_cores(cores = 2)
54 |
55 | # Pick a cluster type.
56 | cluster_type <- pick_cluster_type(specification$types)
57 |
58 | # Let the specification determine the cluster type.
59 | specification$set_type(type = cluster_type)
60 |
61 | # Create a synchronous backend object.
62 | backend <- AsyncBackend$new()
63 |
64 | # Start the cluster on the backend.
65 | backend$start(specification)
66 |
67 | # Disable forceful stopping.
68 | set_option("stop_forceful", FALSE)
69 |
70 | # Make sure the backend is busy.
71 | backend$sapply(1:100, function(x) {
72 | while (TRUE) { }
73 | })
74 |
75 | # Expect error when stopping a busy backend while a task is running.
76 | expect_error(backend$stop(), as_text(Exception$stop_busy_backend_not_allowed()))
77 |
78 | # Enable forceful stopping.
79 | set_option("stop_forceful", TRUE)
80 |
81 | # Stop the cluster on the backend.
82 | backend$stop()
83 |
84 | # Expect the backend to be inactive.
85 | expect_false(backend$active)
86 |
87 | # Start the cluster on the backend.
88 | backend$start(specification)
89 |
90 | # Disable forceful stopping.
91 | set_option("stop_forceful", FALSE)
92 |
93 | # Make sure the backend has unread results.
94 | backend$sapply(1:100, function(x) x)
95 |
96 | # Block the main session until the task is finished.
97 | block_until_async_task_finished(backend)
98 |
99 | # Expect error when stopping backend with unread task results.
100 | expect_error(backend$stop(), as_text(Exception$stop_busy_backend_not_allowed()))
101 |
102 | # Enable forceful stopping.
103 | set_option("stop_forceful", TRUE)
104 |
105 | # Stop the cluster on the backend.
106 | backend$stop()
107 |
108 | # Expect the backend to be inactive.
109 | expect_false(backend$active)
110 |
111 | # Restore the default options.
112 | set_default_options()
113 | })
114 |
115 |
116 | test_that("'AsyncBackend' performs operations on the cluster correctly", {
117 | # Create a specification.
118 | specification <- Specification$new()
119 |
120 | # Set the number of cores.
121 | specification$set_cores(cores = 2)
122 |
123 | # Determine the cluster type.
124 | cluster_type <- pick_cluster_type(specification$types)
125 |
126 | # Determine the cluster type automatically.
127 | specification$set_type(type = cluster_type)
128 |
129 | # Create an asynchronous backend object.
130 | backend <- AsyncBackend$new()
131 |
132 | # Perform tests for asynchronous backend operations.
133 | tests_set_for_asynchronous_backend_operations(backend, specification, test_task)
134 | })
135 |
--------------------------------------------------------------------------------
/man/ModernBar.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/ModernBar.R
3 | \name{ModernBar}
4 | \alias{ModernBar}
5 | \title{ModernBar}
6 | \description{
7 | This is a concrete implementation of the abstract class \code{\link{Bar}}
8 | using the \code{\link[progress:progress_bar]{progress::progress_bar}} as engine for the progress bar.
9 | }
10 | \examples{
11 | # Create a modern bar instance.
12 | bar <- ModernBar$new()
13 |
14 | # Specify the number of ticks to be performed.
15 | total <- 100
16 |
17 | # Create the progress bar.
18 | bar$create(total = total, initial = 0)
19 |
20 | # Use the progress bar.
21 | for (i in 1:total) {
22 | # Sleep a bit.
23 | Sys.sleep(0.02)
24 |
25 | # Update the progress bar.
26 | bar$update(i)
27 | }
28 |
29 | # Terminate the progress bar.
30 | bar$terminate()
31 |
32 | }
33 | \seealso{
34 | \code{\link{Bar}}, \code{\link{BasicBar}}, and \code{\link{BarFactory}}.
35 | }
36 | \section{Super class}{
37 | \code{\link[parabar:Bar]{parabar::Bar}} -> \code{ModernBar}
38 | }
39 | \section{Methods}{
40 | \subsection{Public methods}{
41 | \itemize{
42 | \item \href{#method-ModernBar-new}{\code{ModernBar$new()}}
43 | \item \href{#method-ModernBar-create}{\code{ModernBar$create()}}
44 | \item \href{#method-ModernBar-update}{\code{ModernBar$update()}}
45 | \item \href{#method-ModernBar-terminate}{\code{ModernBar$terminate()}}
46 | \item \href{#method-ModernBar-clone}{\code{ModernBar$clone()}}
47 | }
48 | }
49 | \if{html}{\out{
}}
50 | \if{html}{\out{}}
51 | \if{latex}{\out{\hypertarget{method-ModernBar-new}{}}}
52 | \subsection{Method \code{new()}}{
53 | Create a new \code{\link{ModernBar}} object.
54 | \subsection{Usage}{
55 | \if{html}{\out{}}\preformatted{ModernBar$new()}\if{html}{\out{
}}
56 | }
57 |
58 | \subsection{Returns}{
59 | An object of class \code{\link{ModernBar}}.
60 | }
61 | }
62 | \if{html}{\out{
}}
63 | \if{html}{\out{}}
64 | \if{latex}{\out{\hypertarget{method-ModernBar-create}{}}}
65 | \subsection{Method \code{create()}}{
66 | Create a progress bar.
67 | \subsection{Usage}{
68 | \if{html}{\out{}}\preformatted{ModernBar$create(total, initial, ...)}\if{html}{\out{
}}
69 | }
70 |
71 | \subsection{Arguments}{
72 | \if{html}{\out{}}
73 | \describe{
74 | \item{\code{total}}{The total number of times the progress bar should tick.}
75 |
76 | \item{\code{initial}}{The starting point of the progress bar.}
77 |
78 | \item{\code{...}}{Additional arguments for the bar creation passed to
79 | \code{\link[progress:progress_bar]{progress::progress_bar$new()}}.}
80 | }
81 | \if{html}{\out{
}}
82 | }
83 | \subsection{Returns}{
84 | This method returns void. The resulting bar is stored in the private
85 | field \code{.bar}, accessible via the active binding \code{engine}. Both the
86 | private field and the active binding are defined in the super class
87 | \code{\link{Bar}}.
88 | }
89 | }
90 | \if{html}{\out{
}}
91 | \if{html}{\out{}}
92 | \if{latex}{\out{\hypertarget{method-ModernBar-update}{}}}
93 | \subsection{Method \code{update()}}{
94 | Update the progress bar by calling
95 | \code{\link[progress:progress_bar]{progress::progress_bar$update()}}.
96 | \subsection{Usage}{
97 | \if{html}{\out{}}\preformatted{ModernBar$update(current)}\if{html}{\out{
}}
98 | }
99 |
100 | \subsection{Arguments}{
101 | \if{html}{\out{}}
102 | \describe{
103 | \item{\code{current}}{The position the progress bar should be at (e.g., 30
104 | out of 100), usually the index in a loop.}
105 | }
106 | \if{html}{\out{
}}
107 | }
108 | }
109 | \if{html}{\out{
}}
110 | \if{html}{\out{}}
111 | \if{latex}{\out{\hypertarget{method-ModernBar-terminate}{}}}
112 | \subsection{Method \code{terminate()}}{
113 | Terminate the progress bar by calling
114 | \code{\link[progress:progress_bar]{progress::progress_bar$terminate()}}.
115 | \subsection{Usage}{
116 | \if{html}{\out{}}\preformatted{ModernBar$terminate()}\if{html}{\out{
}}
117 | }
118 |
119 | }
120 | \if{html}{\out{
}}
121 | \if{html}{\out{}}
122 | \if{latex}{\out{\hypertarget{method-ModernBar-clone}{}}}
123 | \subsection{Method \code{clone()}}{
124 | The objects of this class are cloneable with this method.
125 | \subsection{Usage}{
126 | \if{html}{\out{}}\preformatted{ModernBar$clone(deep = FALSE)}\if{html}{\out{
}}
127 | }
128 |
129 | \subsection{Arguments}{
130 | \if{html}{\out{}}
131 | \describe{
132 | \item{\code{deep}}{Whether to make a deep clone.}
133 | }
134 | \if{html}{\out{
}}
135 | }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/man/par_apply.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/exports.R
3 | \name{par_apply}
4 | \alias{par_apply}
5 | \title{Run a Task in Parallel}
6 | \usage{
7 | par_apply(backend = NULL, x, margin, fun, ...)
8 | }
9 | \arguments{
10 | \item{backend}{An object of class \code{\link{Backend}} as returned by the
11 | \code{\link[=start_backend]{start_backend()}} function. It can also be \code{NULL} to run the task
12 | sequentially via \code{\link[base:apply]{base::apply()}}. The default value is \code{NULL}.}
13 |
14 | \item{x}{An array to pass to the \code{fun} function.}
15 |
16 | \item{margin}{A numeric vector indicating the dimensions of \code{x} the
17 | \code{fun} function should be applied over. For example, for a matrix,
18 | \code{margin = 1} indicates applying \code{fun} rows-wise, \code{margin = 2}
19 | indicates applying \code{fun} columns-wise, and \code{margin = c(1, 2)}
20 | indicates applying \code{fun} element-wise. Named dimensions are also
21 | possible depending on \code{x}. See \code{\link[parallel:clusterApply]{parallel::parApply()}} and
22 | \code{\link[base:apply]{base::apply()}} for more details.}
23 |
24 | \item{fun}{A function to apply to \code{x} according to the \code{margin}.}
25 |
26 | \item{...}{Additional arguments to pass to the \code{fun} function.}
27 | }
28 | \value{
29 | The dimensions of the output vary according to the \code{margin} argument. Consult
30 | the documentation of \code{\link[base:apply]{base::apply()}} for a detailed explanation on how the
31 | output is structured.
32 | }
33 | \description{
34 | This function can be used to run a task in parallel. The task is executed in
35 | parallel on the specified backend, similar to \code{\link[parallel:clusterApply]{parallel::parApply()}}. If
36 | \code{backend = NULL}, the task is executed sequentially using \code{\link[base:apply]{base::apply()}}.
37 | See the \strong{Details} section for more information on how this function works.
38 | }
39 | \details{
40 | This function uses the \code{\link{UserApiConsumer}} class that acts like an
41 | interface for the developer API of the \code{\link{parabar}} package.
42 | }
43 | \examples{
44 | \donttest{
45 |
46 | # Define a simple task.
47 | task <- function(x) {
48 | # Perform computations.
49 | Sys.sleep(0.01)
50 |
51 | # Return the result.
52 | mean(x)
53 | }
54 |
55 | # Define a matrix for the task.
56 | x <- matrix(rnorm(100^2, mean = 10, sd = 0.5), nrow = 100, ncol = 100)
57 |
58 | # Start an asynchronous backend.
59 | backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "async")
60 |
61 | # Run a task in parallel over the rows of `x`.
62 | results <- par_apply(backend, x = x, margin = 1, fun = task)
63 |
64 | # Run a task in parallel over the columns of `x`.
65 | results <- par_apply(backend, x = x, margin = 2, fun = task)
66 |
67 | # The task can also be run over all elements of `x` using `margin = c(1, 2)`.
68 | # Improper dimensions will throw an error.
69 | try(par_apply(backend, x = x, margin = c(1, 2, 3), fun = task))
70 |
71 | # Disable progress tracking.
72 | set_option("progress_track", FALSE)
73 |
74 | # Run a task in parallel.
75 | results <- par_apply(backend, x = x, margin = 1, fun = task)
76 |
77 | # Enable progress tracking.
78 | set_option("progress_track", TRUE)
79 |
80 | # Change the progress bar options.
81 | configure_bar(type = "modern", format = "[:bar] :percent")
82 |
83 | # Run a task in parallel.
84 | results <- par_apply(backend, x = x, margin = 1, fun = task)
85 |
86 | # Stop the backend.
87 | stop_backend(backend)
88 |
89 | # Start a synchronous backend.
90 | backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "sync")
91 |
92 | # Run a task in parallel.
93 | results <- par_apply(backend, x = x, margin = 1, fun = task)
94 |
95 | # Disable progress tracking to remove the warning that progress is not supported.
96 | set_option("progress_track", FALSE)
97 |
98 | # Run a task in parallel.
99 | results <- par_apply(backend, x = x, margin = 1, fun = task)
100 |
101 | # Stop the backend.
102 | stop_backend(backend)
103 |
104 | # Run the task using the `base::lapply` (i.e., non-parallel).
105 | results <- par_apply(NULL, x = x, margin = 1, fun = task)
106 |
107 | }
108 |
109 | }
110 | \seealso{
111 | \code{\link[=start_backend]{start_backend()}}, \code{\link[=peek]{peek()}}, \code{\link[=export]{export()}},
112 | \code{\link[=evaluate]{evaluate()}}, \code{\link[=clear]{clear()}}, \code{\link[=configure_bar]{configure_bar()}},
113 | \code{\link[=par_sapply]{par_sapply()}}, \code{\link[=par_lapply]{par_lapply()}}, \code{\link[=stop_backend]{stop_backend()}},
114 | \code{\link[=set_option]{set_option()}}, \code{\link[=get_option]{get_option()}}, \code{\link{Options}},
115 | \code{\link{UserApiConsumer}}, and \code{\link{BackendService}}.
116 | }
117 |
--------------------------------------------------------------------------------
/man/Backend.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/Backend.R
3 | \name{Backend}
4 | \alias{Backend}
5 | \title{Backend}
6 | \description{
7 | This is an abstract class that serves as a base class for all concrete
8 | backend implementations. It defines the common properties that all concrete
9 | backends require.
10 | }
11 | \details{
12 | This class cannot be instantiated. It needs to be extended by concrete
13 | subclasses that implement the pure virtual methods. Instances of concrete
14 | backend implementations can be conveniently obtained using the
15 | \code{\link{BackendFactory}} class.
16 | }
17 | \seealso{
18 | \code{\link{BackendService}}, \code{\link{SyncBackend}},
19 | \code{\link{AsyncBackend}}, \code{\link{BackendFactory}}, and
20 | \code{\link{Context}}.
21 | }
22 | \section{Super class}{
23 | \code{\link[parabar:BackendService]{parabar::BackendService}} -> \code{Backend}
24 | }
25 | \section{Active bindings}{
26 | \if{html}{\out{}}
27 | \describe{
28 | \item{\code{cluster}}{The cluster object used by the backend. For
29 | \code{\link{SyncBackend}} objects, this is a cluster object created by
30 | \code{\link[parallel:makeCluster]{parallel::makeCluster()}}. For \code{\link{AsyncBackend}} objects,
31 | this is a permanent \code{R} session created by \code{\link[callr:r_session]{callr::r_session}} that
32 | contains the \code{\link[parallel:makeCluster]{parallel::makeCluster()}} cluster object.}
33 |
34 | \item{\code{supports_progress}}{A boolean value indicating whether the
35 | backend implementation supports progress tracking.}
36 |
37 | \item{\code{active}}{A boolean value indicating whether the backend
38 | implementation has an active cluster.}
39 | }
40 | \if{html}{\out{
}}
41 | }
42 | \section{Methods}{
43 | \subsection{Public methods}{
44 | \itemize{
45 | \item \href{#method-Backend-new}{\code{Backend$new()}}
46 | \item \href{#method-Backend-clone}{\code{Backend$clone()}}
47 | }
48 | }
49 | \if{html}{\out{
50 | Inherited methods
51 |
63 |
64 | }}
65 | \if{html}{\out{
}}
66 | \if{html}{\out{}}
67 | \if{latex}{\out{\hypertarget{method-Backend-new}{}}}
68 | \subsection{Method \code{new()}}{
69 | Create a new \code{\link{Backend}} object.
70 | \subsection{Usage}{
71 | \if{html}{\out{}}\preformatted{Backend$new()}\if{html}{\out{
}}
72 | }
73 |
74 | \subsection{Returns}{
75 | Instantiating this class will throw an error.
76 | }
77 | }
78 | \if{html}{\out{
}}
79 | \if{html}{\out{}}
80 | \if{latex}{\out{\hypertarget{method-Backend-clone}{}}}
81 | \subsection{Method \code{clone()}}{
82 | The objects of this class are cloneable with this method.
83 | \subsection{Usage}{
84 | \if{html}{\out{}}\preformatted{Backend$clone(deep = FALSE)}\if{html}{\out{
}}
85 | }
86 |
87 | \subsection{Arguments}{
88 | \if{html}{\out{}}
89 | \describe{
90 | \item{\code{deep}}{Whether to make a deep clone.}
91 | }
92 | \if{html}{\out{
}}
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/man-roxygen/start-backend.R:
--------------------------------------------------------------------------------
1 | #' @title
2 | #' Start a Backend
3 | #'
4 | #' @description
5 | #' This function can be used to start a backend. Check out the **Details**
6 | #' section for more information.
7 | #'
8 | #' @param cores A positive integer representing the number of cores to use
9 | #' (i.e., the number of processes to start). This value must be between `2` and
10 | #' `parallel::detectCores() - 1`.
11 | #'
12 | #' @param cluster_type A character string representing the type of cluster to
13 | #' create. Possible values are `"fork"` and `"psock"`. Defaults to `"psock"`.
14 | #' See the section **Cluster Type** for more information.
15 | #'
16 | #' @param backend_type A character string representing the type of backend to
17 | #' create. Possible values are `"sync"` and `"async"`. Defaults to `"async"`.
18 | #' See the section **Backend Type** for more information.
19 | #'
20 | #' @details
21 | #' This function is a convenience wrapper around the lower-lever API of
22 | #' [`parabar::parabar`] aimed at developers. More specifically, this function
23 | #' uses the [`parabar::Specification`] class to create a specification object,
24 | #' and the [`parabar::BackendFactory`] class to create a [`parabar::Backend`]
25 | #' instance based on the specification object.
26 | #'
27 | #' @section Cluster Type:
28 | #' The cluster type determines the type of cluster to create. The requested
29 | #' value is validated and passed to the `type` argument of the
30 | #' [parallel::makeCluster()] function. The following table lists the possible
31 | #' values and their corresponding description.
32 | #'
33 | #' | **Cluster** | **Description** |
34 | #' | :---------- | :------------------------- |
35 | #' | `"fork"` | For Unix-based systems. |
36 | #' | `"psock"` | For Windows-based systems. |
37 | #'
38 | #' @section Backend Type:
39 | #' The backend type determines the type of backend to create. The requested
40 | #' value is passed to the [`parabar::BackendFactory`] class, which returns a
41 | #' [`parabar::Backend`] instance of the desired type. The following table lists
42 | #' the possible backend types and their corresponding description.
43 | #'
44 | #' | **Backend** | **Description** | **Implementation** | **Progress** |
45 | #' | :---------- | :----------------------- | :------------------------ | :----------: |
46 | #' | `"sync"` | A synchronous backend. | [`parabar::SyncBackend`] | no |
47 | #' | `"async"` | An asynchronous backend. | [`parabar::AsyncBackend`] | yes |
48 | #'
49 | #' In a nutshell, the difference between the two backend types is that for the
50 | #' synchronous backend the cluster is created in the main process, while for the
51 | #' asynchronous backend the cluster is created in a backend `R` process using
52 | #' [`callr::r_session`]. Therefore, the synchronous backend is blocking the main
53 | #' process during task execution, while the asynchronous backend is
54 | #' non-blocking. Check out the implementations listed in the table above for
55 | #' more information. All concrete implementations extend the
56 | #' [`parabar::Backend`] abstract class and implement the
57 | #' [`parabar::BackendService`] interface.
58 | #'
59 | #' @return
60 | #' A [`parabar::Backend`] instance that can be used to parallelize computations.
61 | #' The methods available on the [`parabar::Backend`] instance are defined by the
62 | #' [`parabar::BackendService`] interface.
63 | #'
64 | #' @examples
65 | #' # Create an asynchronous backend.
66 | #' backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "async")
67 | #'
68 | #' # Check that the backend is active.
69 | #' backend$active
70 | #'
71 | #' # Check if there is anything on the backend.
72 | #' peek(backend)
73 | #'
74 | #' # Create a dummy variable.
75 | #' name <- "parabar"
76 | #'
77 | #' # Export the `name` variable in the current environment to the backend.
78 | #' export(backend, "name", environment())
79 | #'
80 | #' # Remove the dummy variable from the current environment.
81 | #' rm(name)
82 | #'
83 | #' # Check the backend to see that the variable has been exported.
84 | #' peek(backend)
85 | #'
86 | #' # Run an expression on the backend.
87 | #' # Note that the symbols in the expression are resolved on the backend.
88 | #' evaluate(backend, {
89 | #' # Print the name.
90 | #' print(paste0("Hello, ", name, "!"))
91 | #' })
92 | #'
93 | #' # Clear the backend.
94 | #' clear(backend)
95 | #'
96 | #' # Check that there is nothing on the backend.
97 | #' peek(backend)
98 | #'
99 | #' # Use a basic progress bar (i.e., see `parabar::Bar`).
100 | #' configure_bar(type = "basic", style = 3)
101 | #'
102 | #' # Run a task in parallel (i.e., approx. 1.25 seconds).
103 | #' output <- par_sapply(backend, x = 1:10, fun = function(x) {
104 | #' # Sleep a bit.
105 | #' Sys.sleep(0.25)
106 | #'
107 | #' # Compute and return.
108 | #' return(x + 1)
109 | #' })
110 | #'
111 | #' # Print the output.
112 | #' print(output)
113 | #'
114 | #' # Stop the backend.
115 | #' stop_backend(backend)
116 | #'
117 | #' # Check that the backend is not active.
118 | #' backend$active
119 | #'
120 | #' @seealso
121 | #' [parabar::peek()], [parabar::export()], [parabar::evaluate()],
122 | #' [parabar::clear()], [parabar::configure_bar()], [parabar::par_sapply()],
123 | #' [parabar::par_lapply()], [parabar::par_apply()], [parabar::stop_backend()],
124 | #' and [`parabar::BackendService`].
125 |
--------------------------------------------------------------------------------
/man/Options.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/Options.R
3 | \name{Options}
4 | \alias{Options}
5 | \title{Class for Package Options}
6 | \description{
7 | This class holds public fields that represent the package
8 | \code{\link[base:options]{options}} used to configure the default behavior of the
9 | functionality \code{\link{parabar}} provides.
10 | }
11 | \details{
12 | An instance of this class is automatically created and stored in the session
13 | \code{\link[base:options]{base::.Options}} at load time. This instance can be accessed and changed
14 | via \code{\link[base:options]{getOption("parabar")}}. Specific package
15 | \code{\link[base:options]{options}} can be retrieved using the helper function
16 | \code{\link[=get_option]{get_option()}}.
17 | }
18 | \examples{
19 | # Set the default package options (i.e., automatically set at load time).
20 | set_default_options()
21 |
22 | # First, get the options instance from the session options.
23 | parabar <- getOption("parabar")
24 |
25 | # Then, disable progress tracking.
26 | parabar$progress_track <- FALSE
27 |
28 | # Check that the change was applied (i.e., `progress_track: FALSE`).
29 | getOption("parabar")
30 |
31 | # To restore defaults, set the default options again.
32 | set_default_options()
33 |
34 | # Check that the change was applied (i.e., `progress_track: TRUE`).
35 | getOption("parabar")
36 |
37 | # We can also use the built-in helpers to get and set options more conveniently.
38 |
39 | # Get the progress tracking option.
40 | get_option("progress_track")
41 |
42 | # Set the progress tracking option to `FALSE`.
43 | set_option("progress_track", FALSE)
44 |
45 | # Check that the change was applied (i.e., `progress_track: FALSE`).
46 | get_option("progress_track")
47 |
48 | # Get a temporary file for logging the progress.
49 | get_option("progress_log_path")
50 |
51 | # Fix the logging file path.
52 | set_option("progress_log_path", "./progress.log")
53 |
54 | # Check that the logging path change was applied.
55 | get_option("progress_log_path")
56 |
57 | # Restore the logging path to the default behavior.
58 | set_option("progress_log_path", NULL)
59 |
60 | # Check that the logging path change was applied.
61 | get_option("progress_log_path")
62 |
63 | # Restore the defaults.
64 | set_default_options()
65 |
66 | }
67 | \seealso{
68 | \code{\link[=get_option]{get_option()}}, \code{\link[=set_option]{set_option()}}, and
69 | \code{\link[=set_default_options]{set_default_options()}}.
70 | }
71 | \section{Public fields}{
72 | \if{html}{\out{}}
73 | \describe{
74 | \item{\code{progress_track}}{A logical value indicating whether progress
75 | tracking should be enabled (i.e., \code{TRUE}) or disabled (i.e.,
76 | \code{FALSE}) globally for compatible backends. The default value is
77 | \code{TRUE}.}
78 |
79 | \item{\code{progress_timeout}}{A numeric value indicating the timeout (i.e.,
80 | in seconds) between subsequent checks of the log file for new
81 | progress records. The default value is \code{0.001}.}
82 |
83 | \item{\code{progress_wait}}{A numeric value indicating the approximate
84 | duration (i.e., in seconds) to wait between progress bar updates
85 | before checking if the task has finished (i.e., possibly with an
86 | error). The default value is \code{0.1}.}
87 |
88 | \item{\code{progress_bar_type}}{A character string indicating the default
89 | bar type to use with compatible backends. Possible values are
90 | \code{"modern"} (the default) or \code{"basic"}.}
91 |
92 | \item{\code{progress_bar_config}}{A list of lists containing the default bar
93 | configuration for each supported bar engine. Elements of these lists
94 | represent arguments for the corresponding bar engines. Currently, the
95 | supported bar engines are:
96 | \itemize{
97 | \item \code{modern}: The \code{\link[progress:progress_bar]{progress::progress_bar}} engine, with the following
98 | default configuration:
99 | \itemize{
100 | \item \code{show_after = 0}
101 | \item \code{format = "> completed :current out of :total tasks [:percent] [:elapsed]"}
102 | }
103 | \item \code{basic}: The \code{\link[utils:txtProgressBar]{utils::txtProgressBar}} engine, with no default
104 | configuration.
105 | }}
106 |
107 | \item{\code{stop_forceful}}{A logical value indicating whether to allow
108 | stopping an asynchronous backend forcefully (i.e., \code{TRUE}), or not
109 | (i.e., \code{FALSE}). When stopping forcefully, the backend is terminated
110 | without waiting for a running tasks to finish or for the results to
111 | be read into the main \code{R} session. The default value is \code{FALSE}.}
112 | }
113 | \if{html}{\out{
}}
114 | }
115 | \section{Active bindings}{
116 | \if{html}{\out{}}
117 | \describe{
118 | \item{\code{progress_log_path}}{A character string indicating the path to
119 | the log file where to track the execution progress of a running task.
120 | The default value is a temporary file generated by
121 | \code{\link[base:tempfile]{base::tempfile()}}. Calling this active binding repeatedly will
122 | yield different temporary file paths. Fixing the path to a specific
123 | value is possible by setting this active binding to a character
124 | string representing the desired path. Setting this active binding to
125 | \code{NULL} will reset it to the default value (i.e., yielding different
126 | temporary file paths).}
127 | }
128 | \if{html}{\out{
}}
129 | }
130 |
--------------------------------------------------------------------------------
/man/start_backend.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/exports.R
3 | \name{start_backend}
4 | \alias{start_backend}
5 | \title{Start a Backend}
6 | \usage{
7 | start_backend(cores, cluster_type = "psock", backend_type = "async")
8 | }
9 | \arguments{
10 | \item{cores}{A positive integer representing the number of cores to use
11 | (i.e., the number of processes to start). This value must be between \code{2} and
12 | \code{parallel::detectCores() - 1}.}
13 |
14 | \item{cluster_type}{A character string representing the type of cluster to
15 | create. Possible values are \code{"fork"} and \code{"psock"}. Defaults to \code{"psock"}.
16 | See the section \strong{Cluster Type} for more information.}
17 |
18 | \item{backend_type}{A character string representing the type of backend to
19 | create. Possible values are \code{"sync"} and \code{"async"}. Defaults to \code{"async"}.
20 | See the section \strong{Backend Type} for more information.}
21 | }
22 | \value{
23 | A \code{\link{Backend}} instance that can be used to parallelize computations.
24 | The methods available on the \code{\link{Backend}} instance are defined by the
25 | \code{\link{BackendService}} interface.
26 | }
27 | \description{
28 | This function can be used to start a backend. Check out the \strong{Details}
29 | section for more information.
30 | }
31 | \details{
32 | This function is a convenience wrapper around the lower-lever API of
33 | \code{\link{parabar}} aimed at developers. More specifically, this function
34 | uses the \code{\link{Specification}} class to create a specification object,
35 | and the \code{\link{BackendFactory}} class to create a \code{\link{Backend}}
36 | instance based on the specification object.
37 | }
38 | \section{Cluster Type}{
39 |
40 | The cluster type determines the type of cluster to create. The requested
41 | value is validated and passed to the \code{type} argument of the
42 | \code{\link[parallel:makeCluster]{parallel::makeCluster()}} function. The following table lists the possible
43 | values and their corresponding description.\tabular{ll}{
44 | \strong{Cluster} \tab \strong{Description} \cr
45 | \code{"fork"} \tab For Unix-based systems. \cr
46 | \code{"psock"} \tab For Windows-based systems. \cr
47 | }
48 | }
49 |
50 | \section{Backend Type}{
51 |
52 | The backend type determines the type of backend to create. The requested
53 | value is passed to the \code{\link{BackendFactory}} class, which returns a
54 | \code{\link{Backend}} instance of the desired type. The following table lists
55 | the possible backend types and their corresponding description.\tabular{lllc}{
56 | \strong{Backend} \tab \strong{Description} \tab \strong{Implementation} \tab \strong{Progress} \cr
57 | \code{"sync"} \tab A synchronous backend. \tab \code{\link{SyncBackend}} \tab no \cr
58 | \code{"async"} \tab An asynchronous backend. \tab \code{\link{AsyncBackend}} \tab yes \cr
59 | }
60 |
61 |
62 | In a nutshell, the difference between the two backend types is that for the
63 | synchronous backend the cluster is created in the main process, while for the
64 | asynchronous backend the cluster is created in a backend \code{R} process using
65 | \code{\link[callr:r_session]{callr::r_session}}. Therefore, the synchronous backend is blocking the main
66 | process during task execution, while the asynchronous backend is
67 | non-blocking. Check out the implementations listed in the table above for
68 | more information. All concrete implementations extend the
69 | \code{\link{Backend}} abstract class and implement the
70 | \code{\link{BackendService}} interface.
71 | }
72 |
73 | \examples{
74 | # Create an asynchronous backend.
75 | backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "async")
76 |
77 | # Check that the backend is active.
78 | backend$active
79 |
80 | # Check if there is anything on the backend.
81 | peek(backend)
82 |
83 | # Create a dummy variable.
84 | name <- "parabar"
85 |
86 | # Export the `name` variable in the current environment to the backend.
87 | export(backend, "name", environment())
88 |
89 | # Remove the dummy variable from the current environment.
90 | rm(name)
91 |
92 | # Check the backend to see that the variable has been exported.
93 | peek(backend)
94 |
95 | # Run an expression on the backend.
96 | # Note that the symbols in the expression are resolved on the backend.
97 | evaluate(backend, {
98 | # Print the name.
99 | print(paste0("Hello, ", name, "!"))
100 | })
101 |
102 | # Clear the backend.
103 | clear(backend)
104 |
105 | # Check that there is nothing on the backend.
106 | peek(backend)
107 |
108 | # Use a basic progress bar (i.e., see `parabar::Bar`).
109 | configure_bar(type = "basic", style = 3)
110 |
111 | # Run a task in parallel (i.e., approx. 1.25 seconds).
112 | output <- par_sapply(backend, x = 1:10, fun = function(x) {
113 | # Sleep a bit.
114 | Sys.sleep(0.25)
115 |
116 | # Compute and return.
117 | return(x + 1)
118 | })
119 |
120 | # Print the output.
121 | print(output)
122 |
123 | # Stop the backend.
124 | stop_backend(backend)
125 |
126 | # Check that the backend is not active.
127 | backend$active
128 |
129 | }
130 | \seealso{
131 | \code{\link[=peek]{peek()}}, \code{\link[=export]{export()}}, \code{\link[=evaluate]{evaluate()}},
132 | \code{\link[=clear]{clear()}}, \code{\link[=configure_bar]{configure_bar()}}, \code{\link[=par_sapply]{par_sapply()}},
133 | \code{\link[=par_lapply]{par_lapply()}}, \code{\link[=par_apply]{par_apply()}}, \code{\link[=stop_backend]{stop_backend()}},
134 | and \code{\link{BackendService}}.
135 | }
136 |
--------------------------------------------------------------------------------
/man/SessionState.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/SessionState.R
3 | \name{SessionState}
4 | \alias{SessionState}
5 | \title{SessionState}
6 | \description{
7 | This class holds the state of a background \code{\link[callr:r_session]{session}}
8 | used by an asynchronous backend (i.e., \code{\link{AsyncBackend}}). See the
9 | \strong{Details} section for more information.
10 | }
11 | \details{
12 | The session state is useful to check if an asynchronous backend is ready for
13 | certain operations. A session can only be in one of the following four states
14 | at a time:
15 | \itemize{
16 | \item \code{session_is_starting}: When \code{TRUE}, it indicates that the session is
17 | starting.
18 | \item \code{session_is_idle}: When \code{TRUE}, it indicates that the session is idle and
19 | ready to execute operations.
20 | \item \code{session_is_busy}: When \code{TRUE}, it indicates that the session is busy
21 | (i.e., see the \code{\link{TaskState}} class for more information about a
22 | task's state).
23 | \item \code{session_is_finished}: When \code{TRUE}, it indicates that the session is closed
24 | and no longer available for operations.
25 | }
26 | }
27 | \examples{
28 | # Handy function to print the session states all at once.
29 | check_state <- function(session) {
30 | # Create a session object and determine its state.
31 | session_state <- SessionState$new(session)
32 |
33 | # Print the state.
34 | cat(
35 | "Session is starting: ", session_state$session_is_starting, "\n",
36 | "Session is idle: ", session_state$session_is_idle, "\n",
37 | "Session is busy: ", session_state$session_is_busy, "\n",
38 | "Session is finished: ", session_state$session_is_finished, "\n",
39 | sep = ""
40 | )
41 | }
42 |
43 | # Create a specification object.
44 | specification <- Specification$new()
45 |
46 | # Set the number of cores.
47 | specification$set_cores(cores = 2)
48 |
49 | # Set the cluster type.
50 | specification$set_type(type = "psock")
51 |
52 | # Create an asynchronous backend object.
53 | backend <- AsyncBackend$new()
54 |
55 | # Start the cluster on the backend.
56 | backend$start(specification)
57 |
58 | # Check that the session is idle.
59 | check_state(backend$cluster)
60 |
61 | {
62 | # Run a task in parallel (i.e., approx. 0.25 seconds).
63 | backend$sapply(
64 | x = 1:10,
65 | fun = function(x) {
66 | # Sleep a bit.
67 | Sys.sleep(0.05)
68 |
69 | # Compute something.
70 | output <- x + 1
71 |
72 | # Return the result.
73 | return(output)
74 | }
75 | )
76 |
77 | # And immediately check that the session is busy.
78 | check_state(backend$cluster)
79 | }
80 |
81 | # Get the output and wait for the task to complete.
82 | output <- backend$get_output(wait = TRUE)
83 |
84 | # Check that the session is idle again.
85 | check_state(backend$cluster)
86 |
87 | # Manually close the session.
88 | backend$cluster$close()
89 |
90 | # Check that the session is finished.
91 | check_state(backend$cluster)
92 |
93 | # Stop the backend.
94 | backend$stop()
95 |
96 | }
97 | \seealso{
98 | \code{\link{TaskState}}, \code{\link{AsyncBackend}} and
99 | \code{\link{ProgressTrackingContext}}.
100 | }
101 | \section{Active bindings}{
102 | \if{html}{\out{}}
103 | \describe{
104 | \item{\code{session_is_starting}}{A logical value indicating whether the
105 | session is starting.}
106 |
107 | \item{\code{session_is_idle}}{A logical value indicating whether the session
108 | is idle and ready to execute operations.}
109 |
110 | \item{\code{session_is_busy}}{A logical value indicating whether the session
111 | is busy.}
112 |
113 | \item{\code{session_is_finished}}{A logical value indicating whether the
114 | session is closed and no longer available for operations.}
115 | }
116 | \if{html}{\out{
}}
117 | }
118 | \section{Methods}{
119 | \subsection{Public methods}{
120 | \itemize{
121 | \item \href{#method-SessionState-new}{\code{SessionState$new()}}
122 | \item \href{#method-SessionState-clone}{\code{SessionState$clone()}}
123 | }
124 | }
125 | \if{html}{\out{
}}
126 | \if{html}{\out{}}
127 | \if{latex}{\out{\hypertarget{method-SessionState-new}{}}}
128 | \subsection{Method \code{new()}}{
129 | Create a new \code{\link{SessionState}} object and determine the state
130 | of a given background \code{\link[callr:r_session]{session}}.
131 | \subsection{Usage}{
132 | \if{html}{\out{}}\preformatted{SessionState$new(session)}\if{html}{\out{
}}
133 | }
134 |
135 | \subsection{Arguments}{
136 | \if{html}{\out{}}
137 | \describe{
138 | \item{\code{session}}{A \code{\link[callr:r_session]{callr::r_session}} object.}
139 | }
140 | \if{html}{\out{
}}
141 | }
142 | \subsection{Returns}{
143 | An object of class \code{\link{SessionState}}.
144 | }
145 | }
146 | \if{html}{\out{
}}
147 | \if{html}{\out{}}
148 | \if{latex}{\out{\hypertarget{method-SessionState-clone}{}}}
149 | \subsection{Method \code{clone()}}{
150 | The objects of this class are cloneable with this method.
151 | \subsection{Usage}{
152 | \if{html}{\out{}}\preformatted{SessionState$clone(deep = FALSE)}\if{html}{\out{
}}
153 | }
154 |
155 | \subsection{Arguments}{
156 | \if{html}{\out{}}
157 | \describe{
158 | \item{\code{deep}}{Whether to make a deep clone.}
159 | }
160 | \if{html}{\out{
}}
161 | }
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/R/SessionState.R:
--------------------------------------------------------------------------------
1 | #' @title
2 | #' SessionState
3 | #'
4 | #' @description
5 | #' This class holds the state of a background [`session`][`callr::r_session`]
6 | #' used by an asynchronous backend (i.e., [`parabar::AsyncBackend`]). See the
7 | #' **Details** section for more information.
8 | #'
9 | #' @details
10 | #' The session state is useful to check if an asynchronous backend is ready for
11 | #' certain operations. A session can only be in one of the following four states
12 | #' at a time:
13 | #' - `session_is_starting`: When `TRUE`, it indicates that the session is
14 | #' starting.
15 | #' - `session_is_idle`: When `TRUE`, it indicates that the session is idle and
16 | #' ready to execute operations.
17 | #' - `session_is_busy`: When `TRUE`, it indicates that the session is busy
18 | #' (i.e., see the [`parabar::TaskState`] class for more information about a
19 | #' task's state).
20 | #' - `session_is_finished`: When `TRUE`, it indicates that the session is closed
21 | #' and no longer available for operations.
22 | #'
23 | #' @examples
24 | #' # Handy function to print the session states all at once.
25 | #' check_state <- function(session) {
26 | #' # Create a session object and determine its state.
27 | #' session_state <- SessionState$new(session)
28 | #'
29 | #' # Print the state.
30 | #' cat(
31 | #' "Session is starting: ", session_state$session_is_starting, "\n",
32 | #' "Session is idle: ", session_state$session_is_idle, "\n",
33 | #' "Session is busy: ", session_state$session_is_busy, "\n",
34 | #' "Session is finished: ", session_state$session_is_finished, "\n",
35 | #' sep = ""
36 | #' )
37 | #' }
38 | #'
39 | #' # Create a specification object.
40 | #' specification <- Specification$new()
41 | #'
42 | #' # Set the number of cores.
43 | #' specification$set_cores(cores = 2)
44 | #'
45 | #' # Set the cluster type.
46 | #' specification$set_type(type = "psock")
47 | #'
48 | #' # Create an asynchronous backend object.
49 | #' backend <- AsyncBackend$new()
50 | #'
51 | #' # Start the cluster on the backend.
52 | #' backend$start(specification)
53 | #'
54 | #' # Check that the session is idle.
55 | #' check_state(backend$cluster)
56 | #'
57 | #' {
58 | #' # Run a task in parallel (i.e., approx. 0.25 seconds).
59 | #' backend$sapply(
60 | #' x = 1:10,
61 | #' fun = function(x) {
62 | #' # Sleep a bit.
63 | #' Sys.sleep(0.05)
64 | #'
65 | #' # Compute something.
66 | #' output <- x + 1
67 | #'
68 | #' # Return the result.
69 | #' return(output)
70 | #' }
71 | #' )
72 | #'
73 | #' # And immediately check that the session is busy.
74 | #' check_state(backend$cluster)
75 | #' }
76 | #'
77 | #' # Get the output and wait for the task to complete.
78 | #' output <- backend$get_output(wait = TRUE)
79 | #'
80 | #' # Check that the session is idle again.
81 | #' check_state(backend$cluster)
82 | #'
83 | #' # Manually close the session.
84 | #' backend$cluster$close()
85 | #'
86 | #' # Check that the session is finished.
87 | #' check_state(backend$cluster)
88 | #'
89 | #' # Stop the backend.
90 | #' backend$stop()
91 | #'
92 | #' @seealso
93 | #' [`parabar::TaskState`], [`parabar::AsyncBackend`] and
94 | #' [`parabar::ProgressTrackingContext`].
95 | #'
96 | #' @export
97 | SessionState <- R6::R6Class("SessionState",
98 | private = list(
99 | # Session state fields.
100 | .session_is_starting = NULL,
101 | .session_is_idle = NULL,
102 | .session_is_busy = NULL,
103 | .session_is_finished = NULL,
104 |
105 | # Set the session state.
106 | .set_state = function(session) {
107 | # Get the session state.
108 | state <- session$get_state()
109 |
110 | # Set the session state.
111 | private$.session_is_starting <- state == "starting"
112 | private$.session_is_idle <- state == "idle"
113 | private$.session_is_busy <- state == "busy"
114 | private$.session_is_finished <- state == "finished"
115 | }
116 | ),
117 |
118 | public = list(
119 | #' @description
120 | #' Create a new [`parabar::SessionState`] object and determine the state
121 | #' of a given background [`session`][`callr::r_session`].
122 | #'
123 | #' @param session A [`callr::r_session`] object.
124 | #'
125 | #' @return
126 | #' An object of class [`parabar::SessionState`].
127 | initialize = function(session) {
128 | # Set the session state.
129 | private$.set_state(session)
130 | }
131 | ),
132 |
133 | active = list(
134 | #' @field session_is_starting A logical value indicating whether the
135 | #' session is starting.
136 | session_is_starting = function() {
137 | return(private$.session_is_starting)
138 | },
139 |
140 | #' @field session_is_idle A logical value indicating whether the session
141 | #' is idle and ready to execute operations.
142 | session_is_idle = function() {
143 | return(private$.session_is_idle)
144 | },
145 |
146 | #' @field session_is_busy A logical value indicating whether the session
147 | #' is busy.
148 | session_is_busy = function() {
149 | return(private$.session_is_busy)
150 | },
151 |
152 | #' @field session_is_finished A logical value indicating whether the
153 | #' session is closed and no longer available for operations.
154 | session_is_finished = function() {
155 | return(private$.session_is_finished)
156 | }
157 | )
158 | )
159 |
--------------------------------------------------------------------------------
/R/exports.R:
--------------------------------------------------------------------------------
1 | #' @include Options.R Helper.R Specification.R BackendFactory.R ContextFactory.R Exception.R BarFactory.R
2 |
3 | #' @template set-default-options
4 | #' @order 3
5 | #' @export
6 | set_default_options <- function() {
7 | # Set `Options` instance.
8 | options(parabar = Options$new())
9 |
10 | # Remain silent.
11 | invisible(NULL)
12 | }
13 |
14 |
15 | #' @template get-option
16 | #' @order 1
17 | #' @export
18 | get_option <- function(option) {
19 | # Invoke the helper.
20 | Helper$get_option(option)
21 | }
22 |
23 |
24 | #' @template set-option
25 | #' @order 2
26 | #' @export
27 | set_option <- function(option, value) {
28 | # Invoke the helper.
29 | Helper$set_option(option, value)
30 |
31 | # Remain silent.
32 | invisible()
33 | }
34 |
35 |
36 | #' @template configure-bar
37 | #' @export
38 | configure_bar <- function(type = "modern", ...) {
39 | # If the type is not known.
40 | if (!type %in% c("modern", "basic")) {
41 | # Throw an error.
42 | Exception$feature_not_developed()
43 | }
44 |
45 | # Update the bar type in options.
46 | set_option("progress_bar_type", type)
47 |
48 | # Capture the bar configuration requested by the user.
49 | user_bar_config <- list(...)
50 |
51 | # If the configuration is not empty.
52 | if (length(user_bar_config)) {
53 | # Get the default config options.
54 | bar_config <- get_option("progress_bar_config")
55 |
56 | # Combine the configurations.
57 | bar_config[[type]] <- utils::modifyList(bar_config[[type]], user_bar_config)
58 |
59 | # Set the bar config in options.
60 | set_option("progress_bar_config", bar_config)
61 | }
62 |
63 | # Remain silent.
64 | invisible()
65 | }
66 |
67 |
68 | #' @template start-backend
69 | #' @export
70 | start_backend <- function(cores, cluster_type = "psock", backend_type = "async") {
71 | # Create specification object.
72 | specification <- Specification$new()
73 |
74 | # Set specification cores.
75 | specification$set_cores(cores)
76 |
77 | # Set the specification cluster type.
78 | specification$set_type(cluster_type)
79 |
80 | # Initialize a backend factory.
81 | backend_factory <- BackendFactory$new()
82 |
83 | # Get a backend instance of the desired type.
84 | backend <- backend_factory$get(backend_type)
85 |
86 | # Start the backend.
87 | backend$start(specification)
88 |
89 | # Register a finalizer.
90 | reg.finalizer(backend, function(backend) {
91 | # If the backend is still active when the finalizer runs in the future.
92 | if (backend$active) {
93 | # Attempt to stop the backend in their stead.
94 | tryCatch(
95 | # The expression to evaluate.
96 | expr = {
97 | # Stop it.
98 | stop_backend(backend)
99 | },
100 | # Catch any errors.
101 | error = function(error) {
102 | # Issue a warning.
103 | Warning$error_in_backend_finalizer(backend, error)
104 | }
105 | )
106 | }
107 | }, onexit = TRUE)
108 |
109 | # Return the backend.
110 | return(backend)
111 | }
112 |
113 |
114 | #' @template stop-backend
115 | #' @export
116 | stop_backend <- function(backend) {
117 | # Check the type.
118 | Helper$check_object_type(backend, "Backend")
119 |
120 | # Stop the backend
121 | backend$stop()
122 |
123 | # Remain silent.
124 | invisible()
125 | }
126 |
127 |
128 | #' @template clear
129 | #' @export
130 | clear <- function(backend) {
131 | # Check the type.
132 | Helper$check_object_type(backend, "Backend")
133 |
134 | # Peek the backend.
135 | backend$clear()
136 | }
137 |
138 |
139 | #' @template peek
140 | #' @export
141 | peek <- function(backend) {
142 | # Check the type.
143 | Helper$check_object_type(backend, "Backend")
144 |
145 | # Peek the backend..
146 | backend$peek()
147 | }
148 |
149 |
150 | #' @template export
151 | #' @export
152 | export <- function(backend, variables, environment) {
153 | # Check the type.
154 | Helper$check_object_type(backend, "Backend")
155 |
156 | # Export variables.
157 | backend$export(variables, environment)
158 | }
159 |
160 |
161 | #' @template evaluate
162 | #' @export
163 | evaluate <- function(backend, expression) {
164 | # Check the type.
165 | Helper$check_object_type(backend, "Backend")
166 |
167 | # Capture the expression.
168 | capture <- substitute(expression)
169 |
170 | # Prepare the call.
171 | capture_call <- bquote(backend$evaluate(.(capture)))
172 |
173 | # Perform the call.
174 | eval(capture_call)
175 | }
176 |
177 |
178 | #' @template par-sapply
179 | #' @export
180 | par_sapply <- function(backend = NULL, x, fun, ...) {
181 | # Create an user API consumer.
182 | consumer <- UserApiConsumer$new()
183 |
184 | # Execute the task using the `sapply` parallel operation.
185 | consumer$sapply(backend = backend, x = x, fun = fun, ...)
186 | }
187 |
188 | #' @template par-lapply
189 | #' @export
190 | par_lapply <- function(backend = NULL, x, fun, ...) {
191 | # Create an user API consumer.
192 | consumer <- UserApiConsumer$new()
193 |
194 | # Execute the task using the `lapply` parallel operation.
195 | consumer$lapply(backend = backend, x = x, fun = fun, ...)
196 | }
197 |
198 | #' @template par-apply
199 | #' @export
200 | par_apply <- function(backend = NULL, x, margin, fun, ...) {
201 | # Create an user API consumer.
202 | consumer <- UserApiConsumer$new()
203 |
204 | # Execute the task using the `apply` parallel operation.
205 | consumer$apply(backend = backend, x = x, margin = margin, fun = fun, ...)
206 | }
207 |
--------------------------------------------------------------------------------
/man/TaskState.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/TaskState.R
3 | \name{TaskState}
4 | \alias{TaskState}
5 | \title{TaskState}
6 | \description{
7 | This class holds the state of a task deployed to an asynchronous backend
8 | (i.e., \code{\link{AsyncBackend}}). See the \strong{Details} section for more
9 | information.
10 | }
11 | \details{
12 | The task state is useful to check if an asynchronous backend is free to
13 | execute other operations. A task can only be in one of the following three
14 | states at a time:
15 | \itemize{
16 | \item \code{task_not_started}: When \code{TRUE}, it indicates whether the backend is free
17 | to execute another operation.
18 | \item \code{task_is_running}: When \code{TRUE}, it indicates that there is a task running
19 | on the backend.
20 | \item \code{task_is_completed}: When \code{TRUE}, it indicates that the task has been
21 | completed, but the backend is still busy because the task output has not
22 | been retrieved.
23 | }
24 |
25 | The task state is determined based on the state of the background
26 | \code{\link[callr:r_session]{session}} (i.e., see the \code{get_state} method for
27 | \code{\link[callr:r_session]{callr::r_session}}) and the state of the task execution inferred from
28 | polling the process (i.e., see the \code{poll_process} method for
29 | \code{\link[callr:r_session]{callr::r_session}}) as follows:\tabular{ccccc}{
30 | Session State \tab Execution State \tab Not Started \tab Is Running \tab Is Completed \cr
31 | \code{idle} \tab \code{timeout} \tab \code{TRUE} \tab \code{FALSE} \tab \code{FALSE} \cr
32 | \code{busy} \tab \code{timeout} \tab \code{FALSE} \tab \code{TRUE} \tab \code{FALSE} \cr
33 | \code{busy} \tab \code{ready} \tab \code{FALSE} \tab \code{FALSE} \tab \code{TRUE} \cr
34 | }
35 | }
36 | \examples{
37 | # Handy function to print the task states all at once.
38 | check_state <- function(session) {
39 | # Create a task state object and determine the state.
40 | task_state <- TaskState$new(session)
41 |
42 | # Print the state.
43 | cat(
44 | "Task not started: ", task_state$task_not_started, "\n",
45 | "Task is running: ", task_state$task_is_running, "\n",
46 | "Task is completed: ", task_state$task_is_completed, "\n",
47 | sep = ""
48 | )
49 | }
50 |
51 | # Create a specification object.
52 | specification <- Specification$new()
53 |
54 | # Set the number of cores.
55 | specification$set_cores(cores = 2)
56 |
57 | # Set the cluster type.
58 | specification$set_type(type = "psock")
59 |
60 | # Create an asynchronous backend object.
61 | backend <- AsyncBackend$new()
62 |
63 | # Start the cluster on the backend.
64 | backend$start(specification)
65 |
66 | # Check that the task has not been started (i.e., the backend is free).
67 | check_state(backend$cluster)
68 |
69 | {
70 | # Run a task in parallel (i.e., approx. 0.25 seconds).
71 | backend$sapply(
72 | x = 1:10,
73 | fun = function(x) {
74 | # Sleep a bit.
75 | Sys.sleep(0.05)
76 |
77 | # Compute something.
78 | output <- x + 1
79 |
80 | # Return the result.
81 | return(output)
82 | }
83 | )
84 |
85 | # And immediately check the state to see that the task is running.
86 | check_state(backend$cluster)
87 | }
88 |
89 | # Sleep for a bit to wait for the task to complete.
90 | Sys.sleep(1)
91 |
92 | # Check that the task is completed (i.e., the output needs to be retrieved).
93 | check_state(backend$cluster)
94 |
95 | # Get the output.
96 | output <- backend$get_output(wait = TRUE)
97 |
98 | # Check that the task has not been started (i.e., the backend is free again).
99 | check_state(backend$cluster)
100 |
101 | # Stop the backend.
102 | backend$stop()
103 |
104 | }
105 | \seealso{
106 | \code{\link{SessionState}}, \code{\link{AsyncBackend}} and
107 | \code{\link{ProgressTrackingContext}}.
108 | }
109 | \section{Active bindings}{
110 | \if{html}{\out{}}
111 | \describe{
112 | \item{\code{task_not_started}}{A logical value indicating whether the task
113 | has been started. It is used to determine if the backend is free to
114 | execute another operation.}
115 |
116 | \item{\code{task_is_running}}{A logical value indicating whether the task is
117 | running.}
118 |
119 | \item{\code{task_is_completed}}{A logical value indicating whether the task
120 | has been completed and the output needs to be retrieved.}
121 | }
122 | \if{html}{\out{
}}
123 | }
124 | \section{Methods}{
125 | \subsection{Public methods}{
126 | \itemize{
127 | \item \href{#method-TaskState-new}{\code{TaskState$new()}}
128 | \item \href{#method-TaskState-clone}{\code{TaskState$clone()}}
129 | }
130 | }
131 | \if{html}{\out{
}}
132 | \if{html}{\out{}}
133 | \if{latex}{\out{\hypertarget{method-TaskState-new}{}}}
134 | \subsection{Method \code{new()}}{
135 | Create a new \code{\link{TaskState}} object and determine the state of
136 | a task on a given background \code{\link[callr:r_session]{session}}.
137 | \subsection{Usage}{
138 | \if{html}{\out{}}\preformatted{TaskState$new(session)}\if{html}{\out{
}}
139 | }
140 |
141 | \subsection{Arguments}{
142 | \if{html}{\out{}}
143 | \describe{
144 | \item{\code{session}}{A \code{\link[callr:r_session]{callr::r_session}} object.}
145 | }
146 | \if{html}{\out{
}}
147 | }
148 | \subsection{Returns}{
149 | An object of class \code{\link{TaskState}}.
150 | }
151 | }
152 | \if{html}{\out{
}}
153 | \if{html}{\out{}}
154 | \if{latex}{\out{\hypertarget{method-TaskState-clone}{}}}
155 | \subsection{Method \code{clone()}}{
156 | The objects of this class are cloneable with this method.
157 | \subsection{Usage}{
158 | \if{html}{\out{}}\preformatted{TaskState$clone(deep = FALSE)}\if{html}{\out{
}}
159 | }
160 |
161 | \subsection{Arguments}{
162 | \if{html}{\out{}}
163 | \describe{
164 | \item{\code{deep}}{Whether to make a deep clone.}
165 | }
166 | \if{html}{\out{
}}
167 | }
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/tests/testthat/test-specification.R:
--------------------------------------------------------------------------------
1 | # Test `Specification` class.
2 |
3 | test_that("'Specification' fails on single core machines", {
4 | # Create a specification object.
5 | specification <- SpecificationTester$new()
6 |
7 | # Pretend the machine has a single core.
8 | specification$available_cores <- 1
9 |
10 | # Expect failure regardless of the number of requested cores.
11 | expect_error(specification$set_cores(cores = 1), as_text(Exception$not_enough_cores()))
12 | expect_error(specification$set_cores(cores = 2), as_text(Exception$not_enough_cores()))
13 | expect_error(specification$set_cores(cores = 7), as_text(Exception$not_enough_cores()))
14 | })
15 |
16 |
17 | test_that("'Specification' sets the number of cores correctly", {
18 | # Create a specification object.
19 | specification <- SpecificationTester$new()
20 |
21 | # Suppose the machine has two cores.
22 | specification$available_cores <- 2
23 |
24 | # When zero cores are requested.
25 | expect_warning(specification$set_cores(cores = 0), as_text(Warning$requested_cluster_cores_too_low()))
26 | expect_equal(specification$cores, 1)
27 |
28 | # When one core is requested.
29 | specification$set_cores(cores = 1)
30 | expect_equal(specification$cores, 1)
31 |
32 | # When two cores are requested.
33 | expect_warning(specification$set_cores(cores = 2), as_text(Warning$requested_cluster_cores_too_high(specification$usable_cores)))
34 | expect_equal(specification$cores, 1)
35 |
36 | # When more than two cores are requested.
37 | expect_warning(specification$set_cores(cores = 7), as_text(Warning$requested_cluster_cores_too_high(specification$usable_cores)))
38 | expect_equal(specification$cores, 1)
39 |
40 | # Suppose the machine has three cores.
41 | specification$available_cores <- 3
42 |
43 | # When zero cores are requested.
44 | expect_warning(specification$set_cores(cores = 0), as_text(Warning$requested_cluster_cores_too_low()))
45 | expect_equal(specification$cores, 1)
46 |
47 | # When one core is requested.
48 | specification$set_cores(cores = 1)
49 | expect_equal(specification$cores, 1)
50 |
51 | # When two cores are requested.
52 | specification$set_cores(cores = 2)
53 | expect_equal(specification$cores, 2)
54 |
55 | # When three cores are requested.
56 | expect_warning(specification$set_cores(cores = 3), as_text(Warning$requested_cluster_cores_too_high(specification$usable_cores)))
57 | expect_equal(specification$cores, 2)
58 |
59 | # Suppose the machine has eight cores.
60 | specification$available_cores <- 8
61 |
62 | # When zero cores are requested.
63 | expect_warning(specification$set_cores(cores = 0), as_text(Warning$requested_cluster_cores_too_low()))
64 | expect_equal(specification$cores, 1)
65 |
66 | # When one core is requested.
67 | specification$set_cores(cores = 1)
68 | expect_equal(specification$cores, 1)
69 |
70 | # When two cores are requested.
71 | specification$set_cores(cores = 2)
72 | expect_equal(specification$cores, 2)
73 |
74 | # When seven cores are requested.
75 | specification$set_cores(cores = 7)
76 | expect_equal(specification$cores, 7)
77 |
78 | # When eight cores are requested.
79 | expect_warning(specification$set_cores(cores = 8), as_text(Warning$requested_cluster_cores_too_high(specification$usable_cores)))
80 | expect_equal(specification$cores, 7)
81 |
82 | # When nine cores are requested.
83 | expect_warning(specification$set_cores(cores = 9), as_text(Warning$requested_cluster_cores_too_high(specification$usable_cores)))
84 | expect_equal(specification$cores, 7)
85 | })
86 |
87 |
88 | test_that("'Specification' sets the cluster type on Unix correctly", {
89 | # Skip test on Windows.
90 | skip_on_os("windows")
91 |
92 | # Create specification object.
93 | specification <- Specification$new()
94 |
95 | # Let the specification determine the cluster type.
96 | specification$set_type(type = NULL)
97 |
98 | # Expect a `FORK` cluster was determined as default on Unix platforms.
99 | expect_equal(specification$type, c(unix = "FORK"))
100 |
101 | # Specify the `FORK` type explicitly.
102 | specification$set_type(type = "fork")
103 |
104 | # Expect the correct type was set.
105 | expect_equal(specification$type, "FORK")
106 |
107 | # Specify the `PSOCK` type explicitly.
108 | specification$set_type(type = "psock")
109 |
110 | # Expect the correct type was set.
111 | expect_equal(specification$type, "PSOCK")
112 |
113 | # Expect warning when specifying an invalid type.
114 | expect_warning(
115 | specification$set_type(type = "invalid"),
116 | as_text(Warning$requested_cluster_type_not_supported(specification$types))
117 | )
118 | })
119 |
120 |
121 | test_that("'Specification' sets the cluster type on Windows correctly", {
122 | # Skip test on Unix and the like.
123 | skip_on_os(c("mac", "linux", "solaris"))
124 |
125 | # Create specification object.
126 | specification <- Specification$new()
127 |
128 | # Let the specification determine the cluster type.
129 | specification$set_type(type = NULL)
130 |
131 | # Expect a `PSOCK` cluster was determined as default on Windows platforms.
132 | expect_equal(specification$type, c(windows = "PSOCK"))
133 |
134 | # Expect warning if a `FORK` cluster is requested on Windows platforms.
135 | expect_warning(
136 | specification$set_type(type = "fork"),
137 | as_text(Warning$requested_cluster_type_not_compatible(specification$types))
138 | )
139 |
140 | # Expect that the correct type was set regardless the incompatible request.
141 | expect_equal(specification$type, c(windows = "PSOCK"))
142 |
143 | # Specify the `PSOCK` type explicitly.
144 | specification$set_type(type = "psock")
145 |
146 | # Expect the correct type was set.
147 | expect_equal(specification$type, "PSOCK")
148 |
149 | # Expect warning when specifying an invalid type.
150 | expect_warning(
151 | specification$set_type(type = "invalid"),
152 | as_text(Warning$requested_cluster_type_not_supported(specification$types))
153 | )
154 | })
155 |
--------------------------------------------------------------------------------
/R/TaskState.R:
--------------------------------------------------------------------------------
1 | #' @title
2 | #' TaskState
3 | #'
4 | #' @description
5 | #' This class holds the state of a task deployed to an asynchronous backend
6 | #' (i.e., [`parabar::AsyncBackend`]). See the **Details** section for more
7 | #' information.
8 | #'
9 | #' @details
10 | #' The task state is useful to check if an asynchronous backend is free to
11 | #' execute other operations. A task can only be in one of the following three
12 | #' states at a time:
13 | #' - `task_not_started`: When `TRUE`, it indicates whether the backend is free
14 | #' to execute another operation.
15 | #' - `task_is_running`: When `TRUE`, it indicates that there is a task running
16 | #' on the backend.
17 | #' - `task_is_completed`: When `TRUE`, it indicates that the task has been
18 | #' completed, but the backend is still busy because the task output has not
19 | #' been retrieved.
20 | #'
21 | #' The task state is determined based on the state of the background
22 | #' [`session`][`callr::r_session`] (i.e., see the `get_state` method for
23 | #' [`callr::r_session`]) and the state of the task execution inferred from
24 | #' polling the process (i.e., see the `poll_process` method for
25 | #' [`callr::r_session`]) as follows:
26 | #'
27 | #' | Session State | Execution State | Not Started | Is Running | Is Completed |
28 | #' | :-----------: | :-------------: | :---------: | :--------: | :----------: |
29 | #' | `idle` | `timeout` | `TRUE` | `FALSE` | `FALSE` |
30 | #' | `busy` | `timeout` | `FALSE` | `TRUE` | `FALSE` |
31 | #' | `busy` | `ready` | `FALSE` | `FALSE` | `TRUE` |
32 | #'
33 | #' @examples
34 | #' # Handy function to print the task states all at once.
35 | #' check_state <- function(session) {
36 | #' # Create a task state object and determine the state.
37 | #' task_state <- TaskState$new(session)
38 | #'
39 | #' # Print the state.
40 | #' cat(
41 | #' "Task not started: ", task_state$task_not_started, "\n",
42 | #' "Task is running: ", task_state$task_is_running, "\n",
43 | #' "Task is completed: ", task_state$task_is_completed, "\n",
44 | #' sep = ""
45 | #' )
46 | #' }
47 | #'
48 | #' # Create a specification object.
49 | #' specification <- Specification$new()
50 | #'
51 | #' # Set the number of cores.
52 | #' specification$set_cores(cores = 2)
53 | #'
54 | #' # Set the cluster type.
55 | #' specification$set_type(type = "psock")
56 | #'
57 | #' # Create an asynchronous backend object.
58 | #' backend <- AsyncBackend$new()
59 | #'
60 | #' # Start the cluster on the backend.
61 | #' backend$start(specification)
62 | #'
63 | #' # Check that the task has not been started (i.e., the backend is free).
64 | #' check_state(backend$cluster)
65 | #'
66 | #' {
67 | #' # Run a task in parallel (i.e., approx. 0.25 seconds).
68 | #' backend$sapply(
69 | #' x = 1:10,
70 | #' fun = function(x) {
71 | #' # Sleep a bit.
72 | #' Sys.sleep(0.05)
73 | #'
74 | #' # Compute something.
75 | #' output <- x + 1
76 | #'
77 | #' # Return the result.
78 | #' return(output)
79 | #' }
80 | #' )
81 | #'
82 | #' # And immediately check the state to see that the task is running.
83 | #' check_state(backend$cluster)
84 | #' }
85 | #'
86 | #' # Sleep for a bit to wait for the task to complete.
87 | #' Sys.sleep(1)
88 | #'
89 | #' # Check that the task is completed (i.e., the output needs to be retrieved).
90 | #' check_state(backend$cluster)
91 | #'
92 | #' # Get the output.
93 | #' output <- backend$get_output(wait = TRUE)
94 | #'
95 | #' # Check that the task has not been started (i.e., the backend is free again).
96 | #' check_state(backend$cluster)
97 | #'
98 | #' # Stop the backend.
99 | #' backend$stop()
100 | #'
101 | #' @seealso
102 | #' [`parabar::SessionState`], [`parabar::AsyncBackend`] and
103 | #' [`parabar::ProgressTrackingContext`].
104 | #'
105 | #' @export
106 | TaskState <- R6::R6Class("TaskState",
107 | private = list(
108 | # Task state fields.
109 | .task_not_started = NULL,
110 | .task_is_running = NULL,
111 | .task_is_completed = NULL,
112 |
113 | # Set the task state.
114 | .set_state = function(cluster) {
115 | # Session state.
116 | session_state <- cluster$get_state()
117 |
118 | # Task execution state.
119 | execution_state <- cluster$poll_process(0)
120 |
121 | # Compute all states.
122 | # The session is free and no task is running.
123 | private$.task_not_started <- (session_state == "idle" && execution_state == "timeout")
124 |
125 | # The session is busy and a task is running.
126 | private$.task_is_running <- (session_state == "busy" && execution_state == "timeout")
127 |
128 | # The session is busy and a task has completed (i.e., results need to be read).
129 | private$.task_is_completed <- (session_state == "busy" && execution_state == "ready")
130 | }
131 | ),
132 |
133 | public = list(
134 | #' @description
135 | #' Create a new [`parabar::TaskState`] object and determine the state of
136 | #' a task on a given background [`session`][`callr::r_session`].
137 | #'
138 | #' @param session A [`callr::r_session`] object.
139 | #'
140 | #' @return
141 | #' An object of class [`parabar::TaskState`].
142 | initialize = function(session) {
143 | # Set the task state.
144 | private$.set_state(session)
145 | }
146 | ),
147 |
148 | active = list(
149 | #' @field task_not_started A logical value indicating whether the task
150 | #' has been started. It is used to determine if the backend is free to
151 | #' execute another operation.
152 | task_not_started = function() {
153 | return(private$.task_not_started)
154 | },
155 |
156 | #' @field task_is_running A logical value indicating whether the task is
157 | #' running.
158 | task_is_running = function() {
159 | return(private$.task_is_running)
160 | },
161 |
162 | #' @field task_is_completed A logical value indicating whether the task
163 | #' has been completed and the output needs to be retrieved.
164 | task_is_completed = function() {
165 | return(private$.task_is_completed)
166 | }
167 | )
168 | )
169 |
--------------------------------------------------------------------------------