2021-09-18 09:23:56 -05:00
|
|
|
#include <dirent.h>
|
2021-05-17 22:33:51 -05:00
|
|
|
#include <errno.h>
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
#include <fcntl.h>
|
2021-09-18 09:23:56 -05:00
|
|
|
#include <limits.h>
|
2021-05-17 22:33:51 -05:00
|
|
|
#include <pthread.h>
|
2021-09-18 09:23:56 -05:00
|
|
|
#include <sched.h>
|
2021-09-18 12:58:33 -05:00
|
|
|
#include <stdatomic.h>
|
2022-06-12 21:10:34 -05:00
|
|
|
#include <stdbool.h>
|
2021-05-17 22:33:51 -05:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2021-09-18 09:23:56 -05:00
|
|
|
#include <string.h>
|
2022-06-12 21:10:34 -05:00
|
|
|
#include <sys/resource.h>
|
2022-06-12 19:59:45 -05:00
|
|
|
#include <threads.h>
|
2021-05-17 22:33:51 -05:00
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "erm.h"
|
|
|
|
|
2021-09-18 12:58:33 -05:00
|
|
|
#ifndef DEBUG
|
|
|
|
#define printf(...)
|
|
|
|
#define puts(s)
|
|
|
|
#endif
|
|
|
|
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
#define ACQUIRED (1U<<31)
|
|
|
|
|
2021-05-17 22:33:51 -05:00
|
|
|
struct task {
|
2021-09-18 09:23:56 -05:00
|
|
|
char *path;
|
2021-09-18 12:58:33 -05:00
|
|
|
struct task *parent;
|
|
|
|
/* reference counting */
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
unsigned files;
|
2022-06-12 21:10:34 -05:00
|
|
|
/* stores the dirfd for this path or -1 */
|
|
|
|
int dfd;
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
atomic_uint removed_count;
|
2021-09-18 09:23:56 -05:00
|
|
|
};
|
2021-05-17 22:33:51 -05:00
|
|
|
|
2021-10-02 23:36:22 -05:00
|
|
|
static struct queue {
|
2021-09-18 09:23:56 -05:00
|
|
|
pthread_mutex_t mtx;
|
2021-09-18 13:36:26 -05:00
|
|
|
pthread_cond_t cond;
|
2021-09-18 09:23:56 -05:00
|
|
|
struct task *tasks;
|
2021-10-02 23:32:48 -05:00
|
|
|
size_t len, size;
|
2021-09-18 13:47:06 -05:00
|
|
|
/* number of free threads */
|
2021-10-02 23:32:48 -05:00
|
|
|
unsigned free;
|
2021-10-02 23:36:22 -05:00
|
|
|
} queue = {.mtx = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
|
|
|
|
|
|
|
|
static pthread_mutex_t fd_mtx = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
static pthread_cond_t fd_cond = PTHREAD_COND_INITIALIZER;
|
2021-09-18 09:23:56 -05:00
|
|
|
|
2021-10-02 23:36:22 -05:00
|
|
|
static unsigned nproc;
|
2022-06-12 21:10:34 -05:00
|
|
|
/* is true if number of available fds is smaller than number of threads being used */
|
|
|
|
static bool limited_fds;
|
|
|
|
/* stores amount of additional fds that can be used beyond the one per thread */
|
|
|
|
static atomic_int dfd_max;
|
2021-09-20 08:40:33 -05:00
|
|
|
|
2022-06-12 19:59:45 -05:00
|
|
|
/* p_old is a struct task cache; this means each thread can leak one struct task in total */
|
|
|
|
static thread_local struct task *p_old = NULL;
|
|
|
|
|
2021-10-02 23:06:46 -05:00
|
|
|
static inline void queue_print(struct queue *q)
|
|
|
|
{
|
|
|
|
#ifdef DEBUGP
|
|
|
|
puts("begin========================");
|
|
|
|
for (size_t i=0; i < q->len; i++) {
|
|
|
|
printf("item %010zu: '%s'\n", i, q->tasks[i].path);
|
|
|
|
}
|
|
|
|
puts("end==========================");
|
|
|
|
#else
|
|
|
|
(void)q;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-01-21 12:41:13 -06:00
|
|
|
static inline void queue_add(struct queue *q, char *path, struct task *parent)
|
2021-05-17 22:33:51 -05:00
|
|
|
{
|
2021-09-18 09:23:56 -05:00
|
|
|
pthread_mutex_lock(&q->mtx);
|
|
|
|
if (q->len + 1 > q->size) {
|
|
|
|
q->size *= 2;
|
|
|
|
if (q->size == 0) q->size = 32;
|
2022-01-21 12:41:13 -06:00
|
|
|
q->tasks = realloc(q->tasks, q->size * sizeof(struct task));
|
|
|
|
if (!q->tasks) {
|
|
|
|
fprintf(stderr, "queue memory exhaustion: %m\n");
|
|
|
|
exit(1);
|
2021-09-18 09:23:56 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-21 12:41:13 -06:00
|
|
|
q->tasks[q->len++] = (struct task){.path = path, .parent = parent};
|
2021-09-18 09:23:56 -05:00
|
|
|
|
2021-09-18 13:36:26 -05:00
|
|
|
pthread_cond_signal(&q->cond);
|
2021-09-18 09:23:56 -05:00
|
|
|
pthread_mutex_unlock(&q->mtx);
|
2021-05-17 22:33:51 -05:00
|
|
|
}
|
|
|
|
|
2021-10-27 15:17:29 -05:00
|
|
|
static inline void queue_remove(struct queue *q, struct task *t)
|
2021-05-17 22:33:51 -05:00
|
|
|
{
|
2021-09-18 09:23:56 -05:00
|
|
|
pthread_mutex_lock(&q->mtx);
|
2021-09-18 13:36:26 -05:00
|
|
|
while (q->len == 0) {
|
2021-09-18 13:47:06 -05:00
|
|
|
if (q->free == nproc - 1) {
|
|
|
|
/* we are done removing things */
|
2022-01-21 13:05:56 -06:00
|
|
|
free(q->tasks);
|
2021-09-18 13:47:06 -05:00
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
q->free++;
|
2021-09-18 13:36:26 -05:00
|
|
|
pthread_cond_wait(&q->cond, &q->mtx);
|
2021-09-18 13:47:06 -05:00
|
|
|
q->free--;
|
2021-09-18 13:36:26 -05:00
|
|
|
}
|
2021-10-02 23:06:46 -05:00
|
|
|
queue_print(q);
|
2021-09-18 12:58:33 -05:00
|
|
|
*t = q->tasks[--(q->len)];
|
2021-09-18 09:23:56 -05:00
|
|
|
|
|
|
|
/* the caller owns the path buffer now */
|
2021-10-27 15:17:29 -05:00
|
|
|
|
2021-09-18 09:23:56 -05:00
|
|
|
pthread_mutex_unlock(&q->mtx);
|
2021-05-17 22:33:51 -05:00
|
|
|
}
|
2021-09-18 09:23:56 -05:00
|
|
|
|
2022-06-12 21:10:34 -05:00
|
|
|
static void close_dfd(const struct task *t)
|
|
|
|
{
|
|
|
|
if (t->dfd == -1) return;
|
|
|
|
close(t->dfd);
|
|
|
|
atomic_fetch_add_explicit(&dfd_max, 1, memory_order_relaxed);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rmdir_parent(const struct task *t)
|
|
|
|
{
|
|
|
|
int rfd = (t->parent && t->parent->dfd != -1) ? t->parent->dfd : AT_FDCWD;
|
|
|
|
return unlinkat(rfd, t->path, AT_REMOVEDIR);
|
|
|
|
}
|
|
|
|
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
static inline void recurse_into_parents(struct task *t)
|
|
|
|
{
|
2022-06-12 19:59:45 -05:00
|
|
|
struct task *recurse = t, *free_list = NULL;
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
while ((recurse = recurse->parent)) {
|
|
|
|
free(free_list); free_list = NULL;
|
|
|
|
|
2022-06-12 19:25:34 -05:00
|
|
|
unsigned rc = atomic_fetch_add_explicit(&recurse->removed_count, 1, memory_order_acquire);
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
if (rc & ACQUIRED) break;
|
|
|
|
|
|
|
|
printf("parent: removed %04d total %04d '%s'\n", rc, recurse->files, recurse->path);
|
|
|
|
if (rc == recurse->files) {
|
|
|
|
/* we have removed all files in the directory */
|
2022-06-12 21:10:34 -05:00
|
|
|
if (rmdir_parent(recurse)) {
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
fprintf(stderr, "rec rmdir failed '%s': %m\n", recurse->path);
|
|
|
|
} else {
|
|
|
|
printf("rec rmdir succeeded '%s'\n", recurse->path);
|
|
|
|
}
|
|
|
|
free(recurse->path);
|
2022-06-12 19:59:45 -05:00
|
|
|
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
/* can't free now because the while condition uses it */
|
2022-06-12 21:10:34 -05:00
|
|
|
close_dfd(recurse);
|
2022-06-12 19:59:45 -05:00
|
|
|
if (p_old) free_list = recurse;
|
|
|
|
else p_old = recurse;
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
} else {
|
|
|
|
/* if we haven't removed this directory yet,
|
|
|
|
* there's no reason to recurse further */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* catch any stragglers, in case the loop doesn't iterate once more */
|
|
|
|
free(free_list);
|
|
|
|
}
|
|
|
|
|
2021-09-18 09:23:56 -05:00
|
|
|
static void *process_queue_item(void *arg)
|
2021-05-17 22:33:51 -05:00
|
|
|
{
|
2021-09-18 09:23:56 -05:00
|
|
|
struct queue *q = arg;
|
2022-06-12 19:59:45 -05:00
|
|
|
struct task t;
|
2021-09-18 09:23:56 -05:00
|
|
|
while (1) {
|
2021-09-18 13:36:26 -05:00
|
|
|
queue_remove(q, &t);
|
2021-09-18 09:23:56 -05:00
|
|
|
|
2022-01-21 12:27:32 -06:00
|
|
|
int dfd;
|
2022-06-12 21:10:34 -05:00
|
|
|
int rfd = (t.parent && t.parent->dfd != -1) ? t.parent->dfd : AT_FDCWD;
|
|
|
|
while ((dfd = openat(rfd, t.path, O_RDONLY|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC)) < 0) {
|
|
|
|
if (limited_fds && errno == EMFILE) {
|
2021-09-20 08:40:33 -05:00
|
|
|
pthread_mutex_lock(&fd_mtx);
|
|
|
|
pthread_cond_wait(&fd_cond, &fd_mtx);
|
|
|
|
pthread_mutex_unlock(&fd_mtx);
|
2021-09-18 09:23:56 -05:00
|
|
|
continue;
|
|
|
|
} else {
|
2022-01-21 12:41:13 -06:00
|
|
|
fprintf(stderr, "couldn't open '%s': %m\n", t.path);
|
|
|
|
exit(1);
|
2021-09-18 09:23:56 -05:00
|
|
|
}
|
|
|
|
}
|
2022-01-21 12:27:32 -06:00
|
|
|
DIR *d = fdopendir(dfd);
|
2022-01-21 12:59:41 -06:00
|
|
|
if (!d) {
|
|
|
|
fprintf(stderr, "couldn't create directory stream: %m\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
2021-09-18 12:58:33 -05:00
|
|
|
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
struct task *p = NULL;
|
|
|
|
unsigned n = 0;
|
2022-06-12 19:32:15 -05:00
|
|
|
size_t plen;
|
2021-09-18 14:24:30 -05:00
|
|
|
struct dirent *entry;
|
|
|
|
while ((entry = readdir(d))) {
|
2022-06-12 19:29:22 -05:00
|
|
|
if (entry->d_name[0] == '.' &&
|
|
|
|
(entry->d_name[1] == '\0' ||
|
|
|
|
(entry->d_name[1] == '.' && entry->d_name[2] == '\0')))
|
|
|
|
continue;
|
2021-09-18 14:24:30 -05:00
|
|
|
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
/* fast path to avoid allocations */
|
|
|
|
int trv;
|
|
|
|
if (entry->d_type == DT_DIR) goto fast_path_dir;
|
|
|
|
if ((trv = unlinkat(dfd, entry->d_name, 0)) && errno == EISDIR) {
|
|
|
|
fast_path_dir:
|
|
|
|
trv = unlinkat(dfd, entry->d_name, AT_REMOVEDIR);
|
|
|
|
}
|
|
|
|
if (!trv) continue;
|
|
|
|
|
2021-09-18 14:24:30 -05:00
|
|
|
n++;
|
|
|
|
|
2022-06-12 19:32:15 -05:00
|
|
|
/* lazy allocation of p and other operations */
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
if (!p) {
|
2022-06-12 19:33:57 -05:00
|
|
|
if (p_old) {
|
|
|
|
p = p_old;
|
|
|
|
p_old = NULL;
|
|
|
|
puts("used p_old");
|
|
|
|
} else {
|
|
|
|
p = malloc(sizeof *p);
|
2022-06-12 19:59:45 -05:00
|
|
|
puts("didn't use p_old");
|
2022-06-12 19:33:57 -05:00
|
|
|
}
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
*p = t;
|
2022-06-12 21:10:34 -05:00
|
|
|
p->dfd = -1;
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
/* access happens only after mutex lock and release */
|
|
|
|
atomic_store_explicit(&p->removed_count, ACQUIRED, memory_order_relaxed);
|
2022-06-12 19:32:15 -05:00
|
|
|
|
2022-06-12 21:10:34 -05:00
|
|
|
if (!limited_fds) {
|
|
|
|
if (atomic_fetch_sub_explicit(&dfd_max, 1, memory_order_relaxed) > 0) {
|
|
|
|
/* we need to duplicate the fd due to calling closedir() below */
|
|
|
|
p->dfd = dup(dfd);
|
|
|
|
} else {
|
|
|
|
atomic_fetch_add_explicit(&dfd_max, 1, memory_order_relaxed);
|
|
|
|
/* we only use plen for absolute paths */
|
|
|
|
plen = strlen(t.path);
|
|
|
|
}
|
|
|
|
}
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
}
|
|
|
|
|
2022-06-12 21:10:34 -05:00
|
|
|
char *buf;
|
|
|
|
if (p->dfd == -1) {
|
|
|
|
size_t nlen = strlen(entry->d_name);
|
|
|
|
buf = malloc(plen + nlen + 2);
|
|
|
|
memcpy(buf, p->path, plen);
|
|
|
|
buf[plen] = '/';
|
|
|
|
memcpy(buf+plen+1, entry->d_name, nlen);
|
|
|
|
buf[plen+nlen+1] = '\0';
|
|
|
|
} else {
|
|
|
|
buf = strdup(entry->d_name);
|
|
|
|
}
|
2021-09-18 12:58:33 -05:00
|
|
|
|
|
|
|
printf("adding to queue'%s'\n", buf);
|
2021-10-02 23:28:30 -05:00
|
|
|
queue_add(q, buf, p);
|
2021-09-18 09:23:56 -05:00
|
|
|
}
|
2021-09-18 14:24:30 -05:00
|
|
|
closedir(d);
|
2022-06-12 21:10:34 -05:00
|
|
|
if (limited_fds) {
|
|
|
|
pthread_mutex_lock(&fd_mtx);
|
|
|
|
pthread_cond_signal(&fd_cond);
|
|
|
|
pthread_mutex_unlock(&fd_mtx);
|
|
|
|
}
|
2021-09-18 14:24:30 -05:00
|
|
|
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
if (p) {
|
|
|
|
p->files = n-1; /* other thread will compare against removed_count-1 */
|
2022-06-12 19:25:34 -05:00
|
|
|
unsigned rc = atomic_fetch_and_explicit(&p->removed_count, ~ACQUIRED, memory_order_release);
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
if (rc == (n|ACQUIRED)) {
|
2022-06-12 19:25:34 -05:00
|
|
|
/* this branch is taken when other threads have already removed all of p's children */
|
2022-06-12 21:10:34 -05:00
|
|
|
close_dfd(p);
|
2022-06-12 19:59:45 -05:00
|
|
|
if (p_old) free(p);
|
|
|
|
else p_old = p;
|
|
|
|
|
2022-06-12 21:10:34 -05:00
|
|
|
if (rmdir_parent(&t)) {
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
fprintf(stderr, "atomic rmdir failed '%s': %m\n", t.path);
|
2021-09-18 12:58:33 -05:00
|
|
|
} else {
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
printf("atomic rmdir succeeded '%s'\n", t.path);
|
2021-09-18 12:58:33 -05:00
|
|
|
}
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
} else {
|
2022-06-12 19:25:34 -05:00
|
|
|
/* we can't recurse into p's parent if p still has children that need to be removed */
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* p wasn't set because we could delete everything inside it */
|
2022-06-12 21:10:34 -05:00
|
|
|
if (rmdir_parent(&t)) {
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
fprintf(stderr, "fast path rmdir failed '%s': %m\n", t.path);
|
|
|
|
} else {
|
|
|
|
printf("fast path rmdir succeeded '%s'\n", t.path);
|
2021-09-18 12:58:33 -05:00
|
|
|
}
|
|
|
|
}
|
Add fast path removal to avoid allocations.
Instead of always adding files to the queue, we can try to remove them
in the readdir loop. This allows us to:
- add fewer items to the queue
- skip allocating and copying the path, since with the dir stream open
we can use unlinkat(2)
- allocate parent task lazily, since it might not be needed
- stop using a recursive mutex, which can be slightly more expensive
Doing this in a naive way lead to a slow down, since we were holding the
queue mutex during the entirety of the operation. Instead, it was
necessary to change the loop structure a lot in order to be able to add
items to the queue without knowing the number of entries in the
directory. It could have been calculated with a readdir(3) loop +
rewinddir(3), but that would have added a lot of syscalls. In order to
work around that, we changed the purpose of the atomic int in struct
task.
Now, removed_count holds how many entries from the directory were
removed and a flag in its most significant byte to signal whether we are
still adding entries to the queue that refer to it or not. This flag,
plus some fancy atomic operations, allow us to control whether the
directory cleanup should happen in the thread that was adding its
entries to the queue or in the thread that removes the last item from
the queue.
We consider it safe to use the most significant bit of the unsigned int
as a flag because scandir(3) returns a signed int for the number of
entries in a directory.
2021-10-02 23:08:18 -05:00
|
|
|
|
|
|
|
if (t.parent) recurse_into_parents(&t);
|
2021-09-18 09:23:56 -05:00
|
|
|
/* we took ownership of the buffer */
|
|
|
|
free(t.path);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
2021-05-17 22:33:51 -05:00
|
|
|
}
|
|
|
|
|
2022-01-21 12:41:13 -06:00
|
|
|
static void exit_init(void)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "thread initialization failed: %m\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void run_queue(void)
|
2021-05-17 22:33:51 -05:00
|
|
|
{
|
2021-10-02 23:32:48 -05:00
|
|
|
long nproc_l = sysconf(_SC_NPROCESSORS_ONLN);
|
|
|
|
if (nproc_l < 1) nproc_l = 1;
|
|
|
|
if (nproc_l > 64) nproc_l = 64;
|
|
|
|
nproc = nproc_l;
|
2021-09-18 13:36:26 -05:00
|
|
|
|
2022-06-12 21:10:34 -05:00
|
|
|
struct rlimit rl;
|
|
|
|
if (getrlimit(RLIMIT_NOFILE, &rl)) exit_init();
|
|
|
|
/* soft limit minus open std streams and minus directory fds from each thread */
|
|
|
|
if (rl.rlim_cur < nproc + 2) limited_fds = true;
|
|
|
|
else atomic_store_explicit(&dfd_max, rl.rlim_cur - 2 - nproc, memory_order_relaxed);
|
2022-01-21 12:41:13 -06:00
|
|
|
|
|
|
|
/* main thread will also be a task */
|
|
|
|
unsigned nproc1 = nproc - 1;
|
|
|
|
if (nproc1) {
|
|
|
|
pthread_attr_t pattr;
|
|
|
|
if (pthread_attr_init(&pattr)) exit_init();
|
2021-09-18 14:24:30 -05:00
|
|
|
#if defined(PTHREAD_STACK_MIN)
|
2022-01-21 12:41:13 -06:00
|
|
|
if (pthread_attr_setstacksize(&pattr, PTHREAD_STACK_MIN) ||
|
|
|
|
pthread_attr_setguardsize(&pattr, 1)) exit_init();
|
2021-09-18 14:24:30 -05:00
|
|
|
#endif
|
2021-09-18 13:19:59 -05:00
|
|
|
|
2022-01-21 12:47:20 -06:00
|
|
|
for (unsigned i = 0; i < nproc1; i++) {
|
|
|
|
pthread_t thread;
|
|
|
|
if (pthread_create(&thread, &pattr, process_queue_item, &queue)) exit_init();
|
|
|
|
pthread_detach(thread);
|
|
|
|
}
|
2022-01-21 12:41:13 -06:00
|
|
|
|
|
|
|
pthread_attr_destroy(&pattr);
|
2021-05-17 22:33:51 -05:00
|
|
|
}
|
|
|
|
|
2022-01-21 12:41:13 -06:00
|
|
|
/* become one of the worker threads */
|
|
|
|
process_queue_item(&queue);
|
2021-05-17 22:33:51 -05:00
|
|
|
}
|
|
|
|
|
2022-01-21 12:41:13 -06:00
|
|
|
static void fail_single_file(const char *path)
|
2021-05-17 22:33:51 -05:00
|
|
|
{
|
2022-01-21 12:41:13 -06:00
|
|
|
fprintf(stderr, "failed to remove '%s': %m\n", path);
|
2021-05-17 22:33:51 -05:00
|
|
|
}
|
2021-09-18 21:48:56 -05:00
|
|
|
|
2022-01-21 12:41:13 -06:00
|
|
|
int single_file(const char *path)
|
2021-09-18 21:48:56 -05:00
|
|
|
{
|
2022-01-21 12:41:13 -06:00
|
|
|
int rv = remove(path);
|
|
|
|
if (rv) fail_single_file(path);
|
|
|
|
return rv;
|
|
|
|
}
|
2021-10-02 23:28:30 -05:00
|
|
|
|
2022-01-21 12:41:13 -06:00
|
|
|
void recurse_into(const char *path, int stop_at_error)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
2021-09-18 21:48:56 -05:00
|
|
|
}
|