Line data Source code
1 : /*
2 : * Copyright © 2016 Mozilla Foundation
3 : *
4 : * This program is made available under an ISC-style license. See the
5 : * accompanying file LICENSE for details.
6 : */
7 : #define NOMINMAX
8 :
9 : #include "cubeb_log.h"
10 : #include "cubeb_ringbuffer.h"
11 : #include <cstdarg>
12 : #ifdef _WIN32
13 : #include <windows.h>
14 : #else
15 : #include <time.h>
16 : #endif
17 :
18 : cubeb_log_level g_cubeb_log_level;
19 : cubeb_log_callback g_cubeb_log_callback;
20 :
21 : /** The maximum size of a log message, after having been formatted. */
22 : const size_t CUBEB_LOG_MESSAGE_MAX_SIZE = 256;
23 : /** The maximum number of log messages that can be queued before dropping
24 : * messages. */
25 : const size_t CUBEB_LOG_MESSAGE_QUEUE_DEPTH = 40;
26 : /** Number of milliseconds to wait before dequeuing log messages. */
27 : #define CUBEB_LOG_BATCH_PRINT_INTERVAL_MS 10
28 :
29 : /**
30 : * This wraps an inline buffer, that represents a log message, that must be
31 : * null-terminated.
32 : * This class should not use system calls or other potentially blocking code.
33 : */
34 : class cubeb_log_message
35 : {
36 : public:
37 0 : cubeb_log_message()
38 0 : {
39 0 : *storage = '\0';
40 0 : }
41 0 : cubeb_log_message(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE])
42 0 : {
43 0 : size_t length = strlen(str);
44 : /* paranoia against malformed message */
45 0 : assert(length < CUBEB_LOG_MESSAGE_MAX_SIZE);
46 0 : if (length > CUBEB_LOG_MESSAGE_MAX_SIZE - 1) {
47 0 : return;
48 : }
49 0 : PodCopy(storage, str, length);
50 0 : storage[length] = '\0';
51 : }
52 0 : char const * get() {
53 0 : return storage;
54 : }
55 : private:
56 : char storage[CUBEB_LOG_MESSAGE_MAX_SIZE];
57 : };
58 :
59 : /** Lock-free asynchronous logger, made so that logging from a
60 : * real-time audio callback does not block the audio thread. */
61 0 : class cubeb_async_logger
62 : {
63 : public:
64 : /* This is thread-safe since C++11 */
65 0 : static cubeb_async_logger & get() {
66 0 : static cubeb_async_logger instance;
67 0 : return instance;
68 : }
69 0 : void push(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE])
70 : {
71 0 : cubeb_log_message msg(str);
72 0 : msg_queue.enqueue(msg);
73 0 : }
74 0 : void run()
75 : {
76 0 : std::thread([this]() {
77 : while (true) {
78 0 : cubeb_log_message msg;
79 0 : while (msg_queue.dequeue(&msg, 1)) {
80 0 : LOGV("%s", msg.get());
81 : }
82 : #ifdef _WIN32
83 : Sleep(CUBEB_LOG_BATCH_PRINT_INTERVAL_MS);
84 : #else
85 0 : timespec sleep_duration = sleep_for;
86 : timespec remainder;
87 0 : do {
88 0 : if (nanosleep(&sleep_duration, &remainder) == 0 ||
89 0 : errno != EINTR) {
90 0 : break;
91 : }
92 0 : sleep_duration = remainder;
93 0 : } while (remainder.tv_sec || remainder.tv_nsec);
94 : #endif
95 0 : }
96 0 : }).detach();
97 0 : }
98 : private:
99 : #ifndef _WIN32
100 : const struct timespec sleep_for = {
101 : CUBEB_LOG_BATCH_PRINT_INTERVAL_MS/1000,
102 : (CUBEB_LOG_BATCH_PRINT_INTERVAL_MS%1000)*1000*1000
103 : };
104 : #endif
105 0 : cubeb_async_logger()
106 0 : : msg_queue(CUBEB_LOG_MESSAGE_QUEUE_DEPTH)
107 : {
108 0 : run();
109 0 : }
110 : /** This is quite a big data structure, but is only instantiated if the
111 : * asynchronous logger is used.*/
112 : lock_free_queue<cubeb_log_message> msg_queue;
113 : };
114 :
115 :
116 0 : void cubeb_async_log(char const * fmt, ...)
117 : {
118 0 : if (!g_cubeb_log_callback) {
119 0 : return;
120 : }
121 : // This is going to copy a 256 bytes array around, which is fine.
122 : // We don't want to allocate memory here, because this is made to
123 : // be called from a real-time callback.
124 : va_list args;
125 0 : va_start(args, fmt);
126 : char msg[CUBEB_LOG_MESSAGE_MAX_SIZE];
127 0 : vsnprintf(msg, CUBEB_LOG_MESSAGE_MAX_SIZE, fmt, args);
128 0 : cubeb_async_logger::get().push(msg);
129 0 : va_end(args);
130 : }
|