Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(801)

Unified Diff: media/cdm/ppapi/cdm_file_io_impl.cc

Issue 545983005: CdmFileIOImpl: Refactor Write() implementation. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Cannot use "ERROR" on Windows!!! Also polished tests. Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: media/cdm/ppapi/cdm_file_io_impl.cc
diff --git a/media/cdm/ppapi/cdm_file_io_impl.cc b/media/cdm/ppapi/cdm_file_io_impl.cc
index 3409f16fd581f9258f23f0096aa1c0ea97ec9765..989c05e84d7b5d6b74965940c442ad201b2548bc 100644
--- a/media/cdm/ppapi/cdm_file_io_impl.cc
+++ b/media/cdm/ppapi/cdm_file_io_impl.cc
@@ -13,7 +13,10 @@
namespace media {
-const int kReadSize = 4 * 1024; // Arbitrary choice.
+// Arbitrary choice based on the following heuristic ideas:
+// - not too big to avoid unnecessarily large memory allocation;
+// - not too small to avoid breaking most reads into multiple read operations.
+const int kReadSize = 8 * 1024;
// Call func_call and check the result. If the result is not
// PP_OK_COMPLETIONPENDING, print out logs, call OnError() and return.
@@ -23,6 +26,7 @@ const int kReadSize = 4 * 1024; // Arbitrary choice.
PP_DCHECK(result != PP_OK); \
if (result != PP_OK_COMPLETIONPENDING) { \
CDM_DLOG() << #func_call << " failed with result: " << result; \
+ state_ = STATE_ERROR; \
OnError(error_type); \
return; \
} \
@@ -59,37 +63,49 @@ CdmFileIOImpl::CdmFileIOImpl(
cdm::FileIOClient* client,
PP_Instance pp_instance,
const pp::CompletionCallback& first_file_read_cb)
- : state_(FILE_UNOPENED),
+ : state_(STATE_UNOPENED),
client_(client),
pp_instance_handle_(pp_instance),
- callback_factory_(this),
io_offset_(0),
first_file_read_reported_(false),
- first_file_read_cb_(first_file_read_cb) {
+ first_file_read_cb_(first_file_read_cb),
+ callback_factory_(this) {
PP_DCHECK(IsMainThread());
PP_DCHECK(pp_instance); // 0 indicates a "NULL handle".
}
CdmFileIOImpl::~CdmFileIOImpl() {
- PP_DCHECK(state_ == FILE_CLOSED);
+ // The dtor is private. |this| can only be destructed through Close().
ddorwin 2014/09/16 18:07:36 "destructor" should fit
xhwang 2014/09/17 21:02:31 Done.
+ PP_DCHECK(state_ == STATE_CLOSED);
}
-// Call sequence: Open() -> OpenFileSystem() -> OpenFile() -> FILE_OPENED.
+// Call sequence: Open() -> OpenFileSystem() -> FILE_OPENED.
ddorwin 2014/09/16 18:07:36 FILE_OPENED does not exist anymore and is no longe
xhwang 2014/09/17 21:02:31 Done.
+// Note: This only stores file name and opens the file system. The real file
+// open is deferred to when Read() or Write() is called.
void CdmFileIOImpl::Open(const char* file_name, uint32_t file_name_size) {
CDM_DLOG() << __FUNCTION__;
PP_DCHECK(IsMainThread());
- if (state_ != FILE_UNOPENED) {
+ if (state_ != STATE_UNOPENED) {
CDM_DLOG() << "Open() called in an invalid state.";
OnError(OPEN_ERROR);
return;
}
- // File name should not contain any path separators.
+ // File name should not be empty and should not start with '_'.
std::string file_name_str(file_name, file_name_size);
+ if (file_name_str.empty() || file_name_str[0] == '_') {
+ CDM_DLOG() << "Invalid file name.";
+ state_ = STATE_ERROR;
+ OnError(OPEN_ERROR);
+ return;
+ }
+
+ // File name should not contain any path separators.
if (file_name_str.find('/') != std::string::npos ||
ddorwin 2014/09/16 18:07:37 combine into one block with above?
xhwang 2014/09/17 21:02:31 Done.
file_name_str.find('\\') != std::string::npos) {
CDM_DLOG() << "Invalid file name.";
+ state_ = STATE_ERROR;
OnError(OPEN_ERROR);
return;
}
@@ -103,59 +119,52 @@ void CdmFileIOImpl::Open(const char* file_name, uint32_t file_name_size) {
return;
}
- state_ = OPENING_FILE_SYSTEM;
+ state_ = STATE_OPENING_FILE_SYSTEM;
OpenFileSystem();
}
// Call sequence:
-// finished
-// Read() -> ReadFile() -> OnFileRead() ----------> Done.
-// ^ |
-// | not finished |
-// |--------------|
+// Read() -> OpenFileForRead() -> ReadFile() -> Done.
void CdmFileIOImpl::Read() {
CDM_DLOG() << __FUNCTION__;
PP_DCHECK(IsMainThread());
- if (state_ == READING_FILE || state_ == WRITING_FILE) {
+ if (state_ == STATE_READING || state_ == STATE_WRITING) {
CDM_DLOG() << "Read() called during pending read/write.";
OnError(READ_WHILE_IN_USE);
return;
}
- if (state_ != FILE_OPENED) {
+ if (state_ != STATE_OPENED) {
CDM_DLOG() << "Read() called in an invalid state.";
OnError(READ_ERROR);
return;
}
+ PP_DCHECK(io_offset_ == 0);
PP_DCHECK(io_buffer_.empty());
PP_DCHECK(cumulative_read_buffer_.empty());
-
io_buffer_.resize(kReadSize);
io_offset_ = 0;
- state_ = READING_FILE;
- ReadFile();
+ state_ = STATE_READING;
+ OpenFileForRead();
}
// Call sequence:
-// finished
-// Write() -> WriteFile() -> OnFileWritten() ----------> Done.
-// ^ |
-// | | not finished
-// |------------------|
+// Write() -> OpenTempFileForWrite() -> WriteTempFile() -> RenameTempFile().
+// The file name of the temparory file is /_<original_file_name>.
void CdmFileIOImpl::Write(const uint8_t* data, uint32_t data_size) {
CDM_DLOG() << __FUNCTION__;
PP_DCHECK(IsMainThread());
- if (state_ == READING_FILE || state_ == WRITING_FILE) {
+ if (state_ == STATE_READING || state_ == STATE_WRITING) {
CDM_DLOG() << "Write() called during pending read/write.";
OnError(WRITE_WHILE_IN_USE);
return;
}
- if (state_ != FILE_OPENED) {
+ if (state_ != STATE_OPENED) {
CDM_DLOG() << "Write() called in an invalid state.";
OnError(WRITE_ERROR);
return;
@@ -168,17 +177,16 @@ void CdmFileIOImpl::Write(const uint8_t* data, uint32_t data_size) {
else
PP_DCHECK(!data);
- state_ = WRITING_FILE;
-
- // Always SetLength() in case |data_size| is less than the file size.
- SetLength(data_size);
+ state_ = STATE_WRITING;
+ OpenTempFileForWrite();
}
void CdmFileIOImpl::Close() {
CDM_DLOG() << __FUNCTION__;
PP_DCHECK(IsMainThread());
- PP_DCHECK(state_ != FILE_CLOSED);
- CloseFile();
+ PP_DCHECK(state_ != STATE_CLOSED);
+ Reset();
+ state_ = STATE_CLOSED;
ReleaseFileLock();
// All pending callbacks are canceled since |callback_factory_| is destroyed.
delete this;
@@ -237,7 +245,7 @@ void CdmFileIOImpl::ReleaseFileLock() {
}
void CdmFileIOImpl::OpenFileSystem() {
- PP_DCHECK(state_ == OPENING_FILE_SYSTEM);
+ PP_DCHECK(state_ == STATE_OPENING_FILE_SYSTEM);
pp::CompletionCallbackWithOutput<pp::FileSystem> cb =
callback_factory_.NewCallbackWithOutput(
@@ -251,51 +259,62 @@ void CdmFileIOImpl::OpenFileSystem() {
void CdmFileIOImpl::OnFileSystemOpened(int32_t result,
pp::FileSystem file_system) {
PP_DCHECK(IsMainThread());
- PP_DCHECK(state_ == OPENING_FILE_SYSTEM);
+ PP_DCHECK(state_ == STATE_OPENING_FILE_SYSTEM);
if (result != PP_OK) {
CDM_DLOG() << "File system open failed asynchronously.";
ReleaseFileLock();
+ state_ = STATE_ERROR;
OnError(OPEN_ERROR);
return;
}
file_system_ = file_system;
- state_ = OPENING_FILE;
- OpenFile();
+
+ state_ = STATE_OPENED;
ddorwin 2014/09/16 18:07:37 So it is STATE_FILE_SYSTEM_OPENED
xhwang 2014/09/17 21:02:32 Done.
+ client_->OnOpenComplete(cdm::FileIOClient::kSuccess);
}
-void CdmFileIOImpl::OpenFile() {
- PP_DCHECK(state_ == OPENING_FILE);
+void CdmFileIOImpl::OpenFileForRead() {
+ PP_DCHECK(state_ == STATE_READING);
+ PP_DCHECK(file_io_.is_null());
+ PP_DCHECK(file_ref_.is_null());
file_io_ = pp::FileIO(pp_instance_handle_);
file_ref_ = pp::FileRef(file_system_, file_name_.c_str());
- int32_t file_open_flag = PP_FILEOPENFLAG_READ |
- PP_FILEOPENFLAG_WRITE |
- PP_FILEOPENFLAG_CREATE;
+
+ // Open file for read. If file doesn't exist, create a new empty file.
ddorwin 2014/09/16 18:07:36 We should update the CDM.h API to allow the CDM to
xhwang 2014/09/17 21:02:31 Changed to impl not to create a new file for read
+ int32_t file_open_flag = PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_CREATE;
+
pp::CompletionCallback cb =
- callback_factory_.NewCallback(&CdmFileIOImpl::OnFileOpened);
+ callback_factory_.NewCallback(&CdmFileIOImpl::OnFileOpenedForRead);
CHECK_PP_OK_COMPLETIONPENDING(file_io_.Open(file_ref_, file_open_flag, cb),
- OPEN_ERROR);
+ READ_ERROR);
}
-void CdmFileIOImpl::OnFileOpened(int32_t result) {
+void CdmFileIOImpl::OnFileOpenedForRead(int32_t result) {
PP_DCHECK(IsMainThread());
- PP_DCHECK(state_ == OPENING_FILE);
+ PP_DCHECK(state_ == STATE_READING);
if (result != PP_OK) {
CDM_DLOG() << "File open failed.";
ReleaseFileLock();
+ state_ = STATE_ERROR;
OnError(OPEN_ERROR);
return;
}
- state_ = FILE_OPENED;
- client_->OnOpenComplete(cdm::FileIOClient::kSuccess);
+ ReadFile();
}
+// Call sequence:
+// finished
ddorwin 2014/09/16 18:07:37 What is the purpose of the finished/not finished p
xhwang 2014/09/17 21:02:31 Changed "not finished" to "partially read"... Is t
ddorwin 2014/09/17 23:16:38 Acknowledged.
+// ReadFile() -> OnFileRead() ----------> Done.
+// ^ |
+// | not finished |
+// |--------------|
void CdmFileIOImpl::ReadFile() {
- PP_DCHECK(state_ == READING_FILE);
+ PP_DCHECK(state_ == STATE_READING);
PP_DCHECK(!io_buffer_.empty());
pp::CompletionCallback cb =
@@ -308,11 +327,12 @@ void CdmFileIOImpl::ReadFile() {
void CdmFileIOImpl::OnFileRead(int32_t bytes_read) {
CDM_DLOG() << __FUNCTION__ << ": " << bytes_read;
PP_DCHECK(IsMainThread());
- PP_DCHECK(state_ == READING_FILE);
+ PP_DCHECK(state_ == STATE_READING);
// 0 |bytes_read| indicates end-of-file reached.
if (bytes_read < PP_OK) {
CDM_DLOG() << "Read file failed.";
+ state_ = STATE_ERROR;
OnError(READ_ERROR);
return;
}
@@ -324,21 +344,19 @@ void CdmFileIOImpl::OnFileRead(int32_t bytes_read) {
io_buffer_.begin() + bytes_read);
io_offset_ += bytes_read;
- // Not received end-of-file yet.
+ // Not received end-of-file yet. Keep reading.
if (bytes_read > 0) {
ReadFile();
return;
}
// We hit end-of-file. Return read data to the client.
- io_buffer_.clear();
- io_offset_ = 0;
+
// Clear |cumulative_read_buffer_| in case OnReadComplete() calls Read() or
// Write().
std::vector<char> local_buffer;
std::swap(cumulative_read_buffer_, local_buffer);
- state_ = FILE_OPENED;
const uint8_t* data = local_buffer.empty() ?
NULL : reinterpret_cast<const uint8_t*>(&local_buffer[0]);
@@ -349,44 +367,71 @@ void CdmFileIOImpl::OnFileRead(int32_t bytes_read) {
first_file_read_reported_ = true;
}
+ Reset();
+
+ state_ = STATE_OPENED;
client_->OnReadComplete(
cdm::FileIOClient::kSuccess, data, local_buffer.size());
}
-void CdmFileIOImpl::SetLength(uint32_t length) {
- PP_DCHECK(state_ == WRITING_FILE);
+void CdmFileIOImpl::OpenTempFileForWrite() {
+ PP_DCHECK(state_ == STATE_WRITING);
+
+ PP_DCHECK(file_name_.size() > 1 && file_name_[0] == '/');
+ // Temporary file name format: /_<original_file_name>
+ std::string temp_file_name = "/_" + file_name_.substr(1);
+
+ PP_DCHECK(file_io_.is_null());
+ PP_DCHECK(file_ref_.is_null());
+ file_io_ = pp::FileIO(pp_instance_handle_);
+ file_ref_ = pp::FileRef(file_system_, temp_file_name.c_str());
+
+ // Create the file if it doesn't exist. Truncate the file to length 0 if it
ddorwin 2014/09/16 18:07:37 Should we UMA the case where we find a temp file?
xhwang 2014/09/17 21:02:31 Good point. But since we are using TRUNCATE, we ha
+ // exists.
+ int32_t file_open_flag = PP_FILEOPENFLAG_WRITE |
+ PP_FILEOPENFLAG_TRUNCATE |
+ PP_FILEOPENFLAG_CREATE;
pp::CompletionCallback cb =
- callback_factory_.NewCallback(&CdmFileIOImpl::OnLengthSet);
- CHECK_PP_OK_COMPLETIONPENDING(file_io_.SetLength(length, cb), WRITE_ERROR);
+ callback_factory_.NewCallback(&CdmFileIOImpl::OnTempFileOpenedForWrite);
+ CHECK_PP_OK_COMPLETIONPENDING(
+ file_io_.Open(file_ref_, file_open_flag, cb), WRITE_ERROR);
}
-void CdmFileIOImpl::OnLengthSet(int32_t result) {
+void CdmFileIOImpl::OnTempFileOpenedForWrite(int32_t result) {
CDM_DLOG() << __FUNCTION__ << ": " << result;
PP_DCHECK(IsMainThread());
- PP_DCHECK(state_ == WRITING_FILE);
+ PP_DCHECK(state_ == STATE_WRITING);
if (result != PP_OK) {
- CDM_DLOG() << "File SetLength failed.";
+ CDM_DLOG() << "Open temporary file failed.";
+ state_ = STATE_ERROR;
OnError(WRITE_ERROR);
return;
}
if (io_buffer_.empty()) {
ddorwin 2014/09/16 18:07:36 Does this indicate we were told to write 0 bytes?
xhwang 2014/09/17 21:02:31 Done.
- state_ = FILE_OPENED;
- client_->OnWriteComplete(cdm::FileIOClient::kSuccess);
+ RenameTempFile();
return;
}
- WriteFile();
+ PP_DCHECK(io_offset_ == 0);
+ io_offset_ = 0;
+ WriteTempFile();
}
-void CdmFileIOImpl::WriteFile() {
- PP_DCHECK(state_ == WRITING_FILE);
+// Call sequence:
+// finished
+// WriteTempFile() -> OnTempFileWritten() ----------> RenameTempFile().
+// ^ |
+// | not finished |
+// |----------------------|
+void CdmFileIOImpl::WriteTempFile() {
+ PP_DCHECK(state_ == STATE_WRITING);
PP_DCHECK(io_offset_ < io_buffer_.size());
pp::CompletionCallback cb =
- callback_factory_.NewCallback(&CdmFileIOImpl::OnFileWritten);
+ callback_factory_.NewCallback(&CdmFileIOImpl::OnTempFileWritten);
CHECK_PP_OK_COMPLETIONPENDING(file_io_.Write(io_offset_,
&io_buffer_[io_offset_],
io_buffer_.size() - io_offset_,
@@ -394,14 +439,15 @@ void CdmFileIOImpl::WriteFile() {
WRITE_ERROR);
}
-void CdmFileIOImpl::OnFileWritten(int32_t bytes_written) {
+void CdmFileIOImpl::OnTempFileWritten(int32_t bytes_written) {
CDM_DLOG() << __FUNCTION__ << ": " << bytes_written;
PP_DCHECK(IsMainThread());
- PP_DCHECK(state_ == WRITING_FILE);
+ PP_DCHECK(state_ == STATE_WRITING);
if (bytes_written <= PP_OK) {
- CDM_DLOG() << "Write file failed.";
- OnError(READ_ERROR);
+ CDM_DLOG() << "Write temporary file failed.";
+ state_ = STATE_ERROR;
+ OnError(WRITE_ERROR);
return;
}
@@ -409,35 +455,58 @@ void CdmFileIOImpl::OnFileWritten(int32_t bytes_written) {
PP_DCHECK(io_offset_ <= io_buffer_.size());
if (io_offset_ < io_buffer_.size()) {
- WriteFile();
+ WriteTempFile();
return;
}
- io_buffer_.clear();
- io_offset_ = 0;
- state_ = FILE_OPENED;
- client_->OnWriteComplete(cdm::FileIOClient::kSuccess);
+ // All data written. Now rename the temporary file to the real file.
+ RenameTempFile();
+}
+
+void CdmFileIOImpl::RenameTempFile() {
ddorwin 2014/09/16 18:07:37 Comment that this is actually a move and thus over
xhwang 2014/09/17 21:02:31 Done.
+ PP_DCHECK(state_ == STATE_WRITING);
+
+ pp::CompletionCallback cb =
+ callback_factory_.NewCallback(&CdmFileIOImpl::OnTempFileRenamed);
+ CHECK_PP_OK_COMPLETIONPENDING(
+ file_ref_.Rename(pp::FileRef(file_system_, file_name_.c_str()), cb),
+ WRITE_ERROR);
}
-void CdmFileIOImpl::CloseFile() {
+void CdmFileIOImpl::OnTempFileRenamed(int32_t result) {
+ CDM_DLOG() << __FUNCTION__ << ": " << result;
PP_DCHECK(IsMainThread());
+ PP_DCHECK(state_ == STATE_WRITING);
- state_ = FILE_CLOSED;
+ if (result != PP_OK) {
+ CDM_DLOG() << "Rename temporary file failed.";
+ state_ = STATE_ERROR;
+ OnError(WRITE_ERROR);
+ return;
+ }
- file_io_.Close();
+ Reset();
ddorwin 2014/09/16 18:07:36 Note: The period between line 471 and here is the
xhwang 2014/09/17 21:02:31 Added comment in .h file
+
+ state_ = STATE_OPENED;
+ client_->OnWriteComplete(cdm::FileIOClient::kSuccess);
+}
+
+void CdmFileIOImpl::Reset() {
+ PP_DCHECK(IsMainThread());
io_buffer_.clear();
io_offset_ = 0;
cumulative_read_buffer_.clear();
+ file_io_.Close();
+ file_io_.detach();
ddorwin 2014/09/16 18:07:37 Why detach? This is leaking a reference I believe.
xhwang 2014/09/17 21:02:31 Done.
+ file_ref_.detach();
}
void CdmFileIOImpl::OnError(ErrorType error_type) {
// For *_WHILE_IN_USE errors, do not reset these values. Otherwise, the
// existing read/write operation will fail.
- if (error_type == READ_ERROR || error_type == WRITE_ERROR) {
- io_buffer_.clear();
- io_offset_ = 0;
- cumulative_read_buffer_.clear();
- }
+ if (error_type == READ_ERROR || error_type == WRITE_ERROR)
+ Reset();
+
PostOnMain(callback_factory_.NewCallback(&CdmFileIOImpl::NotifyClientOfError,
error_type));
}

Powered by Google App Engine
This is Rietveld 408576698