Fixed a crash related to file transfers

This commit is contained in:
WolverinDEV
2021-01-03 17:16:23 +01:00
parent 6a502e23f2
commit 960186d55e
7 changed files with 112 additions and 72 deletions
+57 -35
View File
@@ -71,8 +71,9 @@ void LocalFileTransfer::dispatch_loop_disk_io(void *provider_ptr) {
}
provider->disk_io.queue_head = provider->disk_io.queue_head->file.next_client;
if(!provider->disk_io.queue_head)
if(!provider->disk_io.queue_head) {
provider->disk_io.queue_tail = &provider->disk_io.queue_head;
}
}
if(provider->disk_io.state != DiskIOLoopState::RUNNING) {
@@ -83,8 +84,9 @@ void LocalFileTransfer::dispatch_loop_disk_io(void *provider_ptr) {
} else {
/* force stopping without any flushing */
auto fclient = &*client;
while(fclient)
while(fclient) {
fclient = std::exchange(fclient->file.next_client, nullptr);
}
provider->disk_io.queue_head = nullptr;
provider->disk_io.queue_tail = &provider->disk_io.queue_head;
@@ -92,8 +94,9 @@ void LocalFileTransfer::dispatch_loop_disk_io(void *provider_ptr) {
}
}
if(!client)
if(!client) {
continue;
}
client->file.currently_processing = true;
client->file.next_client = nullptr;
@@ -124,7 +127,7 @@ bool FileClient::enqueue_disk_buffer_bytes(const void *snd_buffer, size_t size)
return buffer_size > TRANSFER_MAX_CACHED_BYTES;
write_disconnected:
free_buffer(tbuffer);
deref_buffer(tbuffer);
return false;
}
@@ -141,7 +144,7 @@ void FileClient::flush_disk_buffer() {
while(current_head) {
auto next = current_head->next;
free_buffer(current_head);
deref_buffer(current_head);
current_head = next;
}
}
@@ -410,7 +413,9 @@ void LocalFileTransfer::enqueue_disk_io(const std::shared_ptr<FileClient> &clien
}
void LocalFileTransfer::execute_disk_io(const std::shared_ptr<FileClient> &client) {
if(!client->transfer) return;
if(!client->transfer) {
return;
}
if(client->transfer->direction == Transfer::DIRECTION_UPLOAD) {
Buffer* buffer;
@@ -419,17 +424,28 @@ void LocalFileTransfer::execute_disk_io(const std::shared_ptr<FileClient> &clien
while(true) {
{
std::lock_guard block{client->disk_buffer.mutex};
buffer = client->disk_buffer.buffer_head;
if(!client->disk_buffer.buffer_head) {
assert(client->disk_buffer.bytes == 0);
buffer_left_size = 0;
break;
}
buffer_left_size = client->disk_buffer.bytes;
}
if(!buffer) {
assert(buffer_left_size == 0);
break;
buffer = ref_buffer(client->disk_buffer.buffer_head);
}
assert(buffer->offset < buffer->length);
auto written = ::write(client->file.file_descriptor, buffer->data + buffer->offset, buffer->length - buffer->offset);
if(written <= 0) {
deref_buffer(buffer);
if(errno == EAGAIN) {
//TODO: Timeout?
this->enqueue_disk_io(client);
break;
}
if(written == 0) {
/* EOF, how the hell is this event possible?! */
auto offset_written = client->statistics.disk_bytes_write.total_bytes + client->transfer->file_offset;
@@ -446,12 +462,6 @@ void LocalFileTransfer::execute_disk_io(const std::shared_ptr<FileClient> &clien
client->handle->disconnect_client(client, slock, true);
}
} else {
if(errno == EAGAIN) {
//TODO: Timeout?
this->enqueue_disk_io(client);
break;
}
auto offset_written = client->statistics.disk_bytes_write.total_bytes + client->transfer->file_offset;
auto aoffset = lseek(client->file.file_descriptor, 0, SEEK_CUR);
logError(LOG_FT, "{} Received write to disk IO error. Write pointer is at {} of {}. Actual file offset: {}. Closing transfer.",
@@ -469,28 +479,38 @@ void LocalFileTransfer::execute_disk_io(const std::shared_ptr<FileClient> &clien
} else {
buffer->offset += written;
assert(buffer->offset <= buffer->length);
if(buffer->length == buffer->offset) {
{
std::lock_guard block{client->disk_buffer.mutex};
client->disk_buffer.buffer_head = buffer->next;
if(!buffer->next)
client->disk_buffer.buffer_tail = &client->disk_buffer.buffer_head;
if(client->disk_buffer.buffer_head == buffer) {
client->disk_buffer.buffer_head = buffer->next;
if(!buffer->next) {
client->disk_buffer.buffer_tail = &client->disk_buffer.buffer_head;
}
assert(client->disk_buffer.bytes >= written);
client->disk_buffer.bytes -= written;
buffer_left_size = client->disk_buffer.bytes;
} else {
/* The buffer got removed */
}
}
/* We have to deref the buffer twice since we've removed it from the list which owns us one reference */
deref_buffer(buffer);
} else {
std::lock_guard block{client->disk_buffer.mutex};
if(client->disk_buffer.buffer_head == buffer) {
assert(client->disk_buffer.bytes >= written);
client->disk_buffer.bytes -= written;
buffer_left_size = client->disk_buffer.bytes;
(void) buffer_left_size; /* trick my IDE here a bit */
} else {
/* The buffer got removed */
}
free_buffer(buffer);
} else {
std::lock_guard block{client->disk_buffer.mutex};
assert(client->disk_buffer.bytes >= written);
client->disk_buffer.bytes -= written;
buffer_left_size = client->disk_buffer.bytes;
(void) buffer_left_size; /* trick my IDE here a bit */
}
deref_buffer(buffer);
client->statistics.disk_bytes_write.increase_bytes(written);
}
}
@@ -502,8 +522,10 @@ void LocalFileTransfer::execute_disk_io(const std::shared_ptr<FileClient> &clien
}
if(client->state == FileClient::STATE_TRANSFERRING && buffer_left_size < TRANSFER_MAX_CACHED_BYTES / 2) {
if(client->disk_buffer.buffering_stopped)
if(client->disk_buffer.buffering_stopped) {
logMessage(LOG_FT, "{} Starting network read, buffer is capable for reading again.", client->log_prefix());
}
client->add_network_read_event(false);
}
} else if(client->transfer->direction == Transfer::DIRECTION_DOWNLOAD) {
@@ -518,6 +540,11 @@ void LocalFileTransfer::execute_disk_io(const std::shared_ptr<FileClient> &clien
auto read = ::read(client->file.file_descriptor, buffer, buffer_capacity);
if(read <= 0) {
if(errno == EAGAIN) {
this->enqueue_disk_io(client);
return;
}
if(read == 0) {
/* EOF */
auto offset_send = client->statistics.disk_bytes_read.total_bytes + client->transfer->file_offset;
@@ -532,11 +559,6 @@ void LocalFileTransfer::execute_disk_io(const std::shared_ptr<FileClient> &clien
this->invoke_aborted_callback(client, { TransferError::UNEXPECTED_DISK_EOF, "" });
}
} else {
if(errno == EAGAIN) {
this->enqueue_disk_io(client);
return;
}
logWarning(LOG_FT, "{} Failed to read from file {} ({}/{}). Aborting transfer.", client->log_prefix(), client->transfer->absolute_file_path, errno, strerror(errno));
this->invoke_aborted_callback(client, { TransferError::DISK_IO_ERROR, strerror(errno) });