R/booter.R

Defines functions bootKernel .mainLoop

Documented in bootKernel

# Copyright (C) 2017-2018  Spencer Aiello
#
# This file is part of JuniperKernel.
#
# JuniperKernel is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# JuniperKernel is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with JuniperKernel.  If not, see <http://www.gnu.org/licenses/>.
  
#' Juniper Kernel Booter
#' @title Boot the Juniper Kernel
#' @details
#' This method is invoked programatically by a Jupyter client. In fact,
#' it's this method that appears in the \code{kernels.json} file at the
#' install location of the Juniper kernel. This kernel expects that a
#' Jupyter client will pass the connection file via the command line.
#' A connection file contains json that is similar to:
#'  \preformatted{
#'    \{
#'      "stdin_port": 61144,
#'      "transport": "tcp",
#'      "ip": "127.0.0.1",
#'      "iopub_port": 61143,
#'      "hb_port": 61146,
#'      "key": "cc496d37-59a9-4c61-8900-d826985f564d",
#'      "shell_port": 61142,
#'      "control_port": 61145
#'    \}
#'  }
#'
#' This file is generated by the Jupyter client in the install directory of the
#' kernel. For example, if the Juniper kernel were installed on macOS with the
#' \code{--user} flag, the generated json file would have a file path of
#'   ~/Library/Jupyter/runtime/kernel-7a172737-797e-4b90-9e81-720eb8b999ab.json
#'
#' See http://jupyter-client.readthedocs.io/en/latest/kernels.html#connection-files
#' for more details.
#'
#' @examples
#' \dontrun{
#'   /path/to/R -e 'JuniperKernel::bootKernel()' --args /path/to/connection_file.json
#' }
#'
#' @export
bootKernel <- function() {
  require(JuniperKernel)
  argv <- commandArgs(trailingOnly=TRUE)

  if( length(argv)==0L )
    stop("Missing command line arguments. Juniper kernel installation may be corrupt.")

  if( length(argv)>1L )
    warning("Multiple arguments passed; all but the first will be ignored.")

  repos <- getOption('repos')
  nomirror <- is.null(repos) || length(repos) == 0L || repos[[1L]] == '@CRAN@'
  if( nomirror )
    options(repos = c(CRAN = "http://cran.rstudio.com"))

  userConnFile <- argv[1L]
  if( !file.exists(userConnFile) )
    stop("Connection file does not exist: ", userConnFile)

  .JUNIPER$kernel <- init_kernel(userConnFile)
  cfg <- boot_kernel(.kernel(), as.integer(Sys.getenv("JPY_INTERRUPT_EVENT")))
  .mainLoop(cfg)
}

.mainLoop <- function(cfg) {
  # socket order
  CONTROL <- 1L
  SHELL   <- 2L
  socks <- c(cfg$ctl, cfg$shl)
  while( TRUE ) {
    r <- tryCatch(
            zmq.poll( socks
                    , rep(.pbd_env$ZMQ.PO$POLLIN, 2L)
                    , MC = ZMQ.MC(check.eintr = TRUE)
                    )
          , interrupt = function(.) 'SIGINT'
          )
    if( length(r)==1L && r=='SIGINT' )
      next
    if( .hasMsg(CONTROL) && !.handle('control') )
      break
    if( .hasMsg(  SHELL) && !.handle(  'shell') )
      break
  }
}

.handle <- function(sockName) {
  req <- sock_recv(.kernel(), sockName)
  handler <- get(req$message_type, envir=as.environment('package:JuniperKernel'))
  res <- doRequest(handler, req)
  post_handle(.kernel(), res, sockName)
  req$message_type!='shutdown_request'
}


.hasMsg <- function(i) {
  bitwAnd(zmq.poll.get.revents(i), .pbd_env$ZMQ.PO$POLLIN)
}

Try the JuniperKernel package in your browser

Any scripts or data that you put into this service are public.

JuniperKernel documentation built on May 2, 2019, 4:35 a.m.