diff options
Diffstat (limited to 'media/libcubeb/src/cubeb_log.cpp')
-rw-r--r-- | media/libcubeb/src/cubeb_log.cpp | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/media/libcubeb/src/cubeb_log.cpp b/media/libcubeb/src/cubeb_log.cpp new file mode 100644 index 0000000000..ff72e0e87a --- /dev/null +++ b/media/libcubeb/src/cubeb_log.cpp @@ -0,0 +1,132 @@ +/* + * Copyright © 2016 Mozilla Foundation + * + * This program is made available under an ISC-style license. See the + * accompanying file LICENSE for details. + */ +#define NOMINMAX + +#include "cubeb_log.h" +#include "cubeb_ringbuffer.h" +#include <cstdarg> +#ifdef _WIN32 +#include <windows.h> +#else +#include <time.h> +#endif + +cubeb_log_level g_cubeb_log_level; +cubeb_log_callback g_cubeb_log_callback; + +/** The maximum size of a log message, after having been formatted. */ +const size_t CUBEB_LOG_MESSAGE_MAX_SIZE = 256; +/** The maximum number of log messages that can be queued before dropping + * messages. */ +const size_t CUBEB_LOG_MESSAGE_QUEUE_DEPTH = 40; +/** Number of milliseconds to wait before dequeuing log messages. */ +#define CUBEB_LOG_BATCH_PRINT_INTERVAL_MS 10 + +/** + * This wraps an inline buffer, that represents a log message, that must be + * null-terminated. + * This class should not use system calls or other potentially blocking code. + */ +class cubeb_log_message { +public: + cubeb_log_message() { *storage = '\0'; } + cubeb_log_message(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE]) + { + size_t length = strlen(str); + /* paranoia against malformed message */ + assert(length < CUBEB_LOG_MESSAGE_MAX_SIZE); + if (length > CUBEB_LOG_MESSAGE_MAX_SIZE - 1) { + return; + } + PodCopy(storage, str, length); + storage[length] = '\0'; + } + char const * get() { return storage; } + +private: + char storage[CUBEB_LOG_MESSAGE_MAX_SIZE]; +}; + +/** Lock-free asynchronous logger, made so that logging from a + * real-time audio callback does not block the audio thread. */ +class cubeb_async_logger { +public: + /* This is thread-safe since C++11 */ + static cubeb_async_logger & get() + { + static cubeb_async_logger instance; + return instance; + } + void push(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE]) + { + cubeb_log_message msg(str); + msg_queue.enqueue(msg); + } + void run() + { + std::thread([this]() { + while (true) { + cubeb_log_message msg; + while (msg_queue.dequeue(&msg, 1)) { + LOGV("%s", msg.get()); + } +#ifdef _WIN32 + Sleep(CUBEB_LOG_BATCH_PRINT_INTERVAL_MS); +#else + timespec sleep_duration = sleep_for; + timespec remainder; + do { + if (nanosleep(&sleep_duration, &remainder) == 0 || errno != EINTR) { + break; + } + sleep_duration = remainder; + } while (remainder.tv_sec || remainder.tv_nsec); +#endif + } + }).detach(); + } + // Tell the underlying queue the producer thread has changed, so it does not + // assert in debug. This should be called with the thread stopped. + void reset_producer_thread() { msg_queue.reset_thread_ids(); } + +private: +#ifndef _WIN32 + const struct timespec sleep_for = { + CUBEB_LOG_BATCH_PRINT_INTERVAL_MS / 1000, + (CUBEB_LOG_BATCH_PRINT_INTERVAL_MS % 1000) * 1000 * 1000}; +#endif + cubeb_async_logger() : msg_queue(CUBEB_LOG_MESSAGE_QUEUE_DEPTH) { run(); } + /** This is quite a big data structure, but is only instantiated if the + * asynchronous logger is used.*/ + lock_free_queue<cubeb_log_message> msg_queue; +}; + +void +cubeb_async_log(char const * fmt, ...) +{ + if (!g_cubeb_log_callback) { + return; + } + // This is going to copy a 256 bytes array around, which is fine. + // We don't want to allocate memory here, because this is made to + // be called from a real-time callback. + va_list args; + va_start(args, fmt); + char msg[CUBEB_LOG_MESSAGE_MAX_SIZE]; + vsnprintf(msg, CUBEB_LOG_MESSAGE_MAX_SIZE, fmt, args); + cubeb_async_logger::get().push(msg); + va_end(args); +} + +void +cubeb_async_log_reset_threads() +{ + if (!g_cubeb_log_callback) { + return; + } + cubeb_async_logger::get().reset_producer_thread(); +} |