OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license | |
5 * that can be found in the LICENSE file in the root of the source | |
6 * tree. An additional intellectual property rights grant can be found | |
7 * in the file PATENTS. All contributing project authors may | |
8 * be found in the AUTHORS file in the root of the source tree. | |
9 */ | |
10 | |
11 #include "webrtc/system_wrappers/include/data_log.h" | |
12 | |
13 #include <assert.h> | |
14 | |
15 #include <algorithm> | |
16 #include <list> | |
17 | |
18 #include "webrtc/system_wrappers/include/critical_section_wrapper.h" | |
19 #include "webrtc/system_wrappers/include/event_wrapper.h" | |
20 #include "webrtc/system_wrappers/include/file_wrapper.h" | |
21 #include "webrtc/system_wrappers/include/rw_lock_wrapper.h" | |
22 | |
23 namespace webrtc { | |
24 | |
25 DataLogImpl::CritSectScopedPtr DataLogImpl::crit_sect_( | |
26 CriticalSectionWrapper::CreateCriticalSection()); | |
27 | |
28 DataLogImpl* DataLogImpl::instance_ = NULL; | |
29 | |
30 // A Row contains cells, which are indexed by the column names as std::string. | |
31 // The string index is treated in a case sensitive way. | |
32 class Row { | |
33 public: | |
34 Row(); | |
35 ~Row(); | |
36 | |
37 // Inserts a Container into the cell of the column specified with | |
38 // column_name. | |
39 // column_name is treated in a case sensitive way. | |
40 int InsertCell(const std::string& column_name, | |
41 const Container* value_container); | |
42 | |
43 // Converts the value at the column specified by column_name to a string | |
44 // stored in value_string. | |
45 // column_name is treated in a case sensitive way. | |
46 void ToString(const std::string& column_name, std::string* value_string); | |
47 | |
48 private: | |
49 // Collection of containers indexed by column name as std::string | |
50 typedef std::map<std::string, const Container*> CellMap; | |
51 | |
52 CellMap cells_; | |
53 CriticalSectionWrapper* cells_lock_; | |
54 }; | |
55 | |
56 // A LogTable contains multiple rows, where only the latest row is active for | |
57 // editing. The rows are defined by the ColumnMap, which contains the name of | |
58 // each column and the length of the column (1 for one-value-columns and greater | |
59 // than 1 for multi-value-columns). | |
60 class LogTable { | |
61 public: | |
62 LogTable(); | |
63 ~LogTable(); | |
64 | |
65 // Adds the column with name column_name to the table. The column will be a | |
66 // multi-value-column if multi_value_length is greater than 1. | |
67 // column_name is treated in a case sensitive way. | |
68 int AddColumn(const std::string& column_name, int multi_value_length); | |
69 | |
70 // Buffers the current row while it is waiting to be written to file, | |
71 // which is done by a call to Flush(). A new row is available when the | |
72 // function returns | |
73 void NextRow(); | |
74 | |
75 // Inserts a Container into the cell of the column specified with | |
76 // column_name. | |
77 // column_name is treated in a case sensitive way. | |
78 int InsertCell(const std::string& column_name, | |
79 const Container* value_container); | |
80 | |
81 // Creates a log file, named as specified in the string file_name, to | |
82 // where the table will be written when calling Flush(). | |
83 int CreateLogFile(const std::string& file_name); | |
84 | |
85 // Write all complete rows to file. | |
86 // May not be called by two threads simultaneously (doing so may result in | |
87 // a race condition). Will be called by the file_writer_thread_ when that | |
88 // thread is running. | |
89 void Flush(); | |
90 | |
91 private: | |
92 // Collection of multi_value_lengths indexed by column name as std::string | |
93 typedef std::map<std::string, int> ColumnMap; | |
94 typedef std::list<Row*> RowList; | |
95 | |
96 ColumnMap columns_; | |
97 RowList rows_[2]; | |
98 RowList* rows_history_; | |
99 RowList* rows_flush_; | |
100 Row* current_row_; | |
101 FileWrapper* file_; | |
102 bool write_header_; | |
103 CriticalSectionWrapper* table_lock_; | |
104 }; | |
105 | |
106 Row::Row() | |
107 : cells_(), | |
108 cells_lock_(CriticalSectionWrapper::CreateCriticalSection()) { | |
109 } | |
110 | |
111 Row::~Row() { | |
112 for (CellMap::iterator it = cells_.begin(); it != cells_.end();) { | |
113 delete it->second; | |
114 // For maps all iterators (except the erased) are valid after an erase | |
115 cells_.erase(it++); | |
116 } | |
117 delete cells_lock_; | |
118 } | |
119 | |
120 int Row::InsertCell(const std::string& column_name, | |
121 const Container* value_container) { | |
122 CriticalSectionScoped synchronize(cells_lock_); | |
123 assert(cells_.count(column_name) == 0); | |
124 if (cells_.count(column_name) > 0) | |
125 return -1; | |
126 cells_[column_name] = value_container; | |
127 return 0; | |
128 } | |
129 | |
130 void Row::ToString(const std::string& column_name, | |
131 std::string* value_string) { | |
132 CriticalSectionScoped synchronize(cells_lock_); | |
133 const Container* container = cells_[column_name]; | |
134 if (container == NULL) { | |
135 *value_string = "NaN,"; | |
136 return; | |
137 } | |
138 container->ToString(value_string); | |
139 } | |
140 | |
141 LogTable::LogTable() | |
142 : columns_(), | |
143 rows_(), | |
144 rows_history_(&rows_[0]), | |
145 rows_flush_(&rows_[1]), | |
146 current_row_(new Row), | |
147 file_(FileWrapper::Create()), | |
148 write_header_(true), | |
149 table_lock_(CriticalSectionWrapper::CreateCriticalSection()) { | |
150 } | |
151 | |
152 LogTable::~LogTable() { | |
153 for (RowList::iterator row_it = rows_history_->begin(); | |
154 row_it != rows_history_->end();) { | |
155 delete *row_it; | |
156 row_it = rows_history_->erase(row_it); | |
157 } | |
158 for (ColumnMap::iterator col_it = columns_.begin(); | |
159 col_it != columns_.end();) { | |
160 // For maps all iterators (except the erased) are valid after an erase | |
161 columns_.erase(col_it++); | |
162 } | |
163 if (file_ != NULL) { | |
164 file_->Flush(); | |
165 file_->CloseFile(); | |
166 delete file_; | |
167 } | |
168 delete current_row_; | |
169 delete table_lock_; | |
170 } | |
171 | |
172 int LogTable::AddColumn(const std::string& column_name, | |
173 int multi_value_length) { | |
174 assert(multi_value_length > 0); | |
175 if (!write_header_) { | |
176 // It's not allowed to add new columns after the header | |
177 // has been written. | |
178 assert(false); | |
179 return -1; | |
180 } else { | |
181 CriticalSectionScoped synchronize(table_lock_); | |
182 if (write_header_) | |
183 columns_[column_name] = multi_value_length; | |
184 else | |
185 return -1; | |
186 } | |
187 return 0; | |
188 } | |
189 | |
190 void LogTable::NextRow() { | |
191 CriticalSectionScoped sync_rows(table_lock_); | |
192 rows_history_->push_back(current_row_); | |
193 current_row_ = new Row; | |
194 } | |
195 | |
196 int LogTable::InsertCell(const std::string& column_name, | |
197 const Container* value_container) { | |
198 CriticalSectionScoped synchronize(table_lock_); | |
199 assert(columns_.count(column_name) > 0); | |
200 if (columns_.count(column_name) == 0) | |
201 return -1; | |
202 return current_row_->InsertCell(column_name, value_container); | |
203 } | |
204 | |
205 int LogTable::CreateLogFile(const std::string& file_name) { | |
206 if (file_name.length() == 0) | |
207 return -1; | |
208 if (file_->is_open()) | |
209 return -1; | |
210 // Open with read/write permissions | |
211 return file_->OpenFile(file_name.c_str(), false) ? 0 : -1; | |
212 } | |
213 | |
214 void LogTable::Flush() { | |
215 ColumnMap::iterator column_it; | |
216 bool commit_header = false; | |
217 if (write_header_) { | |
218 CriticalSectionScoped synchronize(table_lock_); | |
219 if (write_header_) { | |
220 commit_header = true; | |
221 write_header_ = false; | |
222 } | |
223 } | |
224 if (commit_header) { | |
225 for (column_it = columns_.begin(); | |
226 column_it != columns_.end(); ++column_it) { | |
227 if (column_it->second > 1) { | |
228 file_->WriteText("%s[%u],", column_it->first.c_str(), | |
229 column_it->second); | |
230 for (int i = 1; i < column_it->second; ++i) | |
231 file_->WriteText(","); | |
232 } else { | |
233 file_->WriteText("%s,", column_it->first.c_str()); | |
234 } | |
235 } | |
236 if (columns_.size() > 0) | |
237 file_->WriteText("\n"); | |
238 } | |
239 | |
240 // Swap the list used for flushing with the list containing the row history | |
241 // and clear the history. We also create a local pointer to the new | |
242 // list used for flushing to avoid race conditions if another thread | |
243 // calls this function while we are writing. | |
244 // We don't want to block the list while we're writing to file. | |
245 { | |
246 CriticalSectionScoped synchronize(table_lock_); | |
247 RowList* tmp = rows_flush_; | |
248 rows_flush_ = rows_history_; | |
249 rows_history_ = tmp; | |
250 rows_history_->clear(); | |
251 } | |
252 | |
253 // Write all complete rows to file and delete them | |
254 for (RowList::iterator row_it = rows_flush_->begin(); | |
255 row_it != rows_flush_->end();) { | |
256 for (column_it = columns_.begin(); | |
257 column_it != columns_.end(); ++column_it) { | |
258 std::string row_string; | |
259 (*row_it)->ToString(column_it->first, &row_string); | |
260 file_->WriteText("%s", row_string.c_str()); | |
261 } | |
262 if (columns_.size() > 0) | |
263 file_->WriteText("\n"); | |
264 delete *row_it; | |
265 row_it = rows_flush_->erase(row_it); | |
266 } | |
267 } | |
268 | |
269 int DataLog::CreateLog() { | |
270 return DataLogImpl::CreateLog(); | |
271 } | |
272 | |
273 void DataLog::ReturnLog() { | |
274 return DataLogImpl::ReturnLog(); | |
275 } | |
276 | |
277 std::string DataLog::Combine(const std::string& table_name, int table_id) { | |
278 std::stringstream ss; | |
279 std::string combined_id = table_name; | |
280 std::string number_suffix; | |
281 ss << "_" << table_id; | |
282 ss >> number_suffix; | |
283 combined_id += number_suffix; | |
284 std::transform(combined_id.begin(), combined_id.end(), combined_id.begin(), | |
285 ::tolower); | |
286 return combined_id; | |
287 } | |
288 | |
289 int DataLog::AddTable(const std::string& table_name) { | |
290 DataLogImpl* data_log = DataLogImpl::StaticInstance(); | |
291 if (data_log == NULL) | |
292 return -1; | |
293 return data_log->AddTable(table_name); | |
294 } | |
295 | |
296 int DataLog::AddColumn(const std::string& table_name, | |
297 const std::string& column_name, | |
298 int multi_value_length) { | |
299 DataLogImpl* data_log = DataLogImpl::StaticInstance(); | |
300 if (data_log == NULL) | |
301 return -1; | |
302 return data_log->DataLogImpl::StaticInstance()->AddColumn(table_name, | |
303 column_name, | |
304 multi_value_length); | |
305 } | |
306 | |
307 int DataLog::NextRow(const std::string& table_name) { | |
308 DataLogImpl* data_log = DataLogImpl::StaticInstance(); | |
309 if (data_log == NULL) | |
310 return -1; | |
311 return data_log->DataLogImpl::StaticInstance()->NextRow(table_name); | |
312 } | |
313 | |
314 DataLogImpl::DataLogImpl() | |
315 : counter_(1), | |
316 tables_(), | |
317 flush_event_(EventWrapper::Create()), | |
318 file_writer_thread_( | |
319 new rtc::PlatformThread(DataLogImpl::Run, instance_, "DataLog")), | |
320 tables_lock_(RWLockWrapper::CreateRWLock()) {} | |
321 | |
322 DataLogImpl::~DataLogImpl() { | |
323 StopThread(); | |
324 Flush(); // Write any remaining rows | |
325 delete flush_event_; | |
326 for (TableMap::iterator it = tables_.begin(); it != tables_.end();) { | |
327 delete static_cast<LogTable*>(it->second); | |
328 // For maps all iterators (except the erased) are valid after an erase | |
329 tables_.erase(it++); | |
330 } | |
331 delete tables_lock_; | |
332 } | |
333 | |
334 int DataLogImpl::CreateLog() { | |
335 CriticalSectionScoped synchronize(crit_sect_.get()); | |
336 if (instance_ == NULL) { | |
337 instance_ = new DataLogImpl(); | |
338 return instance_->Init(); | |
339 } else { | |
340 ++instance_->counter_; | |
341 } | |
342 return 0; | |
343 } | |
344 | |
345 int DataLogImpl::Init() { | |
346 file_writer_thread_->Start(); | |
347 file_writer_thread_->SetPriority(rtc::kHighestPriority); | |
348 return 0; | |
349 } | |
350 | |
351 DataLogImpl* DataLogImpl::StaticInstance() { | |
352 return instance_; | |
353 } | |
354 | |
355 void DataLogImpl::ReturnLog() { | |
356 CriticalSectionScoped synchronize(crit_sect_.get()); | |
357 if (instance_ && instance_->counter_ > 1) { | |
358 --instance_->counter_; | |
359 return; | |
360 } | |
361 delete instance_; | |
362 instance_ = NULL; | |
363 } | |
364 | |
365 int DataLogImpl::AddTable(const std::string& table_name) { | |
366 WriteLockScoped synchronize(*tables_lock_); | |
367 // Make sure we don't add a table which already exists | |
368 if (tables_.count(table_name) > 0) | |
369 return -1; | |
370 tables_[table_name] = new LogTable(); | |
371 if (tables_[table_name]->CreateLogFile(table_name + ".txt") == -1) | |
372 return -1; | |
373 return 0; | |
374 } | |
375 | |
376 int DataLogImpl::AddColumn(const std::string& table_name, | |
377 const std::string& column_name, | |
378 int multi_value_length) { | |
379 ReadLockScoped synchronize(*tables_lock_); | |
380 if (tables_.count(table_name) == 0) | |
381 return -1; | |
382 return tables_[table_name]->AddColumn(column_name, multi_value_length); | |
383 } | |
384 | |
385 int DataLogImpl::InsertCell(const std::string& table_name, | |
386 const std::string& column_name, | |
387 const Container* value_container) { | |
388 ReadLockScoped synchronize(*tables_lock_); | |
389 assert(tables_.count(table_name) > 0); | |
390 if (tables_.count(table_name) == 0) | |
391 return -1; | |
392 return tables_[table_name]->InsertCell(column_name, value_container); | |
393 } | |
394 | |
395 int DataLogImpl::NextRow(const std::string& table_name) { | |
396 ReadLockScoped synchronize(*tables_lock_); | |
397 if (tables_.count(table_name) == 0) | |
398 return -1; | |
399 tables_[table_name]->NextRow(); | |
400 // Signal a complete row | |
401 flush_event_->Set(); | |
402 return 0; | |
403 } | |
404 | |
405 void DataLogImpl::Flush() { | |
406 ReadLockScoped synchronize(*tables_lock_); | |
407 for (TableMap::iterator it = tables_.begin(); it != tables_.end(); ++it) { | |
408 it->second->Flush(); | |
409 } | |
410 } | |
411 | |
412 bool DataLogImpl::Run(void* obj) { | |
413 static_cast<DataLogImpl*>(obj)->Process(); | |
414 return true; | |
415 } | |
416 | |
417 void DataLogImpl::Process() { | |
418 // Wait for a row to be complete | |
419 flush_event_->Wait(WEBRTC_EVENT_INFINITE); | |
420 Flush(); | |
421 } | |
422 | |
423 void DataLogImpl::StopThread() { | |
424 flush_event_->Set(); | |
425 file_writer_thread_->Stop(); | |
426 } | |
427 | |
428 } // namespace webrtc | |
OLD | NEW |