mirror of https://github.com/ericonr/erm.git
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:
parent
3f72eb9f5f
commit
5b4396c4a1
22
erm.c
22
erm.c
|
@ -40,24 +40,22 @@ int main(int argc, char **argv)
|
||||||
usage(1);
|
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;
|
int rv = 0;
|
||||||
for (int i = 0; i < argc; i++) {
|
for (int i = 0; i < argc; i++) {
|
||||||
const char *path = argv[i];
|
const char *path = argv[i];
|
||||||
if (action(path)) {
|
if (recursive) {
|
||||||
fprintf(stderr, err_fmt, path, strerror(errno));
|
recurse_into(path, stop_at_error);
|
||||||
if (stop_at_error) {
|
} else {
|
||||||
return 1;
|
if (single_file(path)) {
|
||||||
} else {
|
if (stop_at_error) {
|
||||||
rv = 1;
|
return 1;
|
||||||
|
} else {
|
||||||
|
rv = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (callback) callback();
|
if (recursive) run_queue();
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
7
erm.h
7
erm.h
|
@ -1,7 +1,4 @@
|
||||||
typedef int (*file_action)(const char *path);
|
|
||||||
typedef int (*callback_action)(void);
|
|
||||||
|
|
||||||
/* remove.c */
|
/* remove.c */
|
||||||
int recurse_into(const char *);
|
void recurse_into(const char *, int);
|
||||||
|
void run_queue(void);
|
||||||
int single_file(const char *);
|
int single_file(const char *);
|
||||||
int run_queue(void);
|
|
||||||
|
|
92
remove.c
92
remove.c
|
@ -54,30 +54,23 @@ static inline void queue_print(struct queue *q)
|
||||||
#endif
|
#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);
|
pthread_mutex_lock(&q->mtx);
|
||||||
if (q->len + 1 > q->size) {
|
if (q->len + 1 > q->size) {
|
||||||
q->size *= 2;
|
q->size *= 2;
|
||||||
if (q->size == 0) q->size = 32;
|
if (q->size == 0) q->size = 32;
|
||||||
void *t = realloc(q->tasks, q->size * sizeof(struct task));
|
q->tasks = realloc(q->tasks, q->size * sizeof(struct task));
|
||||||
if (!t) {
|
if (!q->tasks) {
|
||||||
rv = -1;
|
fprintf(stderr, "queue memory exhaustion: %m\n");
|
||||||
goto error;
|
exit(1);
|
||||||
}
|
}
|
||||||
q->tasks = t;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct task t = {.path = path, .parent = parent};
|
q->tasks[q->len++] = (struct task){.path = path, .parent = parent};
|
||||||
q->tasks[q->len++] = t;
|
|
||||||
|
|
||||||
pthread_cond_signal(&q->cond);
|
pthread_cond_signal(&q->cond);
|
||||||
|
|
||||||
error:
|
|
||||||
pthread_mutex_unlock(&q->mtx);
|
pthread_mutex_unlock(&q->mtx);
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void queue_remove(struct queue *q, struct task *t)
|
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);
|
pthread_mutex_unlock(&fd_mtx);
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
break;
|
fprintf(stderr, "couldn't open '%s': %m\n", t.path);
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DIR *d = fdopendir(dfd);
|
DIR *d = fdopendir(dfd);
|
||||||
|
@ -222,51 +216,65 @@ fast_path_dir:
|
||||||
return NULL;
|
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);
|
long nproc_l = sysconf(_SC_NPROCESSORS_ONLN);
|
||||||
if (nproc_l < 1) nproc_l = 1;
|
if (nproc_l < 1) nproc_l = 1;
|
||||||
if (nproc_l > 64) nproc_l = 64;
|
if (nproc_l > 64) nproc_l = 64;
|
||||||
nproc = nproc_l;
|
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 defined(PTHREAD_STACK_MIN)
|
||||||
if (pthread_attr_setstacksize(&pattr, PTHREAD_STACK_MIN)) return -1;
|
if (pthread_attr_setstacksize(&pattr, PTHREAD_STACK_MIN) ||
|
||||||
if (pthread_attr_setguardsize(&pattr, 1)) return -1;
|
pthread_attr_setguardsize(&pattr, 1)) exit_init();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pthread_t *threads = calloc(sizeof(pthread_t), nproc);
|
pthread_t *threads = calloc(sizeof(pthread_t), nproc1);
|
||||||
if (!threads) return -1;
|
if (!threads) exit_init();
|
||||||
|
|
||||||
unsigned i, j = 0;
|
for (unsigned i = 0; i < nproc1; i++)
|
||||||
for (i = 0; i < nproc; i++) {
|
if (pthread_create(threads+i, &pattr, process_queue_item, &queue)) exit_init();
|
||||||
if (pthread_create(threads+i, &pattr, process_queue_item, &queue)) {
|
|
||||||
j = 1;
|
pthread_attr_destroy(&pattr);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
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 (!remove(path)) {
|
||||||
if (errno==ENOTEMPTY) return queue_add(&queue, strdup(path), NULL);
|
return;
|
||||||
|
} else if (errno == ENOTEMPTY) {
|
||||||
return 1;
|
queue_add(&queue, strdup(path), NULL);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
fail_single_file(path);
|
||||||
|
if (stop_at_error) exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue