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
18namespace lldb_private {
19
20class StreamTee : public Stream {
21public:
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
106protected:
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