| 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 |