/[pcsx2_0.9.7]/branch/debug/0.X/0.9.X/0.9.7/ramdump-lateset/pcsx2/windows/WinConsolePipe.cpp
ViewVC logotype

Contents of /branch/debug/0.X/0.9.X/0.9.7/ramdump-lateset/pcsx2/windows/WinConsolePipe.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 415 - (show annotations) (download)
Sun Feb 27 08:25:06 2011 UTC (9 years, 7 months ago) by william
File size: 8804 byte(s)
fix issues from upstream merge of r4239-r4370
1 /* PCSX2 - PS2 Emulator for PCs
2 * Copyright (C) 2002-2010 PCSX2 Dev Team
3 *
4 * PCSX2 is free software: you can redistribute it and/or modify it under the terms
5 * of the GNU Lesser General Public License as published by the Free Software Found-
6 * ation, either version 3 of the License, or (at your option) any later version.
7 *
8 * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10 * PURPOSE. See the GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License along with PCSX2.
13 * If not, see <http://www.gnu.org/licenses/>.
14 */
15
16 #include "PrecompiledHeader.h"
17 #include "Win32.h"
18
19 #include "App.h"
20 #include "ConsoleLogger.h"
21
22 // --------------------------------------------------------------------------------------
23 // Win32 Console Pipes
24 // As a courtesy and convenience, we redirect stdout/stderr to the console and logfile.
25 // --------------------------------------------------------------------------------------
26
27 using namespace Threading;
28
29 // --------------------------------------------------------------------------------------
30 // WinPipeThread
31 // --------------------------------------------------------------------------------------
32 class WinPipeThread : public pxThread
33 {
34 typedef pxThread _parent;
35
36 protected:
37 const HANDLE& m_outpipe;
38 const ConsoleColors m_color;
39
40 public:
41 WinPipeThread( const HANDLE& outpipe, ConsoleColors color )
42 : m_outpipe( outpipe )
43 , m_color( color )
44 {
45 m_name = (m_color == Color_Red) ? L"Redirect_Stderr" : L"Redirect_Stdout";
46 }
47
48 virtual ~WinPipeThread() throw()
49 {
50 _parent::Cancel();
51 }
52
53 protected:
54 void ExecuteTaskInThread()
55 {
56 ::SetThreadPriority( ::GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL );
57 if( m_outpipe == INVALID_HANDLE_VALUE ) return;
58
59 try
60 {
61 char s8_Buf[2049];
62 DWORD u32_Read = 0;
63
64 while( true )
65 {
66 if( !ReadFile(m_outpipe, s8_Buf, sizeof(s8_Buf)-1, &u32_Read, NULL) )
67 {
68 DWORD result = GetLastError();
69 if( result == ERROR_HANDLE_EOF || result == ERROR_BROKEN_PIPE ) break;
70 if( result == ERROR_IO_PENDING )
71 {
72 Yield( 10 );
73 continue;
74 }
75
76 throw Exception::WinApiError().SetDiagMsg(L"ReadFile from pipe failed.");
77 }
78
79 if( u32_Read <= 3 )
80 {
81 // Windows has a habit of sending 1 or 2 characters of every message, and then sending
82 // the rest in a second message. This is "ok" really, except our Console class is hardly
83 // free of overhead, so it's helpful if we can concatenate the couple of messages together.
84 // But we don't want to break the ability to print progressive status bars, like '....'
85 // so I use a clever Yield/Peek loop combo that keeps reading as long as there's new data
86 // immediately being fed to our pipe. :) --air
87
88 DWORD u32_avail = 0;
89
90 do
91 {
92 Yield();
93 if( !PeekNamedPipe(m_outpipe, 0, 0, 0, &u32_avail, 0) )
94 throw Exception::WinApiError().SetDiagMsg(L"Error peeking Pipe.");
95
96 if( u32_avail == 0 ) break;
97
98 DWORD loopread;
99 if( !ReadFile(m_outpipe, &s8_Buf[u32_Read], sizeof(s8_Buf)-u32_Read-1, &loopread, NULL) ) break;
100 u32_Read += loopread;
101
102 } while( u32_Read < sizeof(s8_Buf)-32 );
103 }
104
105 // ATTENTION: The Console always prints ANSI to the pipe independent if compiled as UNICODE or MBCS!
106 s8_Buf[u32_Read] = 0;
107
108 ConsoleColorScope cs(m_color);
109 Console.DoWriteFromStdout( fromUTF8(s8_Buf) );
110
111 TestCancel();
112 }
113 }
114 catch( Exception::RuntimeError& ex )
115 {
116 // Log error, and fail silently. It's not really important if the
117 // pipe fails. PCSX2 will run fine without it in any case.
118 Console.Error( ex.FormatDiagnosticMessage() );
119 }
120 }
121 };
122
123 // --------------------------------------------------------------------------------------
124 // WinPipeRedirection
125 // --------------------------------------------------------------------------------------
126 class WinPipeRedirection : public PipeRedirectionBase
127 {
128 DeclareNoncopyableObject( WinPipeRedirection );
129
130 protected:
131 DWORD m_stdhandle;
132 FILE* m_stdfp;
133 FILE m_stdfp_copy;
134
135 HANDLE m_readpipe;
136 HANDLE m_writepipe;
137 int m_crtFile;
138 FILE* m_fp;
139
140 WinPipeThread m_Thread;
141
142 public:
143 WinPipeRedirection( FILE* stdstream );
144 virtual ~WinPipeRedirection() throw();
145
146 void Cleanup() throw();
147 };
148
149 WinPipeRedirection::WinPipeRedirection( FILE* stdstream )
150 : m_Thread( m_readpipe, (stdstream == stderr) ? Color_Red : Color_Black )
151 {
152 m_stdhandle = ( stdstream == stderr ) ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE;
153 m_stdfp = stdstream;
154 m_stdfp_copy = *stdstream;
155
156 m_readpipe = INVALID_HANDLE_VALUE;
157 m_writepipe = INVALID_HANDLE_VALUE;
158 m_crtFile = -1;
159 m_fp = NULL;
160
161 pxAssume( (stdstream == stderr) || (stdstream == stdout) );
162
163 try
164 {
165 if( 0 == CreatePipe( &m_readpipe, &m_writepipe, NULL, 0 ) )
166 throw Exception::WinApiError().SetDiagMsg(L"CreatePipe failed.");
167
168 if( 0 == SetStdHandle( m_stdhandle, m_writepipe ) )
169 throw Exception::WinApiError().SetDiagMsg(L"SetStdHandle failed.");
170
171 // Note: Don't use GetStdHandle to "confirm" the handle.
172 //
173 // Under Windows7, and possibly Vista, GetStdHandle for STDOUT will return NULL
174 // after it's been assigned a custom write pipe (this differs from XP, which
175 // returns the assigned handle). Amusingly, the GetStdHandle succeeds for STDERR
176 // and also tends to succeed when the app is run from the MSVC debugger.
177 //
178 // Fortunately, there's no need to use GetStdHandle anyway, so long as SetStdHandle
179 // didn't error.
180
181 m_crtFile = _open_osfhandle( (intptr_t)m_writepipe, _O_TEXT );
182 if( m_crtFile == -1 )
183 throw Exception::RuntimeError().SetDiagMsg( L"_open_osfhandle returned -1." );
184
185 m_fp = _fdopen( m_crtFile, "w" );
186 if( m_fp == NULL )
187 throw Exception::RuntimeError().SetDiagMsg( L"_fdopen returned NULL." );
188
189 *m_stdfp = *m_fp; // omg hack. but it works >_<
190 setvbuf( stdstream, NULL, _IONBF, 0 );
191
192 m_Thread.Start();
193 }
194 catch( Exception::BaseThreadError& ex )
195 {
196 // thread object will become invalid because of scoping after we leave
197 // the constructor, so re-pack a new exception:
198
199 Cleanup();
200 throw Exception::RuntimeError().SetDiagMsg( ex.FormatDiagnosticMessage() ).SetUserMsg( ex.FormatDisplayMessage() );
201 }
202 catch( BaseException& ex )
203 {
204 Cleanup();
205 ex.DiagMsg() = (wxString)((stdstream==stdout) ? L"STDOUT" : L"STDERR") + L" Redirection Init failed: " + ex.DiagMsg();
206 throw;
207 }
208 catch( ... )
209 {
210 // C++ doesn't execute the object destructor automatically, because it's fail++
211 // (and I'm *not* encapsulating each handle into its own object >_<)
212
213 Cleanup();
214 throw;
215 }
216 }
217
218 WinPipeRedirection::~WinPipeRedirection()
219 {
220 Cleanup();
221 }
222
223 void WinPipeRedirection::Cleanup() throw()
224 {
225 // restore the old handle we so graciously hacked earlier ;)
226 // (or don't and suffer CRT crashes! ahaha!)
227
228 if( m_stdfp != NULL )
229 *m_stdfp = m_stdfp_copy;
230
231 // Cleanup Order Notes:
232 // * The redirection thread is most likely blocking on ReadFile(), so we can't Cancel yet, lest we deadlock --
233 // Closing the writepipe (either directly or through the fp/crt handles) issues an EOF to the thread,
234 // so it's safe to Cancel afterward.
235 //
236 // * The seemingly redundant series of checks here are designed to handle cases where the pipe init fails
237 // mid-init (in which case the writepipe might be allocated while the fp/crtFile are still invalid, etc).
238
239 if( m_fp != NULL )
240 {
241 fclose( m_fp );
242 m_fp = NULL;
243
244 m_crtFile = -1; // crtFile is closed implicitly when closing m_fp
245 m_writepipe = INVALID_HANDLE_VALUE; // same for the write end of the pipe
246 }
247
248 if( m_crtFile != -1 )
249 {
250 _close( m_crtFile );
251 m_crtFile = -1; // m_file is closed implicitly when closing crtFile
252 m_writepipe = INVALID_HANDLE_VALUE; // same for the write end of the pipe (I assume)
253 }
254
255 if( m_writepipe != INVALID_HANDLE_VALUE )
256 {
257 CloseHandle( m_writepipe );
258 m_writepipe = INVALID_HANDLE_VALUE;
259 }
260
261 m_Thread.Cancel();
262
263 if( m_readpipe != INVALID_HANDLE_VALUE )
264 {
265 CloseHandle( m_readpipe );
266 m_readpipe = INVALID_HANDLE_VALUE;
267 }
268 }
269
270 // The win32 specific implementation of PipeRedirection.
271 PipeRedirectionBase* NewPipeRedir( FILE* stdstream )
272 {
273 try
274 {
275 return new WinPipeRedirection( stdstream );
276 }
277 catch( Exception::RuntimeError& ex )
278 {
279 // Entirely non-critical errors. Log 'em and move along.
280 Console.Error( ex.FormatDiagnosticMessage() );
281 }
282
283 return NULL;
284 }

  ViewVC Help
Powered by ViewVC 1.1.22