DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
threaded_io_file.cc
1 // Copyright 2015 Google Inc. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file or at
5 // https://developers.google.com/open-source/licenses/bsd
6 
7 #include "packager/media/file/threaded_io_file.h"
8 
9 #include "packager/base/bind.h"
10 #include "packager/base/bind_helpers.h"
11 #include "packager/base/location.h"
12 #include "packager/base/threading/worker_pool.h"
13 
14 namespace shaka {
15 namespace media {
16 
17 using base::subtle::NoBarrier_Load;
18 using base::subtle::NoBarrier_Store;
19 
20 ThreadedIoFile::ThreadedIoFile(std::unique_ptr<File, FileCloser> internal_file,
21  Mode mode,
22  uint64_t io_cache_size,
23  uint64_t io_block_size)
24  : File(internal_file->file_name()),
25  internal_file_(std::move(internal_file)),
26  mode_(mode),
27  cache_(io_cache_size),
28  io_buffer_(io_block_size),
29  position_(0),
30  size_(0),
31  eof_(false),
32  flushing_(false),
33  flush_complete_event_(false, false),
34  internal_file_error_(0),
35  task_exit_event_(false, false) {
36  DCHECK(internal_file_);
37 }
38 
39 ThreadedIoFile::~ThreadedIoFile() {}
40 
42  DCHECK(internal_file_);
43 
44  if (!internal_file_->Open())
45  return false;
46 
47  position_ = 0;
48  size_ = internal_file_->Size();
49 
50  base::WorkerPool::PostTask(FROM_HERE, base::Bind(&ThreadedIoFile::TaskHandler,
51  base::Unretained(this)),
52  true /* task_is_slow */);
53  return true;
54 }
55 
57  DCHECK(internal_file_);
58 
59  if (mode_ == kOutputMode)
60  Flush();
61 
62  cache_.Close();
63  task_exit_event_.Wait();
64 
65  bool result = internal_file_.release()->Close();
66  delete this;
67  return result;
68 }
69 
70 int64_t ThreadedIoFile::Read(void* buffer, uint64_t length) {
71  DCHECK(internal_file_);
72  DCHECK_EQ(kInputMode, mode_);
73 
74  if (NoBarrier_Load(&eof_) && !cache_.BytesCached())
75  return 0;
76 
77  if (NoBarrier_Load(&internal_file_error_))
78  return NoBarrier_Load(&internal_file_error_);
79 
80 
81  uint64_t bytes_read = cache_.Read(buffer, length);
82  position_ += bytes_read;
83 
84  return bytes_read;
85 }
86 
87 int64_t ThreadedIoFile::Write(const void* buffer, uint64_t length) {
88  DCHECK(internal_file_);
89  DCHECK_EQ(kOutputMode, mode_);
90 
91  if (NoBarrier_Load(&internal_file_error_))
92  return NoBarrier_Load(&internal_file_error_);
93 
94  uint64_t bytes_written = cache_.Write(buffer, length);
95  position_ += bytes_written;
96  if (position_ > size_)
97  size_ = position_;
98 
99  return bytes_written;
100 }
101 
103  DCHECK(internal_file_);
104 
105  return size_;
106 }
107 
109  DCHECK(internal_file_);
110  DCHECK_EQ(kOutputMode, mode_);
111 
112  flushing_ = true;
113  cache_.Close();
114  flush_complete_event_.Wait();
115  return internal_file_->Flush();
116 }
117 
118 bool ThreadedIoFile::Seek(uint64_t position) {
119  if (mode_ == kOutputMode) {
120  // Writing. Just flush the cache and seek.
121  if (!Flush()) return false;
122  if (!internal_file_->Seek(position)) return false;
123  } else {
124  // Reading. Close cache, wait for thread task to exit, seek, and re-post
125  // the task.
126  cache_.Close();
127  task_exit_event_.Wait();
128  bool result = internal_file_->Seek(position);
129  if (!result) {
130  // Seek failed. Seek to logical position instead.
131  if (!internal_file_->Seek(position_) && (position != position_)) {
132  LOG(WARNING) << "Seek failed. ThreadedIoFile left in invalid state.";
133  }
134  }
135  cache_.Reopen();
136  eof_ = false;
137  base::WorkerPool::PostTask(
138  FROM_HERE,
139  base::Bind(&ThreadedIoFile::TaskHandler, base::Unretained(this)),
140  true /* task_is_slow */);
141  if (!result) return false;
142  }
143  position_ = position;
144  return true;
145 }
146 
147 bool ThreadedIoFile::Tell(uint64_t* position) {
148  DCHECK(position);
149 
150  *position = position_;
151  return true;
152 }
153 
154 void ThreadedIoFile::TaskHandler() {
155  if (mode_ == kInputMode)
156  RunInInputMode();
157  else
158  RunInOutputMode();
159  task_exit_event_.Signal();
160 }
161 
162 void ThreadedIoFile::RunInInputMode() {
163  DCHECK(internal_file_);
164  DCHECK_EQ(kInputMode, mode_);
165 
166  while (true) {
167  int64_t read_result = internal_file_->Read(&io_buffer_[0],
168  io_buffer_.size());
169  if (read_result <= 0) {
170  NoBarrier_Store(&eof_, read_result == 0);
171  NoBarrier_Store(&internal_file_error_, read_result);
172  cache_.Close();
173  return;
174  }
175  if (cache_.Write(&io_buffer_[0], read_result) == 0) {
176  return;
177  }
178  }
179 }
180 
181 void ThreadedIoFile::RunInOutputMode() {
182  DCHECK(internal_file_);
183  DCHECK_EQ(kOutputMode, mode_);
184 
185  while (true) {
186  uint64_t write_bytes = cache_.Read(&io_buffer_[0], io_buffer_.size());
187  if (write_bytes == 0) {
188  if (flushing_) {
189  cache_.Reopen();
190  flushing_ = false;
191  flush_complete_event_.Signal();
192  } else {
193  return;
194  }
195  } else {
196  uint64_t bytes_written(0);
197  while (bytes_written < write_bytes) {
198  int64_t write_result = internal_file_->Write(
199  &io_buffer_[bytes_written], write_bytes - bytes_written);
200  if (write_result < 0) {
201  NoBarrier_Store(&internal_file_error_, write_result);
202  cache_.Close();
203  return;
204  }
205  bytes_written += write_result;
206  }
207  }
208  }
209 }
210 
211 } // namespace media
212 } // namespace shaka
bool Tell(uint64_t *position) override
bool Seek(uint64_t position) override
int64_t Write(const void *buffer, uint64_t length) override
void Reopen()
Reopens the cache. Any data still in the cache will be lost.
Definition: io_cache.cc:116
uint64_t Read(void *buffer, uint64_t size)
Definition: io_cache.cc:38
bool Open() override
Internal open. Should not be used directly.
uint64_t BytesCached()
Definition: io_cache.cc:125
uint64_t Write(const void *buffer, uint64_t size)
Definition: io_cache.cc:66
int64_t Read(void *buffer, uint64_t length) override