phobos/std/thread_helper.d
2010-05-22 21:04:09 +00:00

189 lines
6.1 KiB
D

// Written in the D programming language
/*
* Copyright (C) 2010 by Digital Mars, http://www.digitalmars.com
* Written by Rainer Schuetze
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* o The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* o Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
* o This notice may not be removed or altered from any source
* distribution.
*/
/**
* This module provides OS specific helper function for threads support
*/
module std.thread_helper;
version( Windows )
{
import std.c.windows.windows;
import std.c.stdlib;
public import std.thread;
///////////////////////////////////////////////////////////////////
const SystemProcessInformation = 5;
const STATUS_INFO_LENGTH_MISMATCH = 0xc0000004;
// abbreviated versions of these structs
struct _SYSTEM_PROCESS_INFORMATION
{
int NextEntryOffset; // When this entry is 0, there are no more processes to be read.
int NumberOfThreads;
int[15] fill1;
int ProcessId;
int[28] fill2;
// SYSTEM_THREAD_INFORMATION or SYSTEM_EXTENDED_THREAD_INFORMATION structures follow.
}
struct _SYSTEM_THREAD_INFORMATION
{
int[8] fill1;
int ProcessId;
int ThreadId;
int[6] fill2;
}
alias extern(Windows)
HRESULT fnNtQuerySystemInformation( uint SystemInformationClass, void* info, uint infoLength, uint* ReturnLength );
const ThreadBasicInformation = 0;
struct THREAD_BASIC_INFORMATION
{
int ExitStatus;
void** TebBaseAddress;
int ProcessId;
int ThreadId;
int AffinityMask;
int Priority;
int BasePriority;
}
alias extern(Windows)
int fnNtQueryInformationThread( HANDLE ThreadHandle, uint ThreadInformationClass, void* buf, uint size, uint* ReturnLength );
///////////////////////////////////////////////////////////////////
// support attaching to thread other than just executing
void** getTEB( HANDLE hnd )
{
HANDLE nthnd = GetModuleHandleA( "NTDLL" );
assert( nthnd, "cannot get module handle for ntdll" );
fnNtQueryInformationThread* fn = cast(fnNtQueryInformationThread*) GetProcAddress( nthnd, "NtQueryInformationThread" );
assert( fn, "cannot find NtQueryInformationThread in ntdll" );
THREAD_BASIC_INFORMATION tbi;
int Status = (*fn)(hnd, ThreadBasicInformation, &tbi, tbi.sizeof, null);
assert(Status == 0);
return tbi.TebBaseAddress;
}
extern(Windows)
HANDLE OpenThread(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId);
const SYNCHRONIZE = 0x00100000;
const THREAD_GET_CONTEXT = 8;
const THREAD_QUERY_INFORMATION = 0x40;
const THREAD_SUSPEND_RESUME = 2;
void** getTEB( uint id )
{
HANDLE hnd = OpenThread( THREAD_QUERY_INFORMATION, FALSE, id );
assert( hnd, "OpenThread failed" );
void** teb = getTEB( hnd );
CloseHandle( hnd );
return teb;
}
void* getThreadStackBottom( HANDLE hnd )
{
void** teb = getTEB( hnd );
return teb[1];
}
void* getThreadStackBottom( uint id )
{
void** teb = getTEB( id );
return teb[1];
}
HANDLE OpenThreadHandle( uint id )
{
return OpenThread( SYNCHRONIZE|THREAD_GET_CONTEXT|THREAD_QUERY_INFORMATION|THREAD_SUSPEND_RESUME, FALSE, id );
}
///////////////////////////////////////////////////////////////////
// support attaching to all running threads
// using function instead of delegate here to avoid allocating closure
bool enumProcessThreads( uint procid, bool function( uint id, void* context ) dg, void* context )
{
HANDLE hnd = GetModuleHandleA( "NTDLL" );
fnNtQuerySystemInformation* fn = cast(fnNtQuerySystemInformation*) GetProcAddress( hnd, "NtQuerySystemInformation" );
if( !fn )
return false;
uint sz = 16384;
uint retLength;
HRESULT rc;
char* buf;
for( ; ; )
{
buf = cast(char*) std.c.stdlib.malloc(sz);
if(!buf)
return false;
rc = (*fn)( SystemProcessInformation, buf, sz, &retLength );
if( rc != STATUS_INFO_LENGTH_MISMATCH )
break;
std.c.stdlib.free( buf );
sz *= 2;
}
scope(exit) std.c.stdlib.free( buf );
if(rc != 0)
return false;
auto pinfo = cast(_SYSTEM_PROCESS_INFORMATION*) buf;
auto pend = cast(_SYSTEM_PROCESS_INFORMATION*) (buf + retLength);
for( ; pinfo < pend; )
{
if( pinfo.ProcessId == procid )
{
auto tinfo = cast(_SYSTEM_THREAD_INFORMATION*)(pinfo + 1);
for( int i = 0; i < pinfo.NumberOfThreads; i++, tinfo++ )
if( tinfo.ProcessId == procid )
if( !dg( tinfo.ThreadId, context ) )
return false;
}
if( pinfo.NextEntryOffset == 0 )
break;
pinfo = cast(_SYSTEM_PROCESS_INFORMATION*) (cast(char*) pinfo + pinfo.NextEntryOffset);
}
return true;
}
bool enumProcessThreads( bool function( uint id, void* context ) dg, void* context )
{
return enumProcessThreads( GetCurrentProcessId(), dg, context );
}
}