{**************************************************************************************************} { } { Project JEDI Code Library (JCL) } { } { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); } { you may not use this file except in compliance with the License. You may obtain a copy of the } { License at http://www.mozilla.org/MPL/ } { } { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF } { ANY KIND, either express or implied. See the License for the specific language governing rights } { and limitations under the License. } { } { The Original Code is ThreadExpertSharedNames.pas. } { } { The Initial Developer of the Original Code is Petr Vones. } { Portions created by Petr Vones are Copyright (C) of Petr Vones. } { } {**************************************************************************************************} { } { Unit owner: Petr Vones } { Last modified: $Date: 2005/10/26 03:29:44 $ } { } {**************************************************************************************************} unit ThreadExpertSharedNames; {$I jcl.inc} interface uses Windows, SysUtils, Classes, JclBase, JclFileUtils, JclSynch; type TSharedThreadNames = class(TObject) private FIdeMode: Boolean; FMapping: TJclSwapFileMapping; FMutex: TJclMutex; FNotifyEvent: TJclEvent; FProcessID: DWORD; FReadMutex: TJclMutex; FView: TJclFileMappingView; function GetThreadName(ThreadID: DWORD): string; procedure InternalRegisterThread(ThreadID: DWORD; const ThreadName: string; UpdateOnly: Boolean); procedure SetThreadName(ThreadID: DWORD; const Value: string); protected function EnterMutex: Boolean; public constructor Create(IdeMode: Boolean); destructor Destroy; override; procedure Cleanup(ProcessID: DWORD); class function Exists: Boolean; procedure RegisterThread(ThreadID: DWORD; const ThreadName: string); function ThreadNameTimoeut(ThreadID, Timeout: DWORD; var ThreadName: string): Boolean; procedure UnregisterThread(ThreadID: DWORD); procedure UpdateResumeStatus; property ThreadName[ThreadID: DWORD]: string read GetThreadName write SetThreadName; default; property NotifyEvent: TJclEvent read FNotifyEvent; end; implementation uses JclOtaConsts, JclOtaResources, JclSysUtils; const MaxThreadCount = 256; IdeEnterMutexTimeout = 5000; type TThreadName = record ThreadID: DWORD; ProcessID: DWORD; ThreadName: ShortString; end; PThreadNames = ^TThreadNames; TThreadNames = record Count: Integer; Threads: array [0..MaxThreadCount - 1] of TThreadName; end; procedure SetIdeDebuggerThreadName(ThreadID: DWORD; const ThreadName: string); type TThreadNameInfo = record FType: Longword; // must be 0x1000 FName: PChar; // pointer to name (in user address space) FThreadID: Longword; // thread ID (-1 indicates caller thread) FFlags: Longword; // reserved for future use, must be zero end; var ThreadNameInfo: TThreadNameInfo; begin ThreadNameInfo.FType := $1000; ThreadNameInfo.FName := PChar(ThreadName); ThreadNameInfo.FThreadID := ThreadID; ThreadNameInfo.FFlags := 0; try RaiseException($406D1388, 0, SizeOf(ThreadNameInfo) div SizeOf(Longword), @ThreadNameInfo); except end; end; //=== { TSharedThreadNames } ================================================= constructor TSharedThreadNames.Create(IdeMode: Boolean); begin inherited Create; FIdeMode := IdeMode; FMutex := TJclMutex.Create(nil, False, MutexName); FReadMutex := TJclMutex.Create(nil, False, MutexReadName); FMapping := TJclSwapFileMapping.Create(MappingName, PAGE_READWRITE, SizeOf(TThreadNames), nil); FView := TJclFileMappingView.Create(FMapping, FILE_MAP_ALL_ACCESS, 0, 0); FNotifyEvent := TJclEvent.Create(nil, False, False, EventName); FProcessID := GetCurrentProcessId; end; destructor TSharedThreadNames.Destroy; begin Cleanup(FProcessID); FreeAndNil(FMapping); FreeAndNil(FMutex); FreeAndNil(FReadMutex); FreeAndNil(FNotifyEvent); inherited Destroy; end; procedure TSharedThreadNames.Cleanup(ProcessID: DWORD); var I: Integer; begin if EnterMutex then try with PThreadNames(FView.Memory)^ do for I := Low(Threads) to High(Threads) do with Threads[I] do if ProcessID = ProcessID then begin FReadMutex.WaitForever; try ProcessID := 0; ThreadID := 0; ThreadName := ''; finally FReadMutex.Release; end; end; finally FMutex.Release; end; end; function TSharedThreadNames.EnterMutex: Boolean; begin if FIdeMode then begin case FMutex.WaitFor(IdeEnterMutexTimeout) of wrSignaled: Result := True; wrTimeout: raise Exception.Create(RsEnterMutexTimeout); else Result := False; end; end else begin Sleep(0); // Prevent random deadlocks with IDE Result := FMutex.WaitForever = wrSignaled; end; end; class function TSharedThreadNames.Exists: Boolean; {$IFDEF DELPHI7_UP} begin Result := True; end; {$ELSE DELPHI7_UP} var H: THandle; begin H := OpenMutex(MUTEX_ALL_ACCESS, False, PChar(MutexName)); Result := (H <> 0); if Result then CloseHandle(H); end; {$ENDIF DELPHI7_UP} function TSharedThreadNames.GetThreadName(ThreadID: DWORD): string; var I: Integer; begin Result := ''; if FReadMutex.WaitForever = wrSignaled then try with PThreadNames(FView.Memory)^ do for I := Low(Threads) to High(Threads) do if Threads[I].ThreadID = ThreadID then begin Result := Threads[I].ThreadName; Break; end; finally FReadMutex.Release; end; end; procedure TSharedThreadNames.InternalRegisterThread(ThreadID: DWORD; const ThreadName: string; UpdateOnly: Boolean); var I, Slot: Integer; NeedNotify: Boolean; begin if EnterMutex then try Slot := -1; NeedNotify := ThreadID = MainThreadID; with PThreadNames(FView.Memory)^ do begin for I := Low(Threads) to High(Threads) do if Threads[I].ThreadID = ThreadID then begin Slot := I; NeedNotify := True; Break; end else if (not UpdateOnly) and (Slot = -1) and (Threads[I].ThreadID = 0) then Slot := I; if Slot <> -1 then begin FReadMutex.WaitForever; try Threads[Slot].ProcessID := FProcessID; Threads[Slot].ThreadID := ThreadID; Threads[Slot].ThreadName := ThreadName; finally FReadMutex.Release; end; end; end; {$IFDEF DELPHI7_UP} SetIdeDebuggerThreadName(ThreadID, ThreadName); {$ENDIF DELPHI7_UP} if NeedNotify then FNotifyEvent.SetEvent; finally FMutex.Release; end; end; procedure TSharedThreadNames.RegisterThread(ThreadID: DWORD; const ThreadName: string); begin InternalRegisterThread(ThreadID, ThreadName, False); end; procedure TSharedThreadNames.SetThreadName(ThreadID: DWORD; const Value: string); begin InternalRegisterThread(ThreadID, Value, True); end; function TSharedThreadNames.ThreadNameTimoeut(ThreadID, Timeout: DWORD; var ThreadName: string): Boolean; var I: Integer; begin Result := FReadMutex.WaitFor(Timeout) = wrSignaled; if Result then try with PThreadNames(FView.Memory)^ do for I := Low(Threads) to High(Threads) do if Threads[I].ThreadID = ThreadID then begin ThreadName := Threads[I].ThreadName; Break; end; finally FReadMutex.Release; end; end; procedure TSharedThreadNames.UnregisterThread(ThreadID: DWORD); var I: Integer; begin EnterMutex; try with PThreadNames(FView.Memory)^ do for I := Low(Threads) to High(Threads) do if Threads[I].ThreadID = ThreadID then begin FReadMutex.WaitForever; try Threads[I].ProcessID := 0; Threads[I].ThreadID := 0; Threads[I].ThreadName := ''; finally FReadMutex.Release; end; Break; end; finally FMutex.Release; end; end; procedure TSharedThreadNames.UpdateResumeStatus; var I: Integer; begin EnterMutex; try with PThreadNames(FView.Memory)^ do for I := Low(Threads) to High(Threads) do if Threads[I].ThreadID <> 0 then begin FReadMutex.WaitForever; try SetIdeDebuggerThreadName(Threads[I].ThreadID, Threads[I].ThreadName); finally FReadMutex.Release; end; end; finally FMutex.Release; end; end; // History: // $Log: ThreadExpertSharedNames.pas,v $ // Revision 1.4 2005/10/26 03:29:44 rrossmair // - improved header information, added Date and Log CVS tags. // end.