Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/base/file_stream.h" | 5 #include "net/base/file_stream.h" |
| 6 | 6 |
| 7 #include "base/message_loop.h" | |
| 8 #include "base/task_runner_util.h" | |
| 9 #include "base/threading/thread_restrictions.h" | |
| 10 #include "base/threading/worker_pool.h" | |
| 11 #include "net/base/file_stream_net_log_parameters.h" | |
| 12 #include "net/base/net_errors.h" | |
| 13 | |
| 14 #if defined(OS_WIN) | |
| 15 #include "net/base/file_stream_win.h" | |
| 16 #elif defined(OS_POSIX) | |
| 17 #include "net/base/file_stream_posix.h" | |
| 18 #endif | |
| 19 | |
| 7 namespace net { | 20 namespace net { |
| 8 | 21 |
| 22 void FileStream::Context::Orphan() { | |
|
willchan no longer on Chromium
2012/09/05 23:20:31
DCHECK(!orphaned_);
We shouldn't call this twice.
| |
| 23 orphaned_ = true; | |
| 24 if (async_in_progress_) { | |
| 25 // Do we need this here? It can race with CloseAsync() and result in | |
| 26 // calling CancelIo() on the file handle that was already closed and | |
| 27 // reopened again. | |
| 28 //if (file_ != base::kInvalidPlatformFileValue) | |
| 29 // CancelIo(file_); | |
| 30 } else { | |
| 31 CloseAsync(CompletionCallback()); | |
| 32 } | |
| 33 } | |
| 34 | |
| 35 void FileStream::Context::OpenAsync(const FilePath& path, | |
| 36 int open_flags, | |
| 37 const CompletionCallback& callback) { | |
| 38 DCHECK(!async_in_progress_); | |
| 39 | |
| 40 BeginOpenEvent(path); | |
| 41 | |
| 42 const bool posted = base::PostTaskAndReplyWithResult( | |
| 43 base::WorkerPool::GetTaskRunner(true /* task_is_slow */), | |
| 44 FROM_HERE, | |
| 45 base::Bind(&Context::OpenFileImpl, | |
| 46 base::Unretained(this), path, open_flags), | |
| 47 base::Bind(&Context::OnOpenCompleted, | |
| 48 base::Unretained(this), callback)); | |
| 49 DCHECK(posted); | |
| 50 | |
| 51 async_in_progress_ = true; | |
| 52 } | |
| 53 | |
| 54 int FileStream::Context::OpenSync(const FilePath& path, int open_flags) { | |
|
willchan no longer on Chromium
2012/09/05 23:20:31
DCHECK(!async_in_progress_);
| |
| 55 BeginOpenEvent(path); | |
| 56 int result = OpenFileImpl(path, open_flags); | |
| 57 CheckForOpenError(&result); | |
| 58 // TODO(satorux): Remove this once all async clients are migrated to use | |
| 59 // Open(). crbug.com/114783 | |
| 60 if (open_flags & base::PLATFORM_FILE_ASYNC) | |
| 61 RegisterInMessageLoop(); | |
| 62 return result; | |
| 63 } | |
| 64 | |
| 65 void FileStream::Context::CloseAsync(const CompletionCallback& callback) { | |
| 66 DCHECK(!async_in_progress_); | |
| 67 | |
| 68 bound_net_log_.AddEvent(net::NetLog::TYPE_FILE_STREAM_CLOSE); | |
| 69 | |
| 70 if (file_ == base::kInvalidPlatformFileValue) { | |
| 71 MessageLoop::current()->PostTask( | |
|
willchan no longer on Chromium
2012/09/05 23:20:31
base::MessageLoopProxy::current()->PostTask(
| |
| 72 FROM_HERE, | |
| 73 base::Bind(&Context::OnCloseCompleted, | |
| 74 base::Unretained(this), callback)); | |
| 75 } else { | |
| 76 const bool posted = base::WorkerPool::PostTaskAndReply( | |
| 77 FROM_HERE, | |
| 78 base::Bind(&Context::CloseFileImpl, | |
| 79 base::Unretained(this)), | |
| 80 base::Bind(&Context::OnCloseCompleted, | |
| 81 base::Unretained(this), callback), | |
| 82 true /* task_is_slow */); | |
| 83 DCHECK(posted); | |
| 84 | |
| 85 async_in_progress_ = true; | |
| 86 } | |
| 87 } | |
| 88 | |
| 89 void FileStream::Context::CloseSync() { | |
| 90 DCHECK(!async_in_progress_); | |
| 91 bound_net_log_.AddEvent(net::NetLog::TYPE_FILE_STREAM_CLOSE); | |
| 92 if (file_ != base::kInvalidPlatformFileValue) { | |
| 93 CloseFileImpl(); | |
| 94 bound_net_log_.EndEvent(net::NetLog::TYPE_FILE_STREAM_OPEN); | |
| 95 } | |
| 96 } | |
| 97 | |
| 98 int FileStream::Context::RecordAndMapError(int error, | |
| 99 FileErrorSource source) const { | |
| 100 // The following check is against incorrect use or bug. File descriptor | |
| 101 // shouldn't ever be closed outside of FileStream while it still tries to do | |
| 102 // something with it. | |
| 103 DCHECK(error != kErrorBadFile); | |
| 104 net::Error net_error = MapSystemError(error); | |
| 105 | |
| 106 if (!orphaned_) { | |
| 107 bound_net_log_.AddEvent(net::NetLog::TYPE_FILE_STREAM_ERROR, | |
| 108 base::Bind(&NetLogFileStreamErrorCallback, | |
| 109 source, error, net_error)); | |
| 110 } | |
| 111 RecordFileError(error, source, record_uma_); | |
| 112 return net_error; | |
| 113 } | |
| 114 | |
| 115 void FileStream::Context::BeginOpenEvent(const FilePath& path) { | |
| 116 std::string file_name = path.AsUTF8Unsafe(); | |
| 117 bound_net_log_.BeginEvent(net::NetLog::TYPE_FILE_STREAM_OPEN, | |
| 118 NetLog::StringCallback("file_name", &file_name)); | |
| 119 } | |
| 120 | |
| 121 int FileStream::Context::OpenFileImpl(const FilePath& path, int open_flags) { | |
| 122 file_ = base::CreatePlatformFile(path, open_flags, NULL, NULL); | |
| 123 if (file_ == base::kInvalidPlatformFileValue) | |
| 124 return GetLastErrno(); | |
| 125 | |
| 126 return OK; | |
| 127 } | |
| 128 | |
| 129 void FileStream::Context::CheckForOpenError(int* result) { | |
|
willchan no longer on Chromium
2012/09/05 23:20:31
What do you think about, instead of having a singl
| |
| 130 if (file_ == base::kInvalidPlatformFileValue) { | |
| 131 bound_net_log_.EndEvent(net::NetLog::TYPE_FILE_STREAM_OPEN); | |
| 132 *result = RecordAndMapError(*result, FILE_ERROR_SOURCE_OPEN); | |
| 133 } | |
| 134 } | |
| 135 | |
| 136 void FileStream::Context::OnOpenCompleted(const CompletionCallback& callback, | |
| 137 int result) { | |
| 138 CheckForOpenError(&result); | |
| 139 if (!orphaned_) | |
| 140 RegisterInMessageLoop(); | |
|
willchan no longer on Chromium
2012/09/05 23:20:31
Can we name this differently? Perhaps something li
| |
| 141 OnAsyncCompleted(callback, result); | |
|
willchan no longer on Chromium
2012/09/05 23:20:31
Why isn't OnAsyncCompleted defined here in file_st
| |
| 142 } | |
| 143 | |
| 144 void FileStream::Context::CloseFileImpl() { | |
|
willchan no longer on Chromium
2012/09/05 23:20:31
How would you feel about getting rid of CloseFileI
| |
| 145 if (!base::ClosePlatformFile(file_)) | |
| 146 NOTREACHED(); | |
| 147 file_ = base::kInvalidPlatformFileValue; | |
| 148 } | |
| 149 | |
| 150 void FileStream::Context::OnCloseCompleted(const CompletionCallback& callback) { | |
| 151 if (!orphaned_) { | |
| 152 bound_net_log_.EndEvent(net::NetLog::TYPE_FILE_STREAM_OPEN); | |
| 153 // Reset this before Run() as Run() may issue a new async operation. | |
| 154 async_in_progress_ = false; | |
| 155 callback.Run(OK); | |
| 156 } else { | |
| 157 delete this; | |
| 158 } | |
| 159 } | |
| 160 | |
| 9 FileStream::FileStream(net::NetLog* net_log) | 161 FileStream::FileStream(net::NetLog* net_log) |
| 10 : impl_(net_log) { | 162 : open_flags_(0), |
| 11 } | 163 bound_net_log_(net::BoundNetLog::Make(net_log, |
| 12 | 164 net::NetLog::SOURCE_FILESTREAM)), |
| 13 FileStream::FileStream( | 165 context_(new Context(bound_net_log_)) { |
| 14 base::PlatformFile file, int flags, net::NetLog* net_log) | 166 bound_net_log_.BeginEvent(net::NetLog::TYPE_FILE_STREAM_ALIVE); |
| 15 : impl_(file, flags, net_log) { | 167 } |
| 168 | |
| 169 FileStream::FileStream(base::PlatformFile file, int flags, net::NetLog* net_log) | |
| 170 : open_flags_(flags), | |
| 171 bound_net_log_(net::BoundNetLog::Make(net_log, | |
| 172 net::NetLog::SOURCE_FILESTREAM)), | |
| 173 context_(new Context(file, bound_net_log_, open_flags_)) { | |
| 174 bound_net_log_.BeginEvent(net::NetLog::TYPE_FILE_STREAM_ALIVE); | |
| 16 } | 175 } |
| 17 | 176 |
| 18 FileStream::~FileStream() { | 177 FileStream::~FileStream() { |
| 178 if (IsOpen() && !is_async()) { | |
| 179 CloseSync(); | |
| 180 context_.reset(); | |
| 181 } else { | |
| 182 context_.release()->Orphan(); | |
| 183 } | |
| 184 | |
| 185 bound_net_log_.EndEvent(net::NetLog::TYPE_FILE_STREAM_ALIVE); | |
| 19 } | 186 } |
| 20 | 187 |
| 21 void FileStream::Close(const CompletionCallback& callback) { | 188 void FileStream::Close(const CompletionCallback& callback) { |
| 22 impl_.Close(callback); | 189 DCHECK(is_async()); |
| 190 context_->CloseAsync(callback); | |
| 23 } | 191 } |
| 24 | 192 |
| 25 void FileStream::CloseSync() { | 193 void FileStream::CloseSync() { |
| 26 impl_.CloseSync(); | 194 base::ThreadRestrictions::AssertIOAllowed(); |
| 195 | |
| 196 // TODO(satorux): Replace the following async stuff with a | |
| 197 // DCHECK(is_async()) once all async clients are migrated to | |
| 198 // use Close(). crbug.com/114783 | |
| 199 if (!context_->async_in_progress()) { | |
| 200 context_->CloseSync(); | |
| 201 } else { | |
| 202 Context* old_context = context_.release(); | |
| 203 context_.reset(new Context(bound_net_log_)); | |
| 204 context_->set_record_uma(old_context->record_uma()); | |
| 205 old_context->Orphan(); | |
| 206 } | |
| 27 } | 207 } |
| 28 | 208 |
| 29 int FileStream::Open(const FilePath& path, int open_flags, | 209 int FileStream::Open(const FilePath& path, int open_flags, |
| 30 const CompletionCallback& callback) { | 210 const CompletionCallback& callback) { |
| 31 return impl_.Open(path, open_flags, callback); | 211 if (IsOpen()) { |
| 212 DLOG(FATAL) << "File is already open!"; | |
| 213 return ERR_UNEXPECTED; | |
| 214 } | |
| 215 | |
| 216 open_flags_ = open_flags; | |
| 217 DCHECK(is_async()); | |
| 218 context_->OpenAsync(path, open_flags, callback); | |
| 219 return ERR_IO_PENDING; | |
| 32 } | 220 } |
| 33 | 221 |
| 34 int FileStream::OpenSync(const FilePath& path, int open_flags) { | 222 int FileStream::OpenSync(const FilePath& path, int open_flags) { |
| 35 return impl_.OpenSync(path, open_flags); | 223 base::ThreadRestrictions::AssertIOAllowed(); |
| 224 | |
| 225 if (IsOpen()) { | |
| 226 DLOG(FATAL) << "File is already open!"; | |
| 227 return ERR_UNEXPECTED; | |
| 228 } | |
| 229 | |
| 230 open_flags_ = open_flags; | |
| 231 // TODO(satorux): Put a DCHECK once all async clients are migrated | |
| 232 // to use Open(). crbug.com/114783 | |
| 233 // | |
| 234 // DCHECK(!is_async()); | |
| 235 return context_->OpenSync(path, open_flags_); | |
| 36 } | 236 } |
| 37 | 237 |
| 38 bool FileStream::IsOpen() const { | 238 bool FileStream::IsOpen() const { |
| 39 return impl_.IsOpen(); | 239 return context_->file() != base::kInvalidPlatformFileValue; |
| 40 } | 240 } |
| 41 | 241 |
| 42 int FileStream::Seek(Whence whence, int64 offset, | 242 int FileStream::Seek(Whence whence, |
| 243 int64 offset, | |
| 43 const Int64CompletionCallback& callback) { | 244 const Int64CompletionCallback& callback) { |
| 44 return impl_.Seek(whence, offset, callback); | 245 if (!IsOpen()) |
| 246 return ERR_UNEXPECTED; | |
| 247 | |
| 248 // Make sure we're async. | |
| 249 DCHECK(is_async()); | |
| 250 context_->SeekAsync(whence, offset, callback); | |
| 251 return ERR_IO_PENDING; | |
| 45 } | 252 } |
| 46 | 253 |
| 47 int64 FileStream::SeekSync(Whence whence, int64 offset) { | 254 int64 FileStream::SeekSync(Whence whence, int64 offset) { |
| 48 return impl_.SeekSync(whence, offset); | 255 base::ThreadRestrictions::AssertIOAllowed(); |
| 256 | |
| 257 if (!IsOpen()) | |
| 258 return ERR_UNEXPECTED; | |
| 259 | |
| 260 // If we're in async, make sure we don't have a request in flight. | |
| 261 DCHECK(!is_async() || !context_->async_in_progress()); | |
| 262 return context_->SeekSync(whence, offset); | |
| 49 } | 263 } |
| 50 | 264 |
| 51 int64 FileStream::Available() { | 265 int64 FileStream::Available() { |
| 52 return impl_.Available(); | 266 base::ThreadRestrictions::AssertIOAllowed(); |
| 53 } | 267 |
| 54 | 268 if (!IsOpen()) |
| 55 int FileStream::Read( | 269 return ERR_UNEXPECTED; |
| 56 IOBuffer* in_buf, int buf_len, const CompletionCallback& callback) { | 270 |
| 57 return impl_.Read(in_buf, buf_len, callback); | 271 int64 cur_pos = SeekSync(FROM_CURRENT, 0); |
| 272 if (cur_pos < 0) | |
| 273 return cur_pos; | |
| 274 | |
| 275 int64 size = context_->GetFileSize(); | |
| 276 if (size < 0) | |
| 277 return size; | |
| 278 | |
| 279 DCHECK_GT(size, cur_pos); | |
| 280 return size - cur_pos; | |
| 281 } | |
| 282 | |
| 283 int FileStream::Read(IOBuffer* buf, | |
| 284 int buf_len, | |
| 285 const CompletionCallback& callback) { | |
| 286 if (!IsOpen()) | |
| 287 return ERR_UNEXPECTED; | |
| 288 | |
| 289 // read(..., 0) will return 0, which indicates end-of-file. | |
| 290 DCHECK_GT(buf_len, 0); | |
| 291 DCHECK(open_flags_ & base::PLATFORM_FILE_READ); | |
| 292 DCHECK(is_async()); | |
| 293 | |
| 294 return context_->ReadAsync(buf, buf_len, callback); | |
| 58 } | 295 } |
| 59 | 296 |
| 60 int FileStream::ReadSync(char* buf, int buf_len) { | 297 int FileStream::ReadSync(char* buf, int buf_len) { |
| 61 return impl_.ReadSync(buf, buf_len); | 298 base::ThreadRestrictions::AssertIOAllowed(); |
| 299 | |
| 300 if (!IsOpen()) | |
| 301 return ERR_UNEXPECTED; | |
| 302 | |
| 303 DCHECK(!is_async()); | |
| 304 // read(..., 0) will return 0, which indicates end-of-file. | |
| 305 DCHECK_GT(buf_len, 0); | |
| 306 DCHECK(open_flags_ & base::PLATFORM_FILE_READ); | |
| 307 | |
| 308 return context_->ReadSync(buf, buf_len); | |
| 62 } | 309 } |
| 63 | 310 |
| 64 int FileStream::ReadUntilComplete(char *buf, int buf_len) { | 311 int FileStream::ReadUntilComplete(char *buf, int buf_len) { |
| 65 return impl_.ReadUntilComplete(buf, buf_len); | 312 base::ThreadRestrictions::AssertIOAllowed(); |
| 66 } | 313 |
| 67 | 314 int to_read = buf_len; |
| 68 int FileStream::Write( | 315 int bytes_total = 0; |
| 69 IOBuffer* buf, int buf_len, const CompletionCallback& callback) { | 316 |
| 70 return impl_.Write(buf, buf_len, callback); | 317 do { |
| 318 int bytes_read = ReadSync(buf, to_read); | |
| 319 if (bytes_read <= 0) { | |
| 320 if (bytes_total == 0) | |
| 321 return bytes_read; | |
| 322 | |
| 323 return bytes_total; | |
| 324 } | |
| 325 | |
| 326 bytes_total += bytes_read; | |
| 327 buf += bytes_read; | |
| 328 to_read -= bytes_read; | |
| 329 } while (bytes_total < buf_len); | |
| 330 | |
| 331 return bytes_total; | |
| 332 } | |
| 333 | |
| 334 int FileStream::Write(IOBuffer* buf, | |
| 335 int buf_len, | |
| 336 const CompletionCallback& callback) { | |
| 337 if (!IsOpen()) | |
| 338 return ERR_UNEXPECTED; | |
| 339 | |
| 340 DCHECK(is_async()); | |
| 341 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE); | |
| 342 // write(..., 0) will return 0, which indicates end-of-file. | |
| 343 DCHECK_GT(buf_len, 0); | |
| 344 | |
| 345 return context_->WriteAsync(buf, buf_len, callback); | |
| 71 } | 346 } |
| 72 | 347 |
| 73 int FileStream::WriteSync(const char* buf, int buf_len) { | 348 int FileStream::WriteSync(const char* buf, int buf_len) { |
| 74 return impl_.WriteSync(buf, buf_len); | 349 base::ThreadRestrictions::AssertIOAllowed(); |
| 350 | |
| 351 if (!IsOpen()) | |
| 352 return ERR_UNEXPECTED; | |
| 353 | |
| 354 DCHECK(!is_async()); | |
| 355 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE); | |
| 356 // write(..., 0) will return 0, which indicates end-of-file. | |
| 357 DCHECK_GT(buf_len, 0); | |
| 358 | |
| 359 return context_->WriteSync(buf, buf_len); | |
| 75 } | 360 } |
| 76 | 361 |
| 77 int64 FileStream::Truncate(int64 bytes) { | 362 int64 FileStream::Truncate(int64 bytes) { |
| 78 return impl_.Truncate(bytes); | 363 base::ThreadRestrictions::AssertIOAllowed(); |
| 364 | |
| 365 if (!IsOpen()) | |
| 366 return ERR_UNEXPECTED; | |
| 367 | |
| 368 // We'd better be open for writing. | |
| 369 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE); | |
| 370 | |
| 371 // Seek to the position to truncate from. | |
| 372 int64 seek_position = SeekSync(FROM_BEGIN, bytes); | |
| 373 if (seek_position != bytes) | |
| 374 return ERR_UNEXPECTED; | |
| 375 | |
| 376 // And truncate the file. | |
| 377 return context_->Truncate(bytes); | |
| 79 } | 378 } |
| 80 | 379 |
| 81 int FileStream::Flush() { | 380 int FileStream::Flush() { |
| 82 return impl_.Flush(); | 381 base::ThreadRestrictions::AssertIOAllowed(); |
| 382 | |
| 383 if (!IsOpen()) | |
| 384 return ERR_UNEXPECTED; | |
| 385 | |
| 386 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE); | |
| 387 return context_->Flush(); | |
| 83 } | 388 } |
| 84 | 389 |
| 85 void FileStream::EnableErrorStatistics() { | 390 void FileStream::EnableErrorStatistics() { |
| 86 impl_.EnableErrorStatistics(); | 391 context_->set_record_uma(true); |
| 87 } | 392 } |
| 88 | 393 |
| 89 void FileStream::SetBoundNetLogSource( | 394 void FileStream::SetBoundNetLogSource( |
| 90 const net::BoundNetLog& owner_bound_net_log) { | 395 const net::BoundNetLog& owner_bound_net_log) { |
| 91 impl_.SetBoundNetLogSource(owner_bound_net_log); | 396 if ((owner_bound_net_log.source().id == net::NetLog::Source::kInvalidId) && |
| 397 (bound_net_log_.source().id == net::NetLog::Source::kInvalidId)) { | |
| 398 // Both |BoundNetLog|s are invalid. | |
| 399 return; | |
| 400 } | |
| 401 | |
| 402 // Should never connect to itself. | |
| 403 DCHECK_NE(bound_net_log_.source().id, owner_bound_net_log.source().id); | |
| 404 | |
| 405 bound_net_log_.AddEvent( | |
| 406 net::NetLog::TYPE_FILE_STREAM_BOUND_TO_OWNER, | |
| 407 owner_bound_net_log.source().ToEventParametersCallback()); | |
| 408 | |
| 409 owner_bound_net_log.AddEvent( | |
| 410 net::NetLog::TYPE_FILE_STREAM_SOURCE, | |
| 411 bound_net_log_.source().ToEventParametersCallback()); | |
| 92 } | 412 } |
| 93 | 413 |
| 94 base::PlatformFile FileStream::GetPlatformFileForTesting() { | 414 base::PlatformFile FileStream::GetPlatformFileForTesting() { |
| 95 return impl_.GetPlatformFileForTesting(); | 415 return context_->file(); |
| 96 } | 416 } |
| 97 | 417 |
| 98 } // namespace net | 418 } // namespace net |
| OLD | NEW |