Deletion timing is now real and selected by the four rsync flags plus the plain --delete default. Wire protocol bumps to 2.8.0: two new config booleans (delete_during, delete_delay) are serialized and validated, joining the existing delete_before/delete_after. - Early modes (--delete-before, --delete-during/--del): the sender pre-scans the whole tree (paths only), transmits the keep-set manifest BEFORE any file data, and the receiver removes extras and acks STATUS_OK; the sender only streams data after the deletion committed. Deletion is thus performed even if a later transfer phase fails (rsync delete-before/during are destructive by definition). FastSync streams in a single scan so it cannot interleave per-directory like rsync delete-during; --delete-during selects the same engine mode as --delete-before (documented divergence). - Late/commit modes (plain --delete, --delete-after, --delete-delay): the manifest closes the data stream and deletion is committed only after STATUS_FINISHED proves the whole transfer succeeded, preserving FastSync's commit-style safety. --delete-delay converges with --delete-after because FastSync never snapshots the destination during data flow (documented). - The STATUS_MANIFEST frame is now self-delimiting and position-independent. Single-threaded receivers delete before the success frame; the -m receiver hands the keep-set to server.c, which commits the deletion only after the disk writer thread has drained (fixes a delete-vs-in-flight-temp race). - Every timing flag implies --delete; at most one timing flag is allowed. - Each timing flag implies --delete, matching rsync; conflicts are rejected.
94 lines
3.9 KiB
C
94 lines
3.9 KiB
C
#ifndef MULTIPROCESSING_H
|
|
#define MULTIPROCESSING_H
|
|
|
|
#include <threads.h>
|
|
#include <stdatomic.h>
|
|
|
|
#include "array_list.h"
|
|
#include "config.h"
|
|
#include "file.h"
|
|
#include "protocol.h"
|
|
#include "queue.h"
|
|
#include "receiver.h"
|
|
#include <openssl/ssl.h>
|
|
|
|
typedef struct {
|
|
Config* config;
|
|
Queue* queue_scanner;
|
|
mtx_t mutex_scanner;
|
|
cnd_t condition_not_full_scanner;
|
|
cnd_t condition_not_empty_scanner;
|
|
bool scanner_done;
|
|
Queue* queue_loader;
|
|
mtx_t mutex_loader;
|
|
cnd_t condition_not_full_loader;
|
|
cnd_t condition_not_empty_loader;
|
|
bool loader_done;
|
|
ArrayList* manifest;
|
|
ArrayList* remove_source_files;
|
|
/* True when --delete-before/--delete-during require the keep-set manifest to
|
|
be transmitted before any file data: context->manifest is then prebuilt by
|
|
a path-only pre-scan on the calling thread and the pipeline scanner must
|
|
not append to it. Set once before the worker threads start. */
|
|
bool early_delete;
|
|
mtx_t mutex_progress;
|
|
int total_files;
|
|
unsigned long long progress_bytes;
|
|
unsigned long long total_bytes;
|
|
bool sender_done;
|
|
atomic_bool cancelled;
|
|
ProtocolSession allocation_session;
|
|
} PipelineContextSender;
|
|
|
|
typedef struct PipelineContextReceiver {
|
|
Queue* queue;
|
|
Config* config;
|
|
int file_descriptor;
|
|
SSL* ssl;
|
|
ProtocolSession session;
|
|
ReceiverOutcomes outcomes;
|
|
mtx_t mutex;
|
|
cnd_t condition_not_full;
|
|
cnd_t condition_not_empty;
|
|
bool receiver_done;
|
|
atomic_bool cancelled;
|
|
/* Aggregate payload bytes that have been received but not yet released by
|
|
the disk writer (queued or in the writer's hand). Guarded by `mutex`.
|
|
When `max_queue_bytes` is non-zero the receiver blocks before enqueuing
|
|
once this total would exceed it, so decompressed/copied file payloads
|
|
buffered ahead of a slow disk writer respect the per-connection memory
|
|
budget instead of growing without bound. */
|
|
size_t queued_bytes;
|
|
size_t max_queue_bytes;
|
|
/* Keep-set manifest for the commit-style (late) deletion
|
|
(--delete/--delete-after/--delete-delay). receive_thread parses the whole
|
|
protocol stream but hands the manifest here instead of deleting while the
|
|
disk writer may still be draining; the caller (server.c) commits the
|
|
deletion after both threads have joined, so no extra is removed unless the
|
|
transfer truly succeeded. NULL in the early delete modes (which delete at
|
|
the manifest). */
|
|
ArrayList* deferred_manifest;
|
|
} PipelineContextReceiver;
|
|
|
|
PipelineContextSender* pipeline_context_sender_create(Config* config, Queue* queue_scanner,
|
|
Queue* queue_loader);
|
|
void pipeline_context_sender_destroy(PipelineContextSender* context);
|
|
PipelineContextReceiver* pipeline_context_receiver_create(Config* config, Queue* queue_receiver,
|
|
int file_descriptor, SSL* ssl);
|
|
void pipeline_context_receiver_destroy(PipelineContextReceiver* context);
|
|
/* Bound the bytes buffered ahead of the disk writer (see max_queue_bytes). */
|
|
void pipeline_context_receiver_set_queue_byte_limit(PipelineContextReceiver* context,
|
|
size_t max_bytes);
|
|
/* Blocking enqueue used by the receive pipeline sink. Blocks while the queue
|
|
is full by element count or when adding `file` would push queued_bytes over
|
|
the configured byte limit; waits until the disk writer releases bytes.
|
|
Takes ownership of `file` on success and destroys it on failure/cancel. */
|
|
bool pipeline_context_receiver_enqueue_file(PipelineContextReceiver* context, File* file);
|
|
/* Account for `released_bytes` of payload memory that has been freed by the
|
|
disk writer, unblocking a receiver that is waiting on the byte limit. */
|
|
void pipeline_context_receiver_note_bytes_released(PipelineContextReceiver* context,
|
|
size_t released_bytes);
|
|
int receive_thread(void* pipeline_context);
|
|
int write_thread(void* pipeline_context);
|
|
#endif
|