Fixed a crash related to file transfers
This commit is contained in:
@@ -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) });
|
||||
|
||||
Reference in New Issue
Block a user