Files
filament/libs/utils/src/ostream.cpp
Mathias Agopian 047543a4a6 make our ostream thread safe
ostream internals are now protected by a lock, which ensures the
internal state stays consistant, however, this won't prevent 
multiple threads to have their output ominterfering with each other,
which is no different from the stl behavior.


This fixes #4992
2022-01-10 15:04:26 -08:00

220 lines
5.9 KiB
C++

/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <utils/ostream.h>
#include <utils/compiler.h>
#include <algorithm>
#include <stdarg.h>
#include <string>
namespace utils::io {
ostream::~ostream() = default;
// don't allocate any memory before we actually use the log because one of these is created
// per thread.
ostream::Buffer::Buffer() noexcept = default;
ostream::Buffer::~Buffer() noexcept {
// note: on Android pre r14, thread_local destructors are not called
free(buffer);
}
void ostream::Buffer::advance(ssize_t n) noexcept {
if (n > 0) {
size_t written = n < size ? size_t(n) : size;
curr += written;
size -= written;
}
}
void ostream::Buffer::reserve(size_t newSize) noexcept {
size_t offset = curr - buffer;
if (buffer == nullptr) {
buffer = (char*)malloc(newSize);
} else {
buffer = (char*)realloc(buffer, newSize);
}
assert(buffer);
capacity = newSize;
curr = buffer + offset;
size = capacity - offset;
}
void ostream::Buffer::reset() noexcept {
// aggressively shrink the buffer
if (capacity > 1024) {
free(buffer);
buffer = (char*)malloc(1024);
capacity = 1024;
}
curr = buffer;
size = capacity;
}
std::pair<char*, size_t> ostream::Buffer::grow(size_t s) noexcept {
if (UTILS_UNLIKELY(size < s)) {
size_t used = curr - buffer;
size_t newCapacity = std::max(size_t(32), used + (s * 3 + 1) / 2); // 32 bytes minimum
reserve(newCapacity);
assert(size >= s);
}
return { curr, size };
}
const char* ostream::getFormat(ostream::type t) const noexcept {
switch (t) {
case type::SHORT: return mShowHex ? "0x%hx" : "%hd";
case type::USHORT: return mShowHex ? "0x%hx" : "%hu";
case type::CHAR: return "%c";
case type::UCHAR: return "%c";
case type::INT: return mShowHex ? "0x%x" : "%d";
case type::UINT: return mShowHex ? "0x%x" : "%u";
case type::LONG: return mShowHex ? "0x%lx" : "%ld";
case type::ULONG: return mShowHex ? "0x%lx" : "%lu";
case type::LONG_LONG: return mShowHex ? "0x%llx" : "%lld";
case type::ULONG_LONG: return mShowHex ? "0x%llx" : "%llu";
case type::DOUBLE: return "%f";
case type::LONG_DOUBLE: return "%Lf";
}
}
UTILS_NOINLINE
ostream& ostream::print(const char* format, ...) noexcept {
va_list args0;
va_list args1;
// figure out how much size to we need
va_start(args0, format);
va_copy(args1, args0);
ssize_t s = vsnprintf(nullptr, 0, format, args0);
va_end(args0);
{ // scope for the lock
std::lock_guard lock(mLock);
Buffer& buf = getBuffer();
// grow the buffer to the needed size
auto[curr, size] = buf.grow(s + 1); // +1 to include the null-terminator
// print into the buffer
vsnprintf(curr, size, format, args1);
// advance the buffer
buf.advance(s);
}
va_end(args1);
return *this;
}
ostream& ostream::operator<<(short value) noexcept {
const char* format = getFormat(type::SHORT);
return print(format, value);
}
ostream& ostream::operator<<(unsigned short value) noexcept {
const char* format = getFormat(type::USHORT);
return print(format, value);
}
ostream& ostream::operator<<(char value) noexcept {
const char* format = getFormat(type::CHAR);
return print(format, value);
}
ostream& ostream::operator<<(unsigned char value) noexcept {
const char* format = getFormat(type::UCHAR);
return print(format, value);
}
ostream& ostream::operator<<(int value) noexcept {
const char* format = getFormat(type::INT);
return print(format, value);
}
ostream& ostream::operator<<(unsigned int value) noexcept {
const char* format = getFormat(type::UINT);
return print(format, value);
}
ostream& ostream::operator<<(long value) noexcept {
const char* format = getFormat(type::LONG);
return print(format, value);
}
ostream& ostream::operator<<(unsigned long value) noexcept {
const char* format = getFormat(type::ULONG);
return print(format, value);
}
ostream& ostream::operator<<(long long value) noexcept {
const char* format = getFormat(type::LONG_LONG);
return print(format, value);
}
ostream& ostream::operator<<(unsigned long long value) noexcept {
const char* format = getFormat(type::ULONG_LONG);
return print(format, value);
}
ostream& ostream::operator<<(float value) noexcept {
return operator<<((double)value);
}
ostream& ostream::operator<<(double value) noexcept {
const char* format = getFormat(type::DOUBLE);
return print(format, value);
}
ostream& ostream::operator<<(long double value) noexcept {
const char* format = getFormat(type::LONG_DOUBLE);
return print(format, value);
}
ostream& ostream::operator<<(bool value) noexcept {
return operator<<(value ? "true" : "false");
}
ostream& ostream::operator<<(const char* string) noexcept {
return print("%s", string);
}
ostream& ostream::operator<<(const unsigned char* string) noexcept {
return print("%s", string);
}
ostream& ostream::operator<<(const void* value) noexcept {
return print("%p", value);
}
ostream& ostream::hex() noexcept {
mShowHex = true;
return *this;
}
ostream& ostream::dec() noexcept {
mShowHex = false;
return *this;
}
} // namespace utils::io