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