Improve error handling and error messages.

- Rrror out clearly on most allocation failures (the others will simply
  segfault). Avoid getting into weird program conditions in case some
  operations fail.
- Improve organization of main(), no function pointer usage should be
  necessary.
- Make main thread also a worker thread: avoid leaving a useless thread
  around simply waiting for others to complete. Also means one less
  thread to launch.
This commit is contained in:
Érico Nogueira 2022-01-21 15:41:13 -03:00
parent 3f72eb9f5f
commit 5b4396c4a1
3 changed files with 62 additions and 59 deletions

22
erm.c
View File

@ -40,24 +40,22 @@ int main(int argc, char **argv)
usage(1);
}
file_action action = recursive ? recurse_into : single_file;
callback_action callback = recursive ? run_queue : NULL;
const char *err_fmt = recursive ?
"failed to queue '%s': %s\n" : "failed to remove '%s': %s\n";
int rv = 0;
for (int i = 0; i < argc; i++) {
const char *path = argv[i];
if (action(path)) {
fprintf(stderr, err_fmt, path, strerror(errno));
if (stop_at_error) {
return 1;
} else {
rv = 1;
if (recursive) {
recurse_into(path, stop_at_error);
} else {
if (single_file(path)) {
if (stop_at_error) {
return 1;
} else {
rv = 1;
}
}
}
}
if (callback) callback();
if (recursive) run_queue();
return rv;
}

7
erm.h
View File

@ -1,7 +1,4 @@
typedef int (*file_action)(const char *path);
typedef int (*callback_action)(void);
/* remove.c */
int recurse_into(const char *);
void recurse_into(const char *, int);
void run_queue(void);
int single_file(const char *);
int run_queue(void);

View File

@ -54,30 +54,23 @@ static inline void queue_print(struct queue *q)
#endif
}
static inline int queue_add(struct queue *q, char *path, struct task *parent)
static inline void queue_add(struct queue *q, char *path, struct task *parent)
{
int rv = 0;
pthread_mutex_lock(&q->mtx);
if (q->len + 1 > q->size) {
q->size *= 2;
if (q->size == 0) q->size = 32;
void *t = realloc(q->tasks, q->size * sizeof(struct task));
if (!t) {
rv = -1;
goto error;
q->tasks = realloc(q->tasks, q->size * sizeof(struct task));
if (!q->tasks) {
fprintf(stderr, "queue memory exhaustion: %m\n");
exit(1);
}
q->tasks = t;
}
struct task t = {.path = path, .parent = parent};
q->tasks[q->len++] = t;
q->tasks[q->len++] = (struct task){.path = path, .parent = parent};
pthread_cond_signal(&q->cond);
error:
pthread_mutex_unlock(&q->mtx);
return rv;
}
static inline void queue_remove(struct queue *q, struct task *t)
@ -146,7 +139,8 @@ static void *process_queue_item(void *arg)
pthread_mutex_unlock(&fd_mtx);
continue;
} else {
break;
fprintf(stderr, "couldn't open '%s': %m\n", t.path);
exit(1);
}
}
DIR *d = fdopendir(dfd);
@ -222,51 +216,65 @@ fast_path_dir:
return NULL;
}
int run_queue(void)
static void exit_init(void)
{
fprintf(stderr, "thread initialization failed: %m\n");
exit(1);
}
void run_queue(void)
{
long nproc_l = sysconf(_SC_NPROCESSORS_ONLN);
if (nproc_l < 1) nproc_l = 1;
if (nproc_l > 64) nproc_l = 64;
nproc = nproc_l;
pthread_attr_t pattr;
if (pthread_attr_init(&pattr)) return -1;
/* main thread will also be a task */
unsigned nproc1 = nproc - 1;
if (nproc1) {
pthread_attr_t pattr;
if (pthread_attr_init(&pattr)) exit_init();
#if defined(PTHREAD_STACK_MIN)
if (pthread_attr_setstacksize(&pattr, PTHREAD_STACK_MIN)) return -1;
if (pthread_attr_setguardsize(&pattr, 1)) return -1;
if (pthread_attr_setstacksize(&pattr, PTHREAD_STACK_MIN) ||
pthread_attr_setguardsize(&pattr, 1)) exit_init();
#endif
pthread_t *threads = calloc(sizeof(pthread_t), nproc);
if (!threads) return -1;
pthread_t *threads = calloc(sizeof(pthread_t), nproc1);
if (!threads) exit_init();
unsigned i, j = 0;
for (i = 0; i < nproc; i++) {
if (pthread_create(threads+i, &pattr, process_queue_item, &queue)) {
j = 1;
break;
}
}
pthread_attr_destroy(&pattr);
/* if creating threads fails, cancell all the already created ones */
if (j) for (j = 0; j < i; j++) {
pthread_cancel(threads[j]);
}
for (j = 0; j < i; j++) {
pthread_join(threads[j], NULL);
for (unsigned i = 0; i < nproc1; i++)
if (pthread_create(threads+i, &pattr, process_queue_item, &queue)) exit_init();
pthread_attr_destroy(&pattr);
}
return 0;
/* become one of the worker threads */
process_queue_item(&queue);
}
static void fail_single_file(const char *path)
{
fprintf(stderr, "failed to remove '%s': %m\n", path);
}
int single_file(const char *path)
{
return remove(path);
int rv = remove(path);
if (rv) fail_single_file(path);
return rv;
}
int recurse_into(const char *path)
void recurse_into(const char *path, int stop_at_error)
{
if (!remove(path)) return 0;
if (errno==ENOTEMPTY) return queue_add(&queue, strdup(path), NULL);
return 1;
if (!remove(path)) {
return;
} else if (errno == ENOTEMPTY) {
queue_add(&queue, strdup(path), NULL);
return;
} else {
fail_single_file(path);
if (stop_at_error) exit(1);
}
}