fix: close remaining PR 208 review gaps
CI / lint (pull_request) Failing after 32s
CI / build-and-test (pull_request) Has been skipped
CI / sanitizers (address) (pull_request) Has been skipped
CI / sanitizers (undefined) (pull_request) Has been skipped
CI / fuzz-build (pull_request) Has been skipped
CI / coverage (pull_request) Has been skipped
CI / valgrind (pull_request) Has been skipped

This commit is contained in:
2026-08-10 17:26:00 +02:00
parent 5bc319f694
commit 7776a63d3d
11 changed files with 262 additions and 94 deletions
+50 -25
View File
@@ -28,6 +28,20 @@
/* Forward declaration for progress-reporting thread used in multithreaded send. */
static int progress_thread_fn(void* arg);
static void pipeline_cancel(PipelineContextSender* context) {
mtx_lock(&context->mutex_scanner);
mtx_lock(&context->mutex_loader);
atomic_store(&context->cancelled, true);
context->scanner_done = true;
context->loader_done = true;
cnd_broadcast(&context->condition_not_full_scanner);
cnd_broadcast(&context->condition_not_empty_scanner);
cnd_broadcast(&context->condition_not_full_loader);
cnd_broadcast(&context->condition_not_empty_loader);
mtx_unlock(&context->mutex_loader);
mtx_unlock(&context->mutex_scanner);
}
/* Print dry-run manifest showing files that would be transferred. Returns 0 on success. */
static int send_dry_run_manifest(Config* config) {
DirectoryScanner* scanner = directory_scanner_create(
@@ -344,6 +358,7 @@ static int send_chunks_multithreaded(void* pipeline_context) {
return ok ? thrd_success : thrd_error;
send_fail:
pipeline_cancel(context);
client_disconnect(client);
client_delete(client);
mtx_lock(&context->mutex_progress);
@@ -354,6 +369,7 @@ static int send_chunks_multithreaded(void* pipeline_context) {
if (send_chunk(client, current_chunk, context->config) != 0) {
fprintf(stderr, "Error: unexpected error while sending chunk\n");
chunk_destroy(current_chunk);
pipeline_cancel(context);
client_disconnect(client);
client_delete(client);
mtx_lock(&context->mutex_progress);
@@ -388,15 +404,7 @@ static int scan_directory_multithreaded(void* pipeline_context) {
Chunk* current_chunk;
if (scanner == NULL) {
log_message(LOG_LEVEL_ERROR, "Failed to create parallel scanner");
atomic_store(&context->cancelled, true);
cnd_broadcast(&context->condition_not_full_scanner);
cnd_broadcast(&context->condition_not_empty_scanner);
cnd_broadcast(&context->condition_not_full_loader);
cnd_broadcast(&context->condition_not_empty_loader);
mtx_lock(&context->mutex_scanner);
context->scanner_done = true;
cnd_broadcast(&context->condition_not_empty_scanner);
mtx_unlock(&context->mutex_scanner);
pipeline_cancel(context);
return thrd_error;
}
while ((current_chunk = parallel_scanner_next(scanner)) != NULL) {
@@ -410,18 +418,14 @@ static int scan_directory_multithreaded(void* pipeline_context) {
if (!manifest_entry) {
log_message(LOG_LEVEL_ERROR, "Failed to allocate manifest entry");
mtx_unlock(&context->mutex_scanner);
atomic_store(&context->cancelled, true);
cnd_broadcast(&context->condition_not_full_scanner);
cnd_broadcast(&context->condition_not_empty_scanner);
pipeline_cancel(context);
parallel_scanner_destroy(scanner);
return thrd_error;
}
if (!array_list_add(context->manifest, manifest_entry)) {
free(manifest_entry);
atomic_store(&context->cancelled, true);
cnd_broadcast(&context->condition_not_full_scanner);
cnd_broadcast(&context->condition_not_empty_scanner);
mtx_unlock(&context->mutex_scanner);
pipeline_cancel(context);
chunk_destroy(current_chunk);
parallel_scanner_destroy(scanner);
return thrd_error;
@@ -434,13 +438,21 @@ static int scan_directory_multithreaded(void* pipeline_context) {
&context->condition_not_empty_scanner, &context->condition_not_full_scanner,
&context->cancelled)) {
chunk_destroy(current_chunk);
atomic_store(&context->cancelled, true);
cnd_broadcast(&context->condition_not_full_scanner);
cnd_broadcast(&context->condition_not_empty_scanner);
pipeline_cancel(context);
parallel_scanner_destroy(scanner);
return thrd_error;
}
}
if (parallel_scanner_failed(scanner)) {
parallel_scanner_destroy(scanner);
mtx_lock(&context->mutex_scanner);
context->scanner_done = true;
cnd_broadcast(&context->condition_not_empty_scanner);
cnd_broadcast(&context->condition_not_full_scanner);
mtx_unlock(&context->mutex_scanner);
pipeline_cancel(context);
return thrd_error;
}
mtx_lock(&context->mutex_scanner);
context->scanner_done = true;
cnd_signal(&context->condition_not_empty_scanner);
@@ -576,6 +588,15 @@ int send_files(Config* config) {
time_t last_progress = 0;
time_t start = time(NULL);
ArrayList* manifest = config->use_delete ? array_list_create(free) : NULL;
if (!scanner || (config->use_delete && !manifest)) {
if (scanner)
directory_scanner_destroy(scanner);
if (manifest)
array_list_delete(manifest);
client_disconnect(client);
client_delete(client);
return 1;
}
while ((current_chunk = directory_scanner_next(scanner)) != NULL) {
unsigned long long chunk_bytes = 0;
for (int i = 0; i < current_chunk->element_count; i++) {
@@ -620,6 +641,9 @@ int send_files(Config* config) {
if (send_chunk(client, current_chunk, config) != 0) {
log_message(LOG_LEVEL_ERROR, "Failed to send chunk");
chunk_destroy(current_chunk);
if (manifest)
array_list_delete(manifest);
manifest = NULL;
break;
}
if (config->show_progress) {
@@ -635,12 +659,15 @@ int send_files(Config* config) {
}
chunk_destroy(current_chunk);
}
if (directory_scanner_failed(scanner) || (config->use_delete && manifest == NULL))
goto send_fail;
if (config->use_delete) {
if (send_delete_manifest(client->file_descriptor, manifest) != 0) {
array_list_delete(manifest);
goto send_fail;
}
array_list_delete(manifest);
manifest = NULL;
}
if (!send_status(client->file_descriptor, STATUS_FINISHED))
goto send_fail;
@@ -662,6 +689,8 @@ int send_files(Config* config) {
return ok ? 0 : 1;
send_fail:
if (manifest)
array_list_delete(manifest);
directory_scanner_destroy(scanner);
client_disconnect(client);
client_delete(client);
@@ -715,14 +744,10 @@ int send_files_multithreaded(Config* config) {
if (!scanner_created || !loader_created || !sender_created) {
perror("Error creating threads.\n");
atomic_store(&context->cancelled, true);
context->scanner_done = true;
context->loader_done = true;
pipeline_cancel(context);
mtx_lock(&context->mutex_progress);
context->sender_done = true;
cnd_broadcast(&context->condition_not_full_scanner);
cnd_broadcast(&context->condition_not_empty_scanner);
cnd_broadcast(&context->condition_not_full_loader);
cnd_broadcast(&context->condition_not_empty_loader);
mtx_unlock(&context->mutex_progress);
if (sender_created)
thrd_join(sender, NULL);
if (loader_created)
+53 -7
View File
@@ -121,6 +121,7 @@ static int open_next_directory(DirectoryScanner* scanner) {
perror("Could not open directory");
free(scanner->current_path);
scanner->current_path = NULL;
scanner->failed = true;
return -1;
}
return 1;
@@ -128,6 +129,10 @@ static int open_next_directory(DirectoryScanner* scanner) {
Chunk* directory_scanner_next(DirectoryScanner* scanner) {
ArrayList* chunk_data = array_list_create(file_destroy);
if (!chunk_data) {
scanner->failed = true;
return NULL;
}
unsigned long long chunk_data_size = 0;
while (1) {
@@ -136,7 +141,7 @@ Chunk* directory_scanner_next(DirectoryScanner* scanner) {
if (ret == 0)
break;
if (ret < 0)
continue;
break;
}
struct dirent* entry = readdir(scanner->current_dir);
@@ -259,7 +264,11 @@ Chunk* directory_scanner_next(DirectoryScanner* scanner) {
file->data->size = stats.st_size;
if (scanner->use_metadata)
file->metadata = file_metadata_create(&stats);
array_list_add(chunk_data, file);
if (!array_list_add(chunk_data, file)) {
file_destroy(file);
scanner->failed = true;
break;
}
chunk_data_size += file->data->size;
if (chunk_data_size > scanner->chunk_size) {
free(cur_path);
@@ -275,6 +284,10 @@ Chunk* directory_scanner_next(DirectoryScanner* scanner) {
return NULL;
}
bool directory_scanner_failed(const DirectoryScanner* scanner) {
return scanner == NULL || scanner->failed;
}
typedef struct {
ParallelScanner* ps;
char** dirs;
@@ -302,10 +315,31 @@ static int parallel_worker_thread(void* arg) {
wa->dirs[i], wa->use_metadata, wa->chunk_size, wa->exclude_patterns, wa->exclude_count,
wa->include_patterns, wa->include_count, wa->max_size, wa->min_size, wa->max_depth,
wa->follow_symlinks, wa->copy_links, wa->safe_links, wa->copy_unsafe_links, wa->checksum);
if (!ds) {
mtx_lock(&wa->ps->result_mutex);
wa->ps->failed = true;
atomic_store(&wa->ps->cancelled, true);
cnd_broadcast(&wa->ps->result_not_empty);
cnd_broadcast(&wa->ps->result_not_full);
mtx_unlock(&wa->ps->result_mutex);
break;
}
Chunk* chunk;
while ((chunk = directory_scanner_next(ds)) != NULL) {
queue_enqueue_multithreaded(wa->ps->result_queue, chunk, &wa->ps->result_mutex,
&wa->ps->result_not_empty, &wa->ps->result_not_full);
if (!queue_enqueue_multithreaded_cancel(wa->ps->result_queue, chunk, &wa->ps->result_mutex,
&wa->ps->result_not_empty, &wa->ps->result_not_full,
&wa->ps->cancelled)) {
chunk_destroy(chunk);
break;
}
}
if (directory_scanner_failed(ds)) {
mtx_lock(&wa->ps->result_mutex);
wa->ps->failed = true;
atomic_store(&wa->ps->cancelled, true);
cnd_broadcast(&wa->ps->result_not_empty);
cnd_broadcast(&wa->ps->result_not_full);
mtx_unlock(&wa->ps->result_mutex);
}
directory_scanner_destroy(ds);
free(wa->dirs[i]);
@@ -338,6 +372,7 @@ ParallelScanner* parallel_scanner_create(char* root_directory, bool use_metadata
free(ps);
return NULL;
}
atomic_init(&ps->cancelled, false);
int init = 0;
bool ok = true;
if (mtx_init(&ps->result_mutex, mtx_plain) != thrd_success)
@@ -500,8 +535,10 @@ ParallelScanner* parallel_scanner_create(char* root_directory, bool use_metadata
if (!first) {
first = c;
} else {
queue_enqueue_multithreaded(ps->result_queue, c, &ps->result_mutex, &ps->result_not_empty,
&ps->result_not_full);
if (!queue_enqueue(ps->result_queue, c)) {
chunk_destroy(c);
ps->failed = true;
}
}
if (i < root_files->size - 1) {
batch = array_list_create(NULL);
@@ -585,7 +622,9 @@ Chunk* parallel_scanner_next(ParallelScanner* ps) {
return c;
}
if (ps->num_threads == 0) {
mtx_lock(&ps->result_mutex);
ps->done = true;
mtx_unlock(&ps->result_mutex);
return NULL;
}
Chunk* chunk = queue_dequeue_multithreaded(
@@ -593,12 +632,19 @@ Chunk* parallel_scanner_next(ParallelScanner* ps) {
return chunk;
}
bool parallel_scanner_failed(const ParallelScanner* ps) {
return ps == NULL || ps->failed;
}
void parallel_scanner_destroy(ParallelScanner* ps) {
if (!ps)
return;
mtx_lock(&ps->result_mutex);
ps->done = true;
cnd_signal(&ps->result_not_empty);
atomic_store(&ps->cancelled, true);
cnd_broadcast(&ps->result_not_empty);
cnd_broadcast(&ps->result_not_full);
mtx_unlock(&ps->result_mutex);
for (int i = 0; i < ps->num_threads; i++)
thrd_join(ps->threads[i], NULL);
free(ps->threads);
+6
View File
@@ -6,6 +6,7 @@
#include <dirent.h>
#include <stdbool.h>
#include <threads.h>
#include <stdatomic.h>
typedef struct {
Queue* directories;
@@ -26,6 +27,7 @@ typedef struct {
bool safe_links;
bool copy_unsafe_links;
bool checksum;
bool failed;
} DirectoryScanner;
typedef struct {
@@ -36,6 +38,8 @@ typedef struct {
int num_threads;
thrd_t* threads;
bool done;
bool failed;
atomic_bool cancelled;
int completed;
Chunk* initial_chunk;
} ParallelScanner;
@@ -48,6 +52,7 @@ DirectoryScanner* directory_scanner_create(const char* root_directory, bool use_
bool follow_symlinks, bool copy_links, bool safe_links,
bool copy_unsafe_links, bool checksum);
Chunk* directory_scanner_next(DirectoryScanner* scanner);
bool directory_scanner_failed(const DirectoryScanner* scanner);
void directory_scanner_destroy(DirectoryScanner* scanner);
ParallelScanner* parallel_scanner_create(char* root_directory, bool use_metadata,
@@ -58,6 +63,7 @@ ParallelScanner* parallel_scanner_create(char* root_directory, bool use_metadata
int num_threads, bool follow_symlinks, bool copy_links,
bool safe_links, bool copy_unsafe_links, bool checksum);
Chunk* parallel_scanner_next(ParallelScanner* scanner);
bool parallel_scanner_failed(const ParallelScanner* scanner);
void parallel_scanner_destroy(ParallelScanner* scanner);
#endif