11

I read the source code of FastMM4, and notice this interesting function

function GetThreadID: Cardinal;
{$ifdef 32Bit}
asm
  mov eax, FS:[$24]
end;
{$else}
begin
  Result := GetCurrentThreadID;
end;
{$endif}

I've tested it, and it works, so my question is any explanation why it works?

justyy
  • 5,831
  • 4
  • 40
  • 73
  • 2
    And another [must see link](http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Thread/TEB.html) on TEB. [MS documentation](http://msdn.microsoft.com/library/windows/desktop/ms686708) is useless for this subject. – OnTheFly Apr 23 '13 at 16:12
  • On the contrary the MS documentation is excellent. Since this is private implementation the correct documentation strategy for private implementation details is to keep it private. – David Heffernan Apr 23 '13 at 20:04
  • @DoctorLai, I'm curious, why do you call reading memory with **magic** displacement an "effecient"? – OnTheFly Apr 23 '13 at 20:04
  • @David Heffernan, makes sense, but only until these private details published. Since when, it becomes just poorly documented details. – OnTheFly Apr 23 '13 at 20:09
  • @user539484 you are right, actually i didn't know how the Win32 API GetCurrentThreadID is implemented. maybe they both use the same thing. – justyy Apr 23 '13 at 20:09
  • 1
    @Doctor You can step into the winapi call. And of course it uses TEB. – David Heffernan Apr 23 '13 at 20:10
  • @user and then ms are probably stuck with the current offsets until the end of time! – David Heffernan Apr 23 '13 at 20:11
  • @DoctorLai, yes, Windows implementation reads TEB, **but** they use helper function [`NtCurrentTeb/ZwCurrentTeb`](http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Thread/NtCurrentTeb.html) to attain certain grade of flexibility of private implementation. – OnTheFly Apr 23 '13 at 20:26
  • @David Heffernan, does "excellent" MS documentation shed at least some light onto discussed subject? – OnTheFly Apr 23 '13 at 20:31

2 Answers2

10

The x86 register FS points to the Thread Information Block in Windows. The value in TIB at address FS+0x24 contains ID of the current thread. By moving the value to eax, which is used to pass the function return value, GetThreadID returns the current thread ID.

Samuli Hynönen
  • 662
  • 1
  • 8
  • 24
  • so, it means i can do the same with FS+0x20 that returns the Process ID. this must be more efficient than the one defined in Windows unit. – justyy Apr 23 '13 at 16:17
  • 3
    It may be marginally more efficient but I am here to tell you that fetching PID and TID is not the bottleneck in your program. – David Heffernan Apr 23 '13 at 17:30
  • 1
    @David, Reading other posts from OP, I don't think OP has a bottleneck at all. He's more into extreme optimization kind of thing... perhaps :) – kobik Apr 23 '13 at 19:04
  • 1
    @kobik What's funny here, is that the implementation of `GetCurrentThreadId` simply read the TEB. I mean, what else would it do? I would not accept the code in the question into my codebase. – David Heffernan Apr 23 '13 at 19:06
  • @David, "I would not accept the code in the question into my codebase" - I'm with you on this. – kobik Apr 23 '13 at 19:15
  • 1
    @doctorlai: if you look at how winapi implementa this, it does the same thing really... [related stuff](http://www.remkoweijnen.nl/blog/2010/06/18/fun-with-asm/) – Remko Apr 23 '13 at 19:38
  • @kobib can I ask what is 'OP'? – justyy Apr 23 '13 at 19:42
  • @David Heffernan yes I agree with you. getting IDs never is the bottleneck. – justyy Apr 23 '13 at 19:43
  • @Remko so the advantage of doing such is to save the function calling overhead ? – justyy Apr 23 '13 at 19:43
  • 1
    @DoctorLai Why do you want to save function calling overhead? Why don't you want simple code that is easy to maintain? – David Heffernan Apr 23 '13 at 19:53
  • @DoctorLai at the expense of breaking stuff, like in x64 or windows rt. The performance benefit is probably not even measureable so I would stick to winapi! (for play the asm is cool ;-) ) – Remko Apr 23 '13 at 20:01
  • @Remko, it is unlikely a "fun", look more like dirty hacks. – OnTheFly Apr 23 '13 at 20:02
  • @user539484 what is dirty about it, it is 100% confirming to msdn docs and intel specs however limited to current implementation – Remko Apr 23 '13 at 20:04
  • @Remko, most assemblers supports structs (including BASM), you are using magic displacements instead, creating the grounds for the questions like this one. This is not cool. – OnTheFly Apr 23 '13 at 20:13
  • @Samuli Hynönen, a minor, but important correction: FS is x386 register, not just x86. – OnTheFly Apr 23 '13 at 20:28
  • Do you use FastMM4, @David? If so, then "accept the code in the question into my codebase" is exactly what you've done. The assembler implementation of `GetThreadID` has existed since revision 1. The conditional call to `GetCurrentThreadID` was added in revision 36 for 64-bit support of Delphi XE2. – Rob Kennedy Apr 23 '13 at 22:25
  • @Rob Not really. I mean the codebase which I manage. If the code had used GetCurrentThreadId from the start, the change in r36 would not have been needed. – David Heffernan Apr 24 '13 at 06:19
1

This method uses the information stored in the Thread Environment Block

MBo
  • 77,366
  • 5
  • 53
  • 86