/src/wxwidgets/include/wx/private/streamtempinput.h
Line | Count | Source |
1 | | /////////////////////////////////////////////////////////////////////////////// |
2 | | // Name: wx/private/streamtempinput.h |
3 | | // Purpose: defines wxStreamTempInputBuffer which is used by Unix and MSW |
4 | | // implementations of wxExecute; this file is only used by the |
5 | | // library and never by the user code |
6 | | // Author: Vadim Zeitlin |
7 | | // Modified by: Rob Bresalier |
8 | | // Created: 2013-05-04 |
9 | | // Copyright: (c) 2002 Vadim Zeitlin <vadim@wxwidgets.org> |
10 | | // Licence: wxWindows licence |
11 | | /////////////////////////////////////////////////////////////////////////////// |
12 | | |
13 | | #ifndef _WX_PRIVATE_STREAMTEMPINPUT_H |
14 | | #define _WX_PRIVATE_STREAMTEMPINPUT_H |
15 | | |
16 | | #include "wx/private/pipestream.h" |
17 | | |
18 | | // ---------------------------------------------------------------------------- |
19 | | // wxStreamTempInputBuffer |
20 | | // ---------------------------------------------------------------------------- |
21 | | |
22 | | /* |
23 | | wxStreamTempInputBuffer is a hack which we need to solve the problem of |
24 | | executing a child process synchronously with IO redirecting: when we do |
25 | | this, the child writes to a pipe we open to it but when the pipe buffer |
26 | | (which has finite capacity, e.g. commonly just 4Kb) becomes full we have to |
27 | | read data from it because the child blocks in its write() until then and if |
28 | | it blocks we are never going to return from wxExecute() so we dead lock. |
29 | | |
30 | | So here is the fix: we now read the output as soon as it appears into a temp |
31 | | buffer (wxStreamTempInputBuffer object) and later just stuff it back into |
32 | | the stream when the process terminates. See supporting code in wxExecute() |
33 | | itself as well. |
34 | | |
35 | | Note that this is horribly inefficient for large amounts of output (count |
36 | | the number of times we copy the data around) and so a better API is badly |
37 | | needed! However it's not easy to devise a way to do this keeping backwards |
38 | | compatibility with the existing wxExecute(wxEXEC_SYNC)... |
39 | | */ |
40 | | class wxStreamTempInputBuffer |
41 | | { |
42 | | public: |
43 | 0 | wxStreamTempInputBuffer() = default; |
44 | | |
45 | | // call to associate a stream with this buffer, otherwise nothing happens |
46 | | // at all |
47 | | void Init(wxPipeInputStream *stream) |
48 | 0 | { |
49 | 0 | wxASSERT_MSG( !m_stream, wxS("Can only initialize once") ); |
50 | |
|
51 | 0 | m_stream = stream; |
52 | 0 | } |
53 | | |
54 | | // check for input on our stream and cache it in our buffer if any |
55 | | // |
56 | | // return true if anything was done |
57 | | bool Update() |
58 | 0 | { |
59 | 0 | if ( !m_stream || !m_stream->CanRead() ) |
60 | 0 | return false; |
61 | | |
62 | | // realloc in blocks of 4Kb: this is the default (and minimal) buffer |
63 | | // size of the Unix pipes so it should be the optimal step |
64 | | // |
65 | | // NB: don't use "static int" in this inline function, some compilers |
66 | | // (e.g. IBM xlC) don't like it |
67 | 0 | enum { incSize = 4096 }; |
68 | |
|
69 | 0 | void *buf = realloc(m_buffer, m_size + incSize); |
70 | 0 | if ( !buf ) |
71 | 0 | return false; |
72 | | |
73 | 0 | m_buffer = buf; |
74 | 0 | m_stream->Read((char *)m_buffer + m_size, incSize); |
75 | 0 | m_size += m_stream->LastRead(); |
76 | |
|
77 | 0 | return true; |
78 | 0 | } |
79 | | |
80 | | // check if can continue reading from the stream, this is used to disable |
81 | | // the callback once we can't read anything more |
82 | | bool Eof() const |
83 | 0 | { |
84 | | // If we have no stream, always return true as we can't read any more. |
85 | 0 | return !m_stream || m_stream->Eof(); |
86 | 0 | } |
87 | | |
88 | | // read everything remaining until the EOF, this should only be called once |
89 | | // the child process terminates and we know that no more data is coming |
90 | | bool ReadAll() |
91 | 0 | { |
92 | 0 | while ( !Eof() ) |
93 | 0 | { |
94 | 0 | if ( !Update() ) |
95 | 0 | return false; |
96 | 0 | } |
97 | | |
98 | 0 | return true; |
99 | 0 | } |
100 | | |
101 | | // dtor puts the data buffered during this object lifetime into the |
102 | | // associated stream |
103 | | ~wxStreamTempInputBuffer() |
104 | 0 | { |
105 | 0 | if ( m_buffer ) |
106 | 0 | { |
107 | 0 | m_stream->Ungetch(m_buffer, m_size); |
108 | 0 | free(m_buffer); |
109 | 0 | } |
110 | 0 | } |
111 | | |
112 | 0 | const void *GetBuffer() const { return m_buffer; } |
113 | | |
114 | 0 | size_t GetSize() const { return m_size; } |
115 | | |
116 | | private: |
117 | | // the stream we're buffering, if nullptr we don't do anything at all |
118 | | wxPipeInputStream *m_stream = nullptr; |
119 | | |
120 | | // the buffer of size m_size (nullptr if m_size == 0) |
121 | | void *m_buffer = nullptr; |
122 | | |
123 | | // the size of the buffer |
124 | | size_t m_size = 0; |
125 | | |
126 | | wxDECLARE_NO_COPY_CLASS(wxStreamTempInputBuffer); |
127 | | }; |
128 | | |
129 | | #endif // _WX_PRIVATE_STREAMTEMPINPUT_H |