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