1 //===-- StreamTee.h ------------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef LLDB_UTILITY_STREAMTEE_H 10 #define LLDB_UTILITY_STREAMTEE_H 11 12 #include <limits.h> 13 14 #include <mutex> 15 16 #include "lldb/Utility/Stream.h" 17 18 namespace lldb_private { 19 20 class StreamTee : public Stream { 21 public: 22 StreamTee(bool colors = false) Stream(colors)23 : Stream(colors), m_streams_mutex(), m_streams() {} 24 StreamTee(lldb::StreamSP & stream_sp)25 StreamTee(lldb::StreamSP &stream_sp) 26 : Stream(), m_streams_mutex(), m_streams() { 27 // No need to lock mutex during construction 28 if (stream_sp) 29 m_streams.push_back(stream_sp); 30 } 31 StreamTee(lldb::StreamSP & stream_sp,lldb::StreamSP & stream_2_sp)32 StreamTee(lldb::StreamSP &stream_sp, lldb::StreamSP &stream_2_sp) 33 : Stream(), m_streams_mutex(), m_streams() { 34 // No need to lock mutex during construction 35 if (stream_sp) 36 m_streams.push_back(stream_sp); 37 if (stream_2_sp) 38 m_streams.push_back(stream_2_sp); 39 } 40 StreamTee(const StreamTee & rhs)41 StreamTee(const StreamTee &rhs) 42 : Stream(rhs), m_streams_mutex(), m_streams() { 43 // Don't copy until we lock down "rhs" 44 std::lock_guard<std::recursive_mutex> guard(rhs.m_streams_mutex); 45 m_streams = rhs.m_streams; 46 } 47 ~StreamTee()48 ~StreamTee() override {} 49 50 StreamTee &operator=(const StreamTee &rhs) { 51 if (this != &rhs) { 52 Stream::operator=(rhs); 53 std::lock(m_streams_mutex, rhs.m_streams_mutex); 54 std::lock_guard<std::recursive_mutex> lhs_locker(m_streams_mutex, 55 std::adopt_lock); 56 std::lock_guard<std::recursive_mutex> rhs_locker(rhs.m_streams_mutex, 57 std::adopt_lock); 58 m_streams = rhs.m_streams; 59 } 60 return *this; 61 } 62 Flush()63 void Flush() override { 64 std::lock_guard<std::recursive_mutex> guard(m_streams_mutex); 65 collection::iterator pos, end; 66 for (pos = m_streams.begin(), end = m_streams.end(); pos != end; ++pos) { 67 // Allow for our collection to contain NULL streams. This allows the 68 // StreamTee to be used with hard coded indexes for clients that might 69 // want N total streams with only a few that are set to valid values. 70 Stream *strm = pos->get(); 71 if (strm) 72 strm->Flush(); 73 } 74 } 75 AppendStream(const lldb::StreamSP & stream_sp)76 size_t AppendStream(const lldb::StreamSP &stream_sp) { 77 size_t new_idx = m_streams.size(); 78 std::lock_guard<std::recursive_mutex> guard(m_streams_mutex); 79 m_streams.push_back(stream_sp); 80 return new_idx; 81 } 82 GetNumStreams()83 size_t GetNumStreams() const { 84 size_t result = 0; 85 { 86 std::lock_guard<std::recursive_mutex> guard(m_streams_mutex); 87 result = m_streams.size(); 88 } 89 return result; 90 } 91 GetStreamAtIndex(uint32_t idx)92 lldb::StreamSP GetStreamAtIndex(uint32_t idx) { 93 lldb::StreamSP stream_sp; 94 std::lock_guard<std::recursive_mutex> guard(m_streams_mutex); 95 if (idx < m_streams.size()) 96 stream_sp = m_streams[idx]; 97 return stream_sp; 98 } 99 SetStreamAtIndex(uint32_t idx,const lldb::StreamSP & stream_sp)100 void SetStreamAtIndex(uint32_t idx, const lldb::StreamSP &stream_sp) { 101 std::lock_guard<std::recursive_mutex> guard(m_streams_mutex); 102 // Resize our stream vector as necessary to fit as many streams as needed. 103 // This also allows this class to be used with hard coded indexes that can 104 // be used contain many streams, not all of which are valid. 105 if (idx >= m_streams.size()) 106 m_streams.resize(idx + 1); 107 m_streams[idx] = stream_sp; 108 } 109 110 protected: 111 typedef std::vector<lldb::StreamSP> collection; 112 mutable std::recursive_mutex m_streams_mutex; 113 collection m_streams; 114 WriteImpl(const void * s,size_t length)115 size_t WriteImpl(const void *s, size_t length) override { 116 std::lock_guard<std::recursive_mutex> guard(m_streams_mutex); 117 if (m_streams.empty()) 118 return 0; 119 120 size_t min_bytes_written = SIZE_MAX; 121 collection::iterator pos, end; 122 for (pos = m_streams.begin(), end = m_streams.end(); pos != end; ++pos) { 123 // Allow for our collection to contain NULL streams. This allows the 124 // StreamTee to be used with hard coded indexes for clients that might 125 // want N total streams with only a few that are set to valid values. 126 Stream *strm = pos->get(); 127 if (strm) { 128 const size_t bytes_written = strm->Write(s, length); 129 if (min_bytes_written > bytes_written) 130 min_bytes_written = bytes_written; 131 } 132 } 133 if (min_bytes_written == SIZE_MAX) 134 return 0; 135 return min_bytes_written; 136 } 137 }; 138 139 } // namespace lldb_private 140 141 #endif // LLDB_UTILITY_STREAMTEE_H 142