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