Shaka Packager SDK
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/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 
16 using base::subtle::NoBarrier_Load;
17 using base::subtle::NoBarrier_Store;
18 
19 ThreadedIoFile::ThreadedIoFile(std::unique_ptr<File, FileCloser> internal_file,
20  Mode mode,
21  uint64_t io_cache_size,
22  uint64_t io_block_size)
23  : File(internal_file->file_name()),
24  internal_file_(std::move(internal_file)),
25  mode_(mode),
26  cache_(io_cache_size),
27  io_buffer_(io_block_size),
28  position_(0),
29  size_(0),
30  eof_(false),
31  flushing_(false),
32  flush_complete_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
33  base::WaitableEvent::InitialState::NOT_SIGNALED),
34  internal_file_error_(0),
35  task_exit_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
36  base::WaitableEvent::InitialState::NOT_SIGNALED) {
37  DCHECK(internal_file_);
38 }
39 
40 ThreadedIoFile::~ThreadedIoFile() {}
41 
43  DCHECK(internal_file_);
44 
45  if (!internal_file_->Open())
46  return false;
47 
48  position_ = 0;
49  size_ = internal_file_->Size();
50 
51  base::WorkerPool::PostTask(
52  FROM_HERE,
53  base::Bind(&ThreadedIoFile::TaskHandler, base::Unretained(this)),
54  true /* task_is_slow */);
55  return true;
56 }
57 
59  DCHECK(internal_file_);
60 
61  bool result = true;
62  if (mode_ == kOutputMode)
63  result = Flush();
64 
65  cache_.Close();
66  task_exit_event_.Wait();
67 
68  result &= internal_file_.release()->Close();
69  delete this;
70  return result;
71 }
72 
73 int64_t ThreadedIoFile::Read(void* buffer, uint64_t length) {
74  DCHECK(internal_file_);
75  DCHECK_EQ(kInputMode, mode_);
76 
77  if (NoBarrier_Load(&eof_) && !cache_.BytesCached())
78  return 0;
79 
80  if (NoBarrier_Load(&internal_file_error_))
81  return NoBarrier_Load(&internal_file_error_);
82 
83  uint64_t bytes_read = cache_.Read(buffer, length);
84  position_ += bytes_read;
85 
86  return bytes_read;
87 }
88 
89 int64_t ThreadedIoFile::Write(const void* buffer, uint64_t length) {
90  DCHECK(internal_file_);
91  DCHECK_EQ(kOutputMode, mode_);
92 
93  if (NoBarrier_Load(&internal_file_error_))
94  return NoBarrier_Load(&internal_file_error_);
95 
96  uint64_t bytes_written = cache_.Write(buffer, length);
97  position_ += bytes_written;
98  if (position_ > size_)
99  size_ = position_;
100 
101  return bytes_written;
102 }
103 
105  DCHECK(internal_file_);
106 
107  return size_;
108 }
109 
111  DCHECK(internal_file_);
112  DCHECK_EQ(kOutputMode, mode_);
113 
114  if (NoBarrier_Load(&internal_file_error_))
115  return false;
116 
117  flushing_ = true;
118  cache_.Close();
119  flush_complete_event_.Wait();
120  return internal_file_->Flush();
121 }
122 
123 bool ThreadedIoFile::Seek(uint64_t position) {
124  if (mode_ == kOutputMode) {
125  // Writing. Just flush the cache and seek.
126  if (!Flush())
127  return false;
128  if (!internal_file_->Seek(position))
129  return false;
130  } else {
131  // Reading. Close cache, wait for thread task to exit, seek, and re-post
132  // the task.
133  cache_.Close();
134  task_exit_event_.Wait();
135  bool result = internal_file_->Seek(position);
136  if (!result) {
137  // Seek failed. Seek to logical position instead.
138  if (!internal_file_->Seek(position_) && (position != position_)) {
139  LOG(WARNING) << "Seek failed. ThreadedIoFile left in invalid state.";
140  }
141  }
142  cache_.Reopen();
143  eof_ = false;
144  base::WorkerPool::PostTask(
145  FROM_HERE,
146  base::Bind(&ThreadedIoFile::TaskHandler, base::Unretained(this)),
147  true /* task_is_slow */);
148  if (!result)
149  return false;
150  }
151  position_ = position;
152  return true;
153 }
154 
155 bool ThreadedIoFile::Tell(uint64_t* position) {
156  DCHECK(position);
157 
158  *position = position_;
159  return true;
160 }
161 
162 void ThreadedIoFile::TaskHandler() {
163  if (mode_ == kInputMode)
164  RunInInputMode();
165  else
166  RunInOutputMode();
167  task_exit_event_.Signal();
168 }
169 
170 void ThreadedIoFile::RunInInputMode() {
171  DCHECK(internal_file_);
172  DCHECK_EQ(kInputMode, mode_);
173 
174  while (true) {
175  int64_t read_result =
176  internal_file_->Read(&io_buffer_[0], io_buffer_.size());
177  if (read_result <= 0) {
178  NoBarrier_Store(&eof_, read_result == 0);
179  NoBarrier_Store(&internal_file_error_, read_result);
180  cache_.Close();
181  return;
182  }
183  if (cache_.Write(&io_buffer_[0], read_result) == 0) {
184  return;
185  }
186  }
187 }
188 
189 void ThreadedIoFile::RunInOutputMode() {
190  DCHECK(internal_file_);
191  DCHECK_EQ(kOutputMode, mode_);
192 
193  while (true) {
194  uint64_t write_bytes = cache_.Read(&io_buffer_[0], io_buffer_.size());
195  if (write_bytes == 0) {
196  if (flushing_) {
197  cache_.Reopen();
198  flushing_ = false;
199  flush_complete_event_.Signal();
200  } else {
201  return;
202  }
203  } else {
204  uint64_t bytes_written(0);
205  while (bytes_written < write_bytes) {
206  int64_t write_result = internal_file_->Write(
207  &io_buffer_[bytes_written], write_bytes - bytes_written);
208  if (write_result < 0) {
209  NoBarrier_Store(&internal_file_error_, write_result);
210  cache_.Close();
211  if (flushing_) {
212  flushing_ = false;
213  flush_complete_event_.Signal();
214  }
215  return;
216  }
217  bytes_written += write_result;
218  }
219  }
220  }
221 }
222 
223 } // namespace shaka
int64_t Size() override
int64_t Write(const void *buffer, uint64_t length) override
STL namespace.
All the methods that are virtual are virtual for mocking.
bool Open() override
Internal open. Should not be used directly.
bool Seek(uint64_t position) override
bool Tell(uint64_t *position) override
int64_t Read(void *buffer, uint64_t length) override