Skip to content

Commit 670c3cb

Browse files
committed
resource fixes
1 parent 1a6ecdf commit 670c3cb

File tree

6 files changed

+86
-43
lines changed

6 files changed

+86
-43
lines changed

R/watch.R

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
#' `NULL`, causes event paths and types to be written to `stdout` instead.
1616
#'
1717
#' @return A 'Watcher' R6 class object. Start and stop background monitoring
18-
#' using the `$start()` and `$stop()` methods.
18+
#' using the `$start()` and `$stop()` methods - these return a logical value
19+
#' whether or not they have succeeded.
1920
#'
2021
#' @examples
2122
#' w <- watcher(tempdir())
@@ -47,13 +48,19 @@ Watcher <- R6Class(
4748
invisible(self)
4849
},
4950
start = function() {
50-
res <- .Call(watcher_start_monitor, private$watch)
51-
if (res) self$active <- TRUE
52-
invisible(res)
51+
res <- self$active
52+
if (!res) {
53+
self$active <- .Call(watcher_start_monitor, private$watch)
54+
res <- !self$active
55+
}
56+
invisible(!res)
5357
},
5458
stop = function() {
55-
res <- invisible(.Call(watcher_stop_monitor, private$watch))
56-
if (res) self$active <- FALSE
59+
res <- self$active
60+
if (res) {
61+
res <- .Call(watcher_stop_monitor, private$watch)
62+
self$active <- !res
63+
}
5764
invisible(res)
5865
}
5966
),

configure.win

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#!/bin/sh
2-
31
LIB_VER="1.18.2"
42

53
# Find compiler and export flags

man/watcher-package.Rd

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/watcher.Rd

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

src/watcher.c

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#include "watcher.h"
22

3-
static void Wprintf(const int err, const char *fmt, ...) {
3+
static void Wprintf(const char *fmt, ...) {
44

55
char buf[WATCHER_BUFSIZE];
66
va_list arg_ptr;
@@ -9,11 +9,11 @@ static void Wprintf(const int err, const char *fmt, ...) {
99
int bytes = vsnprintf(buf, WATCHER_BUFSIZE, fmt, arg_ptr);
1010
va_end(arg_ptr);
1111

12-
if (write(err ? STDERR_FILENO : STDOUT_FILENO, buf, (size_t) bytes)) {};
12+
if (write(STDOUT_FILENO, buf, (size_t) bytes)) {};
1313

1414
}
1515

16-
void session_finalizer(SEXP xptr) {
16+
static void session_finalizer(SEXP xptr) {
1717
if (R_ExternalPtrAddr(xptr) == NULL) return;
1818
FSW_HANDLE handle = (FSW_HANDLE) R_ExternalPtrAddr(xptr);
1919
fsw_stop_monitor(handle);
@@ -22,38 +22,53 @@ void session_finalizer(SEXP xptr) {
2222

2323
void (*eln2)(void (*)(void *), void *, double, int) = NULL;
2424

25-
void load_later_safe(void *data) {
26-
(void) data;
27-
SEXP fn, call;
28-
fn = Rf_install("loadNamespace");
25+
static void load_later_safe(void *data) {
26+
SEXP call, fn = (SEXP) data;
2927
PROTECT(call = Rf_lang2(fn, Rf_mkString("later")));
3028
Rf_eval(call, R_GlobalEnv);
3129
UNPROTECT(1);
3230
}
3331

34-
void exec_later(void *data) {
32+
static void exec_later(void *data) {
3533
SEXP call, fn = (SEXP) data;
3634
PROTECT(call = Rf_lcons(fn, R_NilValue));
3735
Rf_eval(call, R_GlobalEnv);
3836
UNPROTECT(1);
3937
}
4038

41-
void process_events(fsw_cevent const *const events, const unsigned int event_num, void *data) {
39+
// custom non-allocating version of fsw_get_event_flag_name() handling main types
40+
static void get_event_flag_name(const int flag, char *buf) {
41+
const char *name;
42+
switch(flag) {
43+
case 1 << 1: name = "Created"; break;
44+
case 1 << 2: name = "Updated"; break;
45+
case 1 << 3: name = "Removed"; break;
46+
case 1 << 4: name = "Renamed"; break;
47+
default: name = "Unknown"; break;
48+
}
49+
memcpy(buf, name, strlen(name));
50+
}
51+
52+
static void process_events(fsw_cevent const *const events, const unsigned int event_num, void *data) {
4253
if (data != R_NilValue && eln2 != NULL) {
4354
eln2(exec_later, data, 0, 0);
4455
} else {
45-
for (unsigned int i = 0; i < event_num; i++)
46-
for (unsigned int j = 0; j < events[i].flags_num; j++)
47-
Wprintf(0, "Event: %s\n Flag: %s\n", events[i].path, fsw_get_event_flag_name(events[i].flags[j]));
56+
char buf[8]; // large enough for subset of events handled by get_event_flag_name()
57+
memset(buf, 0, sizeof(buf));
58+
for (unsigned int i = 0; i < event_num; i++) {
59+
for (unsigned int j = 0; j < events[i].flags_num; j++) {
60+
get_event_flag_name(events[i].flags[j], buf);
61+
Wprintf("Event: %s\n Flag: %s\n", events[i].path, buf);
62+
}
63+
}
4864
}
4965
}
5066

51-
void* watcher_thread(void *args) {
67+
static void * watcher_thread(void *args) {
5268

53-
pthread_detach(pthread_self());
5469
FSW_HANDLE handle = (FSW_HANDLE) args;
5570
fsw_start_monitor(handle);
56-
return (void *) NULL;
71+
return NULL;
5772

5873
}
5974

@@ -84,6 +99,7 @@ SEXP watcher_create(SEXP path, SEXP recursive, SEXP callback) {
8499
Rf_error("Failed to set watch callback");
85100
}
86101

102+
// filter only for main event types: Created, Updated, Removed, Renamed
87103
fsw_event_type_filter filter;
88104
for (int flag = Created; flag <= Renamed; flag = flag << 1) {
89105
filter.flag = flag;
@@ -106,12 +122,18 @@ SEXP watcher_start_monitor(SEXP session) {
106122

107123
FSW_HANDLE handle = (FSW_HANDLE) R_ExternalPtrAddr(session);
108124
pthread_t thr;
125+
pthread_attr_t attr;
126+
127+
pthread_attr_init(&attr);
128+
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
109129

110-
if (eln2 == NULL && R_ToplevelExec(load_later_safe, NULL)) {
130+
if (eln2 == NULL && R_ToplevelExec(load_later_safe, (void *) Rf_install("loadNamespace"))) {
111131
eln2 = (void (*)(void (*)(void *), void *, double, int)) R_GetCCallable("later", "execLaterNative2");
112132
}
133+
const int ret = pthread_create(&thr, &attr, &watcher_thread, handle);
134+
pthread_attr_destroy(&attr);
113135

114-
return Rf_ScalarLogical(pthread_create(&thr, NULL, &watcher_thread, handle) == 0);
136+
return Rf_ScalarLogical(ret == 0);
115137

116138
}
117139

tests/testthat/test-watch.R

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
1-
test_that("watcher() watches", {
2-
x <- 0L
3-
dir <- file.path(tempdir(), "watcher-test")
4-
subdir <- file.path(dir, "subdir")
5-
dir.create(dir)
6-
dir.create(subdir)
1+
dir <- file.path(tempdir(), "watcher-test")
2+
subdir <- file.path(dir, "subdir")
3+
dir.create(dir)
4+
dir.create(subdir)
5+
6+
test_that("watcher() logs", {
77
w <- watcher(dir, recursive = FALSE, callback = NULL)
88
expect_s3_class(w, "Watcher")
99
expect_false(w$recursive)
1010
expect_false(w$active)
11+
expect_false(w$stop())
1112
expect_true(w$start())
1213
expect_true(w$active)
13-
Sys.sleep(1L)
14+
expect_type(w$path, "character")
15+
Sys.sleep(1)
1416
file.create(file.path(dir, "testfile"))
15-
Sys.sleep(1L)
17+
file.remove(file.path(dir, "testfile"))
18+
Sys.sleep(1)
19+
expect_false(w$start())
1620
expect_true(w$stop())
21+
expect_false(w$stop())
1722
expect_false(w$active)
23+
rm(w)
24+
})
25+
26+
test_that("watcher() callbacks", {
27+
x <- 0L
1828
w <- watcher(dir, recursive = TRUE, callback = function() x <<- x + 1L)
1929
expect_output(print(w))
2030
expect_s3_class(w, "Watcher")
@@ -23,27 +33,32 @@ test_that("watcher() watches", {
2333
skip_if_not_installed("later")
2434
expect_true(w$start())
2535
expect_true(w$active)
26-
Sys.sleep(1L)
36+
Sys.sleep(1)
2737
file.create(file.path(subdir, "oldfile"))
28-
later::run_now(2L)
38+
later::run_now(2)
2939
expect_equal(x, 1L)
3040
file.rename(file.path(subdir, "oldfile"), file.path(subdir, "newfile"))
31-
later::run_now(2L)
41+
later::run_now(2)
3242
expect_equal(x, 2L)
3343
file.remove(file.path(subdir, "newfile"))
34-
later::run_now(2L)
44+
later::run_now(2)
3545
expect_equal(x, 3L)
3646
file.create(file.path(dir, "oldfile"))
37-
later::run_now(2L)
47+
later::run_now(2)
3848
expect_equal(x, 4L)
3949
file.rename(file.path(dir, "oldfile"), file.path(dir, "newfile"))
40-
later::run_now(2L)
50+
later::run_now(2)
4151
expect_equal(x, 5L)
4252
file.remove(file.path(dir, "newfile"))
43-
later::run_now(2L)
53+
later::run_now(2)
4454
expect_equal(x, 6L)
4555
expect_true(w$stop())
4656
expect_false(w$active)
47-
unlink(dir, recursive = TRUE, force = TRUE)
57+
rm(w)
58+
})
59+
60+
test_that("watcher() validation", {
4861
expect_error(watcher(callback = "callback"), "must be a function")
4962
})
63+
64+
unlink(dir, recursive = TRUE, force = TRUE)

0 commit comments

Comments
 (0)