chiark / gitweb /
e4ecb03f238d20d83ac9f592c39b5143e93f0fae
[stressapptest] / src / logger.cc
1 // Copyright 2009 Google Inc. All Rights Reserved.
2
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6
7 //      http://www.apache.org/licenses/LICENSE-2.0
8
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "logger.h"
16
17 #include <pthread.h>
18 #include <stdarg.h>
19 #include <stdio.h>
20 #include <unistd.h>
21
22 #include <string>
23 #include <vector>
24
25 // This file must work with autoconf on its public version,
26 // so these includes are correct.
27 #include "sattypes.h"
28
29
30 Logger *Logger::GlobalLogger() {
31   static Logger logger;
32   return &logger;
33 }
34
35 void Logger::VLogF(int priority, const char *format, va_list args) {
36   if (priority > verbosity_) {
37     return;
38   }
39   char buffer[4096];
40   int length = vsnprintf(buffer, sizeof buffer, format, args);
41   if (static_cast<size_t>(length) >= sizeof buffer) {
42     length = sizeof buffer;
43     buffer[sizeof buffer - 1] = '\n';
44   }
45   QueueLogLine(new string(buffer, length));
46 }
47
48 void Logger::StartThread() {
49   LOGGER_ASSERT(!thread_running_);
50   thread_running_ = true;
51   LOGGER_ASSERT(0 == pthread_create(&thread_, NULL, &StartRoutine, this));
52 }
53
54 void Logger::StopThread() {
55   LOGGER_ASSERT(thread_running_);
56   thread_running_ = false;
57   LOGGER_ASSERT(0 == pthread_mutex_lock(&queued_lines_mutex_));
58   bool need_cond_signal = queued_lines_.empty();
59   queued_lines_.push_back(NULL);
60   LOGGER_ASSERT(0 == pthread_mutex_unlock(&queued_lines_mutex_));
61   if (need_cond_signal) {
62     LOGGER_ASSERT(0 == pthread_cond_signal(&queued_lines_cond_));
63   }
64   LOGGER_ASSERT(0 == pthread_join(thread_, NULL));
65 }
66
67 Logger::Logger() : verbosity_(20), log_fd_(-1), thread_running_(false) {
68   LOGGER_ASSERT(0 == pthread_mutex_init(&queued_lines_mutex_, NULL));
69   LOGGER_ASSERT(0 == pthread_cond_init(&queued_lines_cond_, NULL));
70   LOGGER_ASSERT(0 == pthread_cond_init(&full_queue_cond_, NULL));
71 }
72
73 Logger::~Logger() {
74   LOGGER_ASSERT(0 == pthread_mutex_destroy(&queued_lines_mutex_));
75   LOGGER_ASSERT(0 == pthread_cond_destroy(&queued_lines_cond_));
76   LOGGER_ASSERT(0 == pthread_cond_destroy(&full_queue_cond_));
77 }
78
79 void Logger::QueueLogLine(string *line) {
80   LOGGER_ASSERT(line != NULL);
81   LOGGER_ASSERT(0 == pthread_mutex_lock(&queued_lines_mutex_));
82   if (thread_running_) {
83     if (queued_lines_.size() >= kMaxQueueSize) {
84       LOGGER_ASSERT(0 == pthread_cond_wait(&full_queue_cond_,
85                                            &queued_lines_mutex_));
86     }
87     if (queued_lines_.empty()) {
88       LOGGER_ASSERT(0 == pthread_cond_signal(&queued_lines_cond_));
89     }
90     queued_lines_.push_back(line);
91   } else {
92     WriteAndDeleteLogLine(line);
93   }
94   LOGGER_ASSERT(0 == pthread_mutex_unlock(&queued_lines_mutex_));
95 }
96
97 namespace {
98 void WriteToFile(const string& line, int fd) {
99   LOGGER_ASSERT(write(fd, line.data(), line.size()) ==
100                 static_cast<ssize_t>(line.size()));
101 }
102 }
103
104 void Logger::WriteAndDeleteLogLine(string *line) {
105   LOGGER_ASSERT(line != NULL);
106   if (log_fd_ >= 0) {
107     WriteToFile(*line, log_fd_);
108   }
109   WriteToFile(*line, 1);
110   delete line;
111 }
112
113 void *Logger::StartRoutine(void *ptr) {
114   Logger *self = static_cast<Logger*>(ptr);
115   self->ThreadMain();
116   return NULL;
117 }
118
119 void Logger::ThreadMain() {
120   vector<string*> local_queue;
121   LOGGER_ASSERT(0 == pthread_mutex_lock(&queued_lines_mutex_));
122
123   for (;;) {
124     if (queued_lines_.empty()) {
125       LOGGER_ASSERT(0 == pthread_cond_wait(&queued_lines_cond_,
126                                            &queued_lines_mutex_));
127       continue;
128     }
129
130     // We move the log lines into a local queue so we can release the lock
131     // while writing them to disk, preventing other threads from blocking on
132     // our writes.
133     local_queue.swap(queued_lines_);
134     if (local_queue.size() >= kMaxQueueSize) {
135       LOGGER_ASSERT(0 == pthread_cond_broadcast(&full_queue_cond_));
136     }
137
138     // Unlock while we process our local queue.
139     LOGGER_ASSERT(0 == pthread_mutex_unlock(&queued_lines_mutex_));
140     for (vector<string*>::const_iterator it = local_queue.begin();
141          it != local_queue.end(); ++it) {
142       if (*it == NULL) {
143         // NULL is guaranteed to be at the end.
144         return;
145       }
146       WriteAndDeleteLogLine(*it);
147     }
148     local_queue.clear();
149     // We must hold the lock at the start of each iteration of this for loop.
150     LOGGER_ASSERT(0 == pthread_mutex_lock(&queued_lines_mutex_));
151   }
152 }