"Use of GoBug" demonstrations
Debugging threads

These tests demonstrate how GoBug deals with the creation of threads by the debuggee.
   Thread break
   One thread created
   Six threads created
   Thread priority test
   Thread making window test

Thread break
Open Testbug as the debuggee. From "START" set a breakpoint at the start of a new thread by using GoBug's menu item "actions, run to new thread". Click on Testbug's menu item "use of Gobug, debugging threads, one thread created". The new thread will cause a break, and you will see the new thread sweep over the panes. Look at the thread control buttons to see the new thread handle, and look in the log to see the record of what happened. You can now single-step down the code in the new thread to see what happens.

One thread created
From "START" press F9 to run in the background and start the test by using Testbug's menu item "use of Gobug, debugging threads, one thread created". A message box is made by the new thread. See the new thread control button in GoBug's toolbar. Click on that and also on the other thread control button to see where GoBug believes the thread is executing. Click on OK in the message box to close it. Open the log and see the new thread being created and closed. View the return code of 1A1A1A1Ah in the log.
Here is the code:-

ONE_THREAD_CREATED:
PUSH ADDR Thread1AId
PUSH 0,01010101h
PUSH ADDR MAIN1A        ;address for new thread to start
PUSH 0,0
CALL CreateThread
MOV [hThread1A],EAX     ;keep handle to new thread
MOV ECX,100D
L866:
MOV EAX,EDX
MOV EDX,EAX
LOOP L866
XOR EAX,EAX
RET
;
MAIN1A:
MOV AL,10D
L32:
DEC CL
DEC AL
JNS L32
PUSH 40h                ;information+ok button only
PUSH 'Testbug - create 1 thread'
PUSH 'This message box is shown by thread 1A'
PUSH [hWnd]
CALL MessageBoxA        ;wait till ok pressed
MOV EAX,1A1A1A1Ah
RET                     ;terminate thread

Six threads created
From "START" press F9 to run in the background and start the test. Six new threads are made and six message boxes are also made. Some threads make other threads. See the new thread control buttons in GoBug's toolbar. Click on those to see where GoBug believes the thread is executing. Click on OK in the message boxes to close them. Open the log and see the new threads being created and closed. View the return codes in the log.
Now do the same but using F7. View in the log the instructions carried out by each thread.
Here is the code:-

SIX_THREADS_CREATED:
PUSH ADDR Thread2Id
PUSH 0,01010101h
PUSH ADDR MAIN2         ;address for new thread to start
PUSH 0,0
CALL CreateThread
MOV [hThread2],EAX      ;get handle to new thread
PUSH ADDR Thread3Id
PUSH 0,01010101h
PUSH ADDR MAIN3         ;address for new thread to start
PUSH 0,0
CALL CreateThread
MOV [hThread3],EAX      ;get handle to new thread
PUSH ADDR Thread4Id
PUSH 0,01010101h
PUSH ADDR MAIN4         ;address for new thread to start
PUSH 0,0
CALL CreateThread
MOV [hThread4],EAX      ;get handle to new thread
XOR EAX,EAX
RET
;
MAIN2B:                 ;thread 2B
MOV EAX,[EBP+0Ch]
PUSH 40h                ;information+ok button only
PUSH 'Testbug - create 6 threads'
PUSH 'This message box is shown by thread 2B'
PUSH [hWnd]
CALL MessageBoxA        ;wait till ok pressed
MOV EAX,1B1B1B1Bh
RET
;
MAIN2A:                 ;thread 2A
MOV EAX,[EBP+0Ch]
PUSH ADDR Thread2BId
PUSH 0,2A2A2A2Ah
PUSH ADDR MAIN2B        ;address for new thread to start
PUSH 0,0
CALL CreateThread
MOV [hThread2B],EAX     ;get handle to new thread
PUSH 40h                ;information+ok button only
PUSH 'Testbug - create 6 threads'
PUSH 'This message box is shown by thread 2A'
PUSH hWnd
CALL MessageBoxA        ;wait till ok pressed
MOV EAX,2A2A2A2Ah
RET
;
MAIN2:                  ;thread 2
MOV EAX,[EBP+0Ch]
PUSH ADDR Thread2AId
PUSH 0,02020202h
PUSH ADDR MAIN2A        ;address for new thread to start
PUSH 0,0
CALL CreateThread
MOV [hThread2A],EAX     ;get handle to new thread
PUSH 40h                ;information+ok button only
PUSH 'Testbug - create 6 threads'
PUSH 'This message box is shown by thread 2'
PUSH hWnd
CALL MessageBoxA        ;wait till ok pressed
MOV EAX,22222222h
RET                     ;terminate thread
;
MAIN3A:                 ;thread 3A
MOV EAX,[EBP+0Ch]
PUSH 40h                ;information+ok button only
PUSH 'Testbug - create 6 threads'
PUSH 'This message box is shown by thread 3A'
PUSH [hWnd]
CALL MessageBoxA        ;wait till ok pressed
MOV EAX,3A3A3A3Ah
RET
;
MAIN3:                  ;thread 3
MOV EAX,[EBP+0Ch]
PUSH ADDR Thread3AId
PUSH 0,03030303h
PUSH ADDR MAIN3A        ;address for new thread to start
PUSH 0,0
CALL CreateThread
MOV [hThread3A],EAX     ;get handle to new thread
PUSH 40h                ;information+ok button only
PUSH 'Testbug - thread test 1'
PUSH 'This message box is shown by thread 3'
PUSH [hWnd]
CALL MessageBoxA        ;wait till ok pressed
MOV EAX,33333333h
RET                     ;terminate thread
;
MAIN4:                  ;thread 4
MOV EAX,[EBP+0Ch]
PUSH 40h                ;information+ok button only
PUSH 'Testbug - thread test 1'
PUSH 'This message box is shown by thread 4'
PUSH [hWnd]
CALL MessageBoxA        ;wait till ok pressed
MOV EAX,44444444h
RET                     ;terminate thread

Thread priority test
This test demonstrates how a thread can give itself priority over other threads. This may be useful for example where a thread needs to work on data and other threads cannot proceed until that work has been completed. To eliminate conflict you should always ensure that the busy thread signals the waiting thread before the waiting thread is allowed to proceed. To do this you can use a data symbol as here (you should use the API Sleep when polling this to avoid wasting processor time), or you can use the APIs dedicated to inter-thread signalling such as WaitForSingleObject.
To view this test just run using F9, run the test and then view the exit code for the thread in the log. This will correspond to the value of LOOPCOUNTER, which you will see will be a remarkably high value, demonstrating the vastly increased priority which was given to the new thread.
Here is the code:-

THREAD_PRIORITYTEST:
MOV D[THREADTESTER],0
MOV D[LOOPCOUNTER],0
PUSH ADDR ThreadId,0,0,ADDR THREAD_PRIORITYTEST1,0,0
CALL CreateThread
XOR ECX,ECX
L10:
CMP D[THREADTESTER],1      ;wait till thread has been made
LOOPNZ L10
MOV ECX,10h
L16:
MOV EAX,EAX
MOV EAX,EAX
MOV EAX,EAX
MOV EAX,EAX
MOV EAX,EAX
MOV EAX,EAX
MOV EAX,EAX
MOV EAX,EAX
LOOP L16
MOV D[THREADTESTER],2
RET
;
THREAD_PRIORITYTEST1:
CALL GetCurrentThread  ;get pseudohandle to this thread
PUSH 2,EAX
CALL SetThreadPriority ;THREAD_PRIORITY_HIGHEST
MOV D[THREADTESTER],1  ;indicate thread has been made
L3:
CMP D[THREADTESTER],2  ;see if may stop yet
JZ >L4                 ;yes
INC D[LOOPCOUNTER]
JMP L3
L4:
MOV EAX,[LOOPCOUNTER]
RET

Thread making window test
It is not good practice to manage your windows using any thread other than the one which made the window. This is because the system conducts window management using messages in strict sequence as can be seen from the messages which result from any window management action or user input. However it is difficult to think of any reason why a secondary thread may not use the Windows Graphical User Interface (the "GUI"), by making its own windows. Each thread may have its own message queue so this is perfectly feasible. Perhaps an application which does this could receive more processor time at the expense of other applications.
In this test you can see what happens when a window is made by a secondary thread. Press F8 from "START" and run the test. See how the messages for the new window made by the secondary thread are intermingled in sequence with messages for the main thread. Move the new window around, activate it and resize it to see what happens.
Here is the code involved (I have not included the window procedure here which is standard coding):-

THREADMAKINGWINDOW_TEST:
PUSH ADDR Thread1AId
PUSH 0,0
PUSH ADDR THREAD_MAKINGWINDOW    ;address for new thread to start
PUSH 0,0
CALL CreateThread
RET
;
THREAD_MAKINGWINDOW:
CALL INITIALISE_WNDCLASS    ;get ready for all windows
MOV D[EBX],01h+02h          ;CS_VREDRAW+CS_HREDRAW
MOV D[EBX+4],ADDR ThreadMakingWindowWndProc
MOV D[EBX+24h],ADDR SIMPLE_CLASSNAME
PUSH EBX                    ;address of structure with window class data
CALL RegisterClassA         ;register a window class for subsequent use
PUSH 0,[hInst],0,[hWnd]     ;owner=main window
PUSH 200D                   ;height
PUSH 260D                   ;width
PUSH 100D,100D              ;position y then x
PUSH 90000000h +0C00000h+40000h +80000h +20000h     +10000h
;(POPUP+VISIBLE)+CAPTION+SIZEBOX+SYSMENU+MINIMIZEBOX+MAXIMIZEBOX
PUSH 'Thread making window test'
PUSH ADDR SIMPLE_CLASSNAME  ;class
PUSH 0                      ;extended window style
CALL CreateWindowExA        ;make window, returning handle in EAX
;************************ now enter the main loop
L60:
PUSH 0,0,0
PUSH ADDR THREADMSG
CALL GetMessageA
OR EAX,EAX
JZ >L62
PUSH ADDR THREADMSG
CALL TranslateMessage
PUSH ADDR THREADMSG
CALL DispatchMessageA
JMP L60
L62:
PUSH [hInst],ADDR SIMPLE_CLASSNAME
CALL UnregisterClassA   ;ensure class is removed
MOV EAX,[THREADMSG+8h]
RET                     ;terminate thread with exit code