//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #include #include "cmdsink.h" #include "subprocess.h" #include "d3dxfxc.h" #include "tools_minidump.h" ////////////////////////////////////////////////////////////////////////// // // Base implementation of the shaderd kernel objects // ////////////////////////////////////////////////////////////////////////// SubProcessKernelObjects::SubProcessKernelObjects( void ) : m_hMemorySection( NULL ), m_hMutex( NULL ) { ZeroMemory( m_hEvent, sizeof( m_hEvent ) ); } SubProcessKernelObjects::~SubProcessKernelObjects( void ) { Close(); } BOOL SubProcessKernelObjects::Create( char const *szBaseName ) { char chBufferName[0x100] = { 0 }; sprintf( chBufferName, "%s_msec", szBaseName ); m_hMemorySection = CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4 * 1024 * 1024, chBufferName ); // 4Mb for a piece if ( NULL != m_hMemorySection ) { if ( ERROR_ALREADY_EXISTS == GetLastError() ) { CloseHandle( m_hMemorySection ); m_hMemorySection = NULL; Assert( 0 && "CreateFileMapping - already exists!\n" ); } } sprintf( chBufferName, "%s_mtx", szBaseName ); m_hMutex = CreateMutex( NULL, FALSE, chBufferName ); for ( int k = 0; k < 2; ++ k ) { sprintf( chBufferName, "%s_evt%d", szBaseName, k ); m_hEvent[k] = CreateEvent( NULL, FALSE, ( k ? TRUE /* = master */ : FALSE ), chBufferName ); } return IsValid(); } BOOL SubProcessKernelObjects::Open( char const *szBaseName ) { char chBufferName[0x100] = { 0 }; sprintf( chBufferName, "%s_msec", szBaseName ); m_hMemorySection = OpenFileMapping( FILE_MAP_ALL_ACCESS, FALSE, chBufferName ); sprintf( chBufferName, "%s_mtx", szBaseName ); m_hMutex = OpenMutex( MUTEX_ALL_ACCESS, FALSE, chBufferName ); for ( int k = 0; k < 2; ++ k ) { sprintf( chBufferName, "%s_evt%d", szBaseName, k ); m_hEvent[k] = OpenEvent( EVENT_ALL_ACCESS, FALSE, chBufferName ); } return IsValid(); } BOOL SubProcessKernelObjects::IsValid( void ) const { return m_hMemorySection && m_hMutex && m_hEvent; } void SubProcessKernelObjects::Close( void ) { if ( m_hMemorySection ) CloseHandle( m_hMemorySection ); if ( m_hMutex ) CloseHandle( m_hMutex ); for ( int k = 0; k < 2; ++ k ) if ( m_hEvent[k] ) CloseHandle( m_hEvent[k] ); } ////////////////////////////////////////////////////////////////////////// // // Helper class to send data back and forth // ////////////////////////////////////////////////////////////////////////// void * SubProcessKernelObjects_Memory::Lock( void ) { // Wait for our turn to act for ( unsigned iWaitAttempt = 0; iWaitAttempt < 13u; ++ iWaitAttempt ) { DWORD dwWait = ::WaitForSingleObject( m_pObjs->m_hEvent[ m_pObjs->m_dwCookie ], 10000 ); switch ( dwWait ) { case WAIT_OBJECT_0: { m_pLockData = MapViewOfFile( m_pObjs->m_hMemorySection, FILE_MAP_ALL_ACCESS, 0, 0, 0 ); if ( * ( const DWORD * ) m_pLockData != m_pObjs->m_dwCookie ) { // Yes, this is our turn, set our cookie in that memory segment * ( DWORD * ) m_pLockData = m_pObjs->m_dwCookie; m_pMemory = ( ( byte * ) m_pLockData ) + 2 * sizeof( DWORD ); return m_pMemory; } else { // We just acted, still waiting for result UnmapViewOfFile( m_pLockData ); m_pLockData = NULL; SetEvent( m_pObjs->m_hEvent[ !m_pObjs->m_dwCookie ] ); Sleep( 1 ); continue; } } break; case WAIT_TIMEOUT: { char chMsg[0x100]; sprintf( chMsg, "th%08X> WAIT_TIMEOUT in Memory::Lock (attempt %d).\n", GetCurrentThreadId(), iWaitAttempt ); OutputDebugString( chMsg ); } continue; // retry default: OutputDebugString( "WAIT failure in Memory::Lock\n" ); SetLastError( ERROR_BAD_UNIT ); return NULL; } } OutputDebugString( "Ran out of wait attempts in Memory::Lock\n" ); SetLastError( ERROR_NOT_READY ); return NULL; } BOOL SubProcessKernelObjects_Memory::Unlock( void ) { if ( m_pLockData ) { // Assert that the memory hasn't been spoiled Assert( m_pObjs->m_dwCookie == * ( const DWORD * ) m_pLockData ); UnmapViewOfFile( m_pLockData ); m_pMemory = NULL; m_pLockData = NULL; SetEvent( m_pObjs->m_hEvent[ !m_pObjs->m_dwCookie ] ); Sleep( 1 ); return TRUE; } return FALSE; } ////////////////////////////////////////////////////////////////////////// // // Implementation of the command subprocess: // // MASTER ---- command -------> SUB // string - zero terminated command string. // // // MASTER <---- result -------- SUB // dword - 1 if succeeded, 0 if failed // dword - result buffer length, 0 if failed // - result buffer data, none if result buffer length is 0 // string - zero-terminated listing string // ////////////////////////////////////////////////////////////////////////// CSubProcessResponse::CSubProcessResponse( void const *pvMemory ) : m_pvMemory( pvMemory ) { byte const *pBytes = ( byte const * ) pvMemory; m_dwResult = * ( DWORD const * ) pBytes; pBytes += sizeof( DWORD ); m_dwResultBufferLength = * ( DWORD const * ) pBytes; pBytes += sizeof( DWORD ); m_pvResultBuffer = pBytes; pBytes += m_dwResultBufferLength; m_szListing = ( char const * ) ( *pBytes ? pBytes : NULL ); } void ShaderCompile_Subprocess_ExceptionHandler( unsigned long exceptionCode, void *pvExceptionInfo ) { // Subprocesses just silently die in our case, then this case will be detected by the worker process and an error code will be passed to the master Assert( !"ShaderCompile_Subprocess_ExceptionHandler" ); ::TerminateProcess( ::GetCurrentProcess(), exceptionCode ); } int ShaderCompile_Subprocess_Main( char const *szSubProcessData ) { // Set our crash handler SetupToolsMinidumpHandler( ShaderCompile_Subprocess_ExceptionHandler ); // Get our kernel objects SubProcessKernelObjects_Open objs( szSubProcessData ); if ( !objs.IsValid() ) return -1; // Enter the command pumping loop SubProcessKernelObjects_Memory shrmem( &objs ); for ( void *pvMemory = NULL; NULL != ( pvMemory = shrmem.Lock() ); shrmem.Unlock() ) { // The memory is actually a command char const *szCommand = ( char const * ) pvMemory; if ( !stricmp( "keepalive", szCommand ) ) { ZeroMemory( pvMemory, 4 * sizeof( DWORD ) ); continue; } if ( !stricmp( "quit", szCommand ) ) { ZeroMemory( pvMemory, 4 * sizeof( DWORD ) ); return 0; } CmdSink::IResponse *pResponse = NULL; if ( InterceptFxc::TryExecuteCommand( szCommand, &pResponse ) ) { byte *pBytes = ( byte * ) pvMemory; // Result DWORD dwSucceededResult = pResponse->Succeeded() ? 1 : 0; * ( DWORD * ) pBytes = dwSucceededResult; pBytes += sizeof( DWORD ); // Result buffer len DWORD dwBufferLength = pResponse->GetResultBufferLen(); * ( DWORD * ) pBytes = dwBufferLength; pBytes += sizeof( DWORD ); // Result buffer const void *pvResultBuffer = pResponse->GetResultBuffer(); memcpy( pBytes, pvResultBuffer, dwBufferLength ); pBytes += dwBufferLength; // Listing - copy string const char *szListing = pResponse->GetListing(); if ( szListing ) { while ( 0 != ( * ( pBytes ++ ) = * ( szListing ++ ) ) ) { NULL; } } else { * ( pBytes ++ ) = 0; } } else { ZeroMemory( pvMemory, 4 * sizeof( DWORD ) ); } } return -2; }