Skip to content

Commit b6f1751

Browse files
authored
Merge pull request #208 from dansmith01/main
2 parents dde739c + 861be2b commit b6f1751

6 files changed

Lines changed: 83 additions & 52 deletions

File tree

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: ps
22
Title: List, Query, Manipulate System Processes
3-
Version: 1.9.1.9001
3+
Version: 1.9.1.9002
44
Authors@R: c(
55
person("Jay", "Loden", role = "aut"),
66
person("Dave", "Daeschler", role = "aut"),

R/string.R

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#' Encode a `ps_handle` as a short string
22
#'
33
#' A convenient format for passing between processes, naming semaphores, or
4-
#' using as a directory/file name. Will always be 14 alphanumeric characters,
4+
#' using as a directory/file name. Will always be 12 alphanumeric characters,
55
#' with the first character guarantied to be a letter. Encodes the pid and
66
#' creation time for a process.
77
#'
@@ -18,57 +18,65 @@
1818

1919
ps_string <- function(p = ps_handle()) {
2020
assert_ps_handle(p)
21-
ps__str_encode(ps_pid(p), ps_create_time(p))
21+
ps__str_encode(p)
2222
}
2323

2424

25-
ps__str_encode <- function(process_id, time) {
26-
whole_secs <- as.integer(time)
27-
micro_secs <- as.numeric(time) %% 1 * 1000000
25+
ps__str_encode <- function(p) {
2826

2927
# Assumptions:
30-
# time between Jan 1st 1970 and Dec 5th 3769.
31-
# max time precision = 1/1,000,000 of a second.
32-
# pid <= 7,311,615 (current std max = 4,194,304).
28+
# - Date < 8888-12-02 (Windows only).
29+
# - System uptime < 6918 years (Unix only).
30+
# - PID <= 768,369,472 (current std max = 4,194,304).
31+
# - PIDs are not reused within the same millisecond.
3332

34-
# Note: micro_secs has three extra unused bits
33+
# Surprisingly, `ps_boot_time()` is not constant from process to process, and
34+
# `ps_create_time()` is derived from `ps_boot_time()` on Unix. Therefore:
35+
# - On Windows, encode `ps_create_time()`
36+
# - On Unix, encode `ps_create_time() - `ps_boot_time()`
3537

36-
map <- c(letters, LETTERS, 0:9)
38+
pid <- ps_pid(p)
39+
time <- as.numeric(ps_create_time(p))
40+
41+
if (.Platform$OS.type == "unix")
42+
time <- time - as.numeric(ps_boot_time())
43+
44+
time <- round(time, 3) * 1000 # millisecond resolution
3745

46+
map <- c(letters, LETTERS, 0:9)
3847
paste(
3948
collapse = '',
4049
map[
4150
1 +
4251
c(
43-
floor(process_id / 52^(3:0)) %% 52,
44-
floor(whole_secs / 62^(5:0)) %% 62,
45-
floor(micro_secs / 62^(3:0)) %% 62
52+
floor(pid / 62^(3:0)) %% 62,
53+
floor(time / 62^(7:0)) %% 62
4654
)
4755
]
4856
)
4957
}
5058

5159

5260
ps__str_decode <- function(str) {
61+
5362
map <- structure(0:61, names = c(letters, LETTERS, 0:9))
5463
val <- map[strsplit(str, '', fixed = TRUE)[[1]]]
64+
pid <- sum(val[01:04] * 62^(3:0))
5565

56-
process_id <- sum(val[01:04] * 52^(3:0))
57-
whole_secs <- sum(val[05:10] * 62^(5:0))
58-
micro_secs <- sum(val[11:14] * 62^(3:0))
59-
60-
time <- whole_secs + (micro_secs / 1000000)
61-
time <- as.POSIXct(time, tz = 'GMT', origin = '1970-01-01')
62-
63-
# Allow fuzzy-matching the time by +/- 2 microseconds
6466
tryCatch(
6567
expr = {
66-
p <- ps_handle(pid = process_id)
67-
stopifnot(abs(ps_create_time(p) - time) < 2 / 1000000)
68+
p <- ps_handle(pid = pid)
69+
stopifnot(str == ps__str_encode(p))
6870
p
6971
},
7072
error = function(e) {
71-
ps_handle(pid = process_id, time = time)
73+
74+
time <- sum(val[05:12] * 62^(7:0)) / 1000
75+
76+
if (.Platform$OS.type == "unix")
77+
time <- time + as.numeric(ps_boot_time())
78+
79+
ps_handle(pid = pid, time = format_unix_time(time))
7280
}
7381
)
7482
}

R/utils.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ assert_pid <- function(x) {
145145
is.character(x) &&
146146
length(x) == 1 &&
147147
!is.na(x) &&
148-
grepl("^[A-Za-z]{4}[A-Za-z0-9]{10}$", x)
148+
grepl("^[A-Za-z][A-Za-z0-9]{11}$", x)
149149
) {
150150
return(x)
151151
}

man/ps_string.Rd

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-common.R

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,29 +16,6 @@ test_that("print", {
1616
expect_output(print(ps), format_regexp())
1717
})
1818

19-
test_that("string", {
20-
ps <- ps_handle()
21-
22-
# Values satisfy encoding assumptions
23-
expect_true(all(ps_pids() < 52^4))
24-
expect_true(Sys.time() < 62^6 * .99)
25-
expect_identical(nchar(format(ps_create_time(), "%OS8")), 9L)
26-
27-
# Roundtrip through ps_string
28-
str <- expect_silent(ps_string(ps))
29-
ps2 <- expect_silent(ps_handle(str))
30-
31-
# Got the same process back
32-
expect_true(ps_is_running(ps2))
33-
expect_identical(ps_pid(ps), ps_pid(ps2))
34-
expect_identical(ps_ppid(ps), ps_ppid(ps2))
35-
36-
# Invalid process
37-
str <- ps__str_encode(ps_pid(ps), ps_create_time(ps) + 1)
38-
ps3 <- expect_silent(ps_handle(str))
39-
expect_false(ps_is_running(ps3))
40-
})
41-
4219
test_that("pid", {
4320
## Argument check
4421
expect_error(ps_pid(123), class = "invalid_argument")

tests/testthat/test-z-string.R

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Named 'test-z-string' so it will run last.
2+
# Testing for errors that crop up due to differences
3+
# in start (load/attach) time for `ps`.
4+
5+
test_that("string", {
6+
ps <- ps_handle()
7+
8+
# Values satisfy encoding assumptions
9+
expect_true(all(ps_pids() < 52 * 62^3))
10+
expect_lt(Sys.time(), (62^8 / 1000) * 0.99)
11+
12+
# Roundtrip through ps_string
13+
str <- expect_silent(ps_string(ps))
14+
ps2 <- expect_silent(ps_handle(str))
15+
16+
# Got the same process back
17+
expect_true(ps_is_running(ps2))
18+
expect_identical(ps_pid(ps), ps_pid(ps2))
19+
expect_identical(ps_ppid(ps), ps_ppid(ps2))
20+
21+
# Invalid process
22+
ps2 <- expect_silent(ps_handle(ps_pid(ps), ps_create_time(ps) + 1))
23+
expect_false(ps_is_running(ps2))
24+
str <- expect_silent(ps_string(ps2))
25+
ps2 <- expect_silent(ps_handle(str))
26+
expect_false(ps_is_running(ps2))
27+
28+
})
29+
30+
31+
test_that("ipc string", {
32+
33+
skip_on_cran()
34+
skip_on_covr()
35+
36+
expect_true(
37+
callr::r(
38+
function(str) {
39+
ps <- ps::ps_handle(str)
40+
ps::ps_is_running(ps)
41+
},
42+
args = list(str = ps_string())
43+
)
44+
)
45+
46+
})

0 commit comments

Comments
 (0)