From d546322cf8512219f029c910c1883deca68fa8e1 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Fri, 9 May 2025 17:09:39 +0100 Subject: [PATCH 1/2] correct for time drift --- DESCRIPTION | 2 +- R/string.R | 39 ++++++++++++++++++--------------------- R/utils.R | 2 +- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index ceefb87c..04cf32fb 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: ps Title: List, Query, Manipulate System Processes -Version: 1.9.1.9001 +Version: 1.9.1.9002 Authors@R: c( person("Jay", "Loden", role = "aut"), person("Dave", "Daeschler", role = "aut"), diff --git a/R/string.R b/R/string.R index ec56ae92..30099a25 100644 --- a/R/string.R +++ b/R/string.R @@ -1,7 +1,7 @@ #' Encode a `ps_handle` as a short string #' #' A convenient format for passing between processes, naming semaphores, or -#' using as a directory/file name. Will always be 14 alphanumeric characters, +#' using as a directory/file name. Will always be 11 alphanumeric characters, #' with the first character guarantied to be a letter. Encodes the pid and #' creation time for a process. #' @@ -18,20 +18,21 @@ ps_string <- function(p = ps_handle()) { assert_ps_handle(p) - ps__str_encode(ps_pid(p), ps_create_time(p)) + ps__str_encode(p) } -ps__str_encode <- function(process_id, time) { - whole_secs <- as.integer(time) - micro_secs <- as.numeric(time) %% 1 * 1000000 +ps__str_encode <- function(p) { - # Assumptions: - # time between Jan 1st 1970 and Dec 5th 3769. - # max time precision = 1/1,000,000 of a second. - # pid <= 7,311,615 (current std max = 4,194,304). + process_id <- ps_pid(p) + create_secs <- as.numeric(ps_create_time(p)) + boot_secs <- as.numeric(ps_boot_time()) + offset_ms <- floor((create_secs - boot_secs) * 1000) - # Note: micro_secs has three extra unused bits + # Assumptions: + # System uptime < 111 years. + # PIDs are not reused within the same millisecond. + # PID <= 7,311,615 (current std max = 4,194,304). map <- c(letters, LETTERS, 0:9) @@ -41,8 +42,7 @@ ps__str_encode <- function(process_id, time) { 1 + c( floor(process_id / 52^(3:0)) %% 52, - floor(whole_secs / 62^(5:0)) %% 62, - floor(micro_secs / 62^(3:0)) %% 62 + floor(offset_ms / 62^(6:0)) %% 62 ) ] ) @@ -53,22 +53,19 @@ ps__str_decode <- function(str) { map <- structure(0:61, names = c(letters, LETTERS, 0:9)) val <- map[strsplit(str, '', fixed = TRUE)[[1]]] - process_id <- sum(val[01:04] * 52^(3:0)) - whole_secs <- sum(val[05:10] * 62^(5:0)) - micro_secs <- sum(val[11:14] * 62^(3:0)) - - time <- whole_secs + (micro_secs / 1000000) - time <- as.POSIXct(time, tz = 'GMT', origin = '1970-01-01') + process_id <- sum(val[01:04] * 52^(3:0)) + offset_ms <- sum(val[05:11] * 62^(6:0)) + create_time <- ps_boot_time() + (offset_ms / 1000) - # Allow fuzzy-matching the time by +/- 2 microseconds + # Allow fuzzy-matching the microseconds tryCatch( expr = { p <- ps_handle(pid = process_id) - stopifnot(abs(ps_create_time(p) - time) < 2 / 1000000) + stopifnot(abs(ps_create_time(p) - create_time) < 1 / 1000) p }, error = function(e) { - ps_handle(pid = process_id, time = time) + ps_handle(pid = process_id, time = create_time) } ) } diff --git a/R/utils.R b/R/utils.R index 9fb11a64..16ddfcc7 100644 --- a/R/utils.R +++ b/R/utils.R @@ -137,7 +137,7 @@ assert_pid <- function(x) { is.character(x) && length(x) == 1 && !is.na(x) && - grepl("^[A-Za-z]{4}[A-Za-z0-9]{10}$", x) + grepl("^[A-Za-z]{4}[A-Za-z0-9]{7}$", x) ) { return(x) } From b0d17f1ff8b4e0ac711bd0f25bd7d2eb0bcadfa6 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Fri, 9 May 2025 17:18:55 +0100 Subject: [PATCH 2/2] updated tests --- tests/testthat/test-common.R | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/testthat/test-common.R b/tests/testthat/test-common.R index 03eef3f7..9d8ff502 100644 --- a/tests/testthat/test-common.R +++ b/tests/testthat/test-common.R @@ -21,8 +21,6 @@ test_that("string", { # Values satisfy encoding assumptions expect_true(all(ps_pids() < 52^4)) - expect_true(Sys.time() < 62^6 * .99) - expect_identical(nchar(format(ps_create_time(), "%OS8")), 9L) # Roundtrip through ps_string str <- expect_silent(ps_string(ps)) @@ -34,9 +32,11 @@ test_that("string", { expect_identical(ps_ppid(ps), ps_ppid(ps2)) # Invalid process - str <- ps__str_encode(ps_pid(ps), ps_create_time(ps) + 1) - ps3 <- expect_silent(ps_handle(str)) - expect_false(ps_is_running(ps3)) + ps2 <- expect_silent(ps_handle(ps_pid(ps), ps_create_time(ps) + 1)) + expect_false(ps_is_running(ps2)) + str <- expect_silent(ps_string(ps2)) + ps2 <- expect_silent(ps_handle(str)) + expect_false(ps_is_running(ps2)) }) test_that("pid", {