|
1 | 1 | #' Encode a `ps_handle` as a short string |
2 | 2 | #' |
3 | 3 | #' 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, |
5 | 5 | #' with the first character guarantied to be a letter. Encodes the pid and |
6 | 6 | #' creation time for a process. |
7 | 7 | #' |
|
18 | 18 |
|
19 | 19 | ps_string <- function(p = ps_handle()) { |
20 | 20 | assert_ps_handle(p) |
21 | | - ps__str_encode(ps_pid(p), ps_create_time(p)) |
| 21 | + ps__str_encode(p) |
22 | 22 | } |
23 | 23 |
|
24 | 24 |
|
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) { |
28 | 26 |
|
29 | 27 | # 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. |
33 | 32 |
|
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()` |
35 | 37 |
|
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 |
37 | 45 |
|
| 46 | + map <- c(letters, LETTERS, 0:9) |
38 | 47 | paste( |
39 | 48 | collapse = '', |
40 | 49 | map[ |
41 | 50 | 1 + |
42 | 51 | 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 |
46 | 54 | ) |
47 | 55 | ] |
48 | 56 | ) |
49 | 57 | } |
50 | 58 |
|
51 | 59 |
|
52 | 60 | ps__str_decode <- function(str) { |
| 61 | + |
53 | 62 | map <- structure(0:61, names = c(letters, LETTERS, 0:9)) |
54 | 63 | val <- map[strsplit(str, '', fixed = TRUE)[[1]]] |
| 64 | + pid <- sum(val[01:04] * 62^(3:0)) |
55 | 65 |
|
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 |
64 | 66 | tryCatch( |
65 | 67 | 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)) |
68 | 70 | p |
69 | 71 | }, |
70 | 72 | 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)) |
72 | 80 | } |
73 | 81 | ) |
74 | 82 | } |
0 commit comments