StreamTee.h revision 355940
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
18namespace lldb_private {
19
20class StreamTee : public Stream {
21public:
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
109protected:
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