Report an error when disk is full

Previously packaging completes successfully without any error
or warning.

With the fix, an error will be reported if write fails. It may
appear as "Cannot close file error" as we use threaded IO, which
could delay the error reporting until Close() call, so the user
of the File API needs to make sure Close() returns successfully.

Also fixed a deadlock in threaded_io_file if internal_file->Write
fails.

Fixes #160

Change-Id: I17f945150fb4021d2dcdbe784e557673f53ca583
This commit is contained in:
KongQun Yang 2017-09-07 16:05:20 -07:00
parent 7627871428
commit 9c861d03f7
5 changed files with 52 additions and 9 deletions

View File

@ -250,8 +250,10 @@ bool File::WriteStringToFile(const char* file_name,
<< contents.size() << " bytes."; << contents.size() << " bytes.";
return false; return false;
} }
if (!file->Flush()) { if (!file.release()->Close()) {
LOG(ERROR) << "Failed to flush the file '" << file_name << "'."; LOG(ERROR)
<< "Failed to close file '" << file_name
<< "', possibly file permission issue or running out of disk space.";
return false; return false;
} }
return true; return true;
@ -302,6 +304,12 @@ bool File::Copy(const char* from_file_name, const char* to_file_name) {
total_bytes_written += bytes_written; total_bytes_written += bytes_written;
} }
if (!output_file.release()->Close()) {
LOG(ERROR)
<< "Failed to close file '" << to_file_name
<< "', possibly file permission issue or running out of disk space.";
return false;
}
return true; return true;
} }

View File

@ -37,13 +37,25 @@ bool LocalFile::Close() {
int64_t LocalFile::Read(void* buffer, uint64_t length) { int64_t LocalFile::Read(void* buffer, uint64_t length) {
DCHECK(buffer != NULL); DCHECK(buffer != NULL);
DCHECK(internal_file_ != NULL); DCHECK(internal_file_ != NULL);
return fread(buffer, sizeof(char), length, internal_file_); size_t bytes_read = fread(buffer, sizeof(char), length, internal_file_);
VLOG(2) << "Read " << length << " return " << bytes_read << " error "
<< ferror(internal_file_);
if (bytes_read == 0 && ferror(internal_file_) != 0) {
return -1;
}
return bytes_read;
} }
int64_t LocalFile::Write(const void* buffer, uint64_t length) { int64_t LocalFile::Write(const void* buffer, uint64_t length) {
DCHECK(buffer != NULL); DCHECK(buffer != NULL);
DCHECK(internal_file_ != NULL); DCHECK(internal_file_ != NULL);
return fwrite(buffer, sizeof(char), length, internal_file_); size_t bytes_written = fwrite(buffer, sizeof(char), length, internal_file_);
VLOG(2) << "Write " << length << " return " << bytes_written << " error "
<< ferror(internal_file_);
if (bytes_written == 0 && ferror(internal_file_) != 0) {
return -1;
}
return bytes_written;
} }
int64_t LocalFile::Size() { int64_t LocalFile::Size() {

View File

@ -58,13 +58,14 @@ bool ThreadedIoFile::Open() {
bool ThreadedIoFile::Close() { bool ThreadedIoFile::Close() {
DCHECK(internal_file_); DCHECK(internal_file_);
bool result = true;
if (mode_ == kOutputMode) if (mode_ == kOutputMode)
Flush(); result = Flush();
cache_.Close(); cache_.Close();
task_exit_event_.Wait(); task_exit_event_.Wait();
bool result = internal_file_.release()->Close(); result &= internal_file_.release()->Close();
delete this; delete this;
return result; return result;
} }
@ -110,6 +111,9 @@ bool ThreadedIoFile::Flush() {
DCHECK(internal_file_); DCHECK(internal_file_);
DCHECK_EQ(kOutputMode, mode_); DCHECK_EQ(kOutputMode, mode_);
if (NoBarrier_Load(&internal_file_error_))
return false;
flushing_ = true; flushing_ = true;
cache_.Close(); cache_.Close();
flush_complete_event_.Wait(); flush_complete_event_.Wait();
@ -204,6 +208,10 @@ void ThreadedIoFile::RunInOutputMode() {
if (write_result < 0) { if (write_result < 0) {
NoBarrier_Store(&internal_file_error_, write_result); NoBarrier_Store(&internal_file_error_, write_result);
cache_.Close(); cache_.Close();
if (flushing_) {
flushing_ = false;
flush_complete_event_.Signal();
}
return; return;
} }
bytes_written += write_result; bytes_written += write_result;

View File

@ -90,8 +90,10 @@ Status SingleSegmentSegmenter::DoFinalize() {
// Close the temp file to prepare for reading later. // Close the temp file to prepare for reading later.
if (!temp_file_.release()->Close()) { if (!temp_file_.release()->Close()) {
return Status(error::FILE_FAILURE, return Status(
"Cannot close the temp file " + temp_file_name_); error::FILE_FAILURE,
"Cannot close the temp file " + temp_file_name_ +
", possibly file permission issue or running out of disk space.");
} }
std::unique_ptr<File, FileCloser> file( std::unique_ptr<File, FileCloser> file(
@ -142,6 +144,16 @@ Status SingleSegmentSegmenter::DoFinalize() {
UpdateProgress(static_cast<double>(size) / temp_file->Size() * UpdateProgress(static_cast<double>(size) / temp_file->Size() *
re_segment_progress_target); re_segment_progress_target);
} }
if (!temp_file.release()->Close()) {
return Status(error::FILE_FAILURE, "Cannot close the temp file " +
temp_file_name_ + " after reading.");
}
if (!file.release()->Close()) {
return Status(
error::FILE_FAILURE,
"Cannot close file " + options().output_file_name +
", possibly file permission issue or running out of disk space.");
}
SetComplete(); SetComplete();
return Status::OK; return Status::OK;
} }

View File

@ -29,7 +29,10 @@ Status MkvWriter::Open(const std::string& name) {
Status MkvWriter::Close() { Status MkvWriter::Close() {
const std::string file_name = file_->file_name(); const std::string file_name = file_->file_name();
if (!file_.release()->Close()) { if (!file_.release()->Close()) {
return Status(error::FILE_FAILURE, "Cannot close file " + file_name); return Status(
error::FILE_FAILURE,
"Cannot close file " + file_name +
", possibly file permission issue or running out of disk space.");
} }
return Status::OK; return Status::OK;
} }