News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Using PostThreadMessage

Started by donkey, April 25, 2011, 01:47:14 AM

Previous topic - Next topic

donkey

I am working on a function that requires the main thread to be suspended in order to work properly (there are quite a few low level functions that require this). Problem is that if your main thread is suspended how do you execute the routine and how do you communicate to the main thread that you're done ? Here's my solution:

// Create an event to synchronize our two threads
invoke CreateEvent,NULL,TRUE,FALSE,"PostThreadMessage_Event"
mov ebx,eax

// Create some local space on the stack for the thread id
sub esp,4

// Since we created some space, we just use ESP as the address to receive
// the thread id returned from CreateThread
invoke CreateThread,NULL,0x10000,offset ThreadProc,ebx,NULL,esp

// We have no need of the thread handle so close it
invoke CloseHandle,eax

// The first wait is for the thread's message queue to be ready
invoke WaitForSingleObject,ebx,INFINITE

// We're only using one event for two waits so reset it
invoke ResetEvent,ebx

// POP the thread id, this will also balance the stack
pop eax

// Post our thread message
invoke PostThreadMessage,eax,WM_USER,0,0

// Wait for the thread to process the message
invoke WaitForSingleObject,ebx,INFINITE

// Destroy the event when you're done with it (after the thread exits!)
invoke CloseHandle,ebx

ThreadProc FRAME lpParameter
LOCAL msg:MSG

// Force Windows to create a message queue
invoke PeekMessage, offset msg, NULL, WM_USER, WM_USER, PM_NOREMOVE

// Once the message queue is ready signal the main thread
invoke SetEvent,[lpParameter]

// Wait for the message, we are only interested in WM_USER to WM_USER+1
invoke GetMessage,offset msg,NULL,WM_USER,WM_USER+1
or eax,eax
JZ >.quit
mov eax,[msg.message]
// WM_USER instructs the thread to do what ever it is you want
cmp eax,WM_USER
// wParam and lParam are used to pass information

// Execute anything you need to here. In my application I suspend the thread here.

// From here you can jump back up and wait for another message or as in my
// case just terminate the thread. In my application it has only the one function
// and I didn't need it after it was done.
.quit
// since the calling thread does not wait until the message result
// is returned it is held in a WaitForSingleObject state
// until the object is signalled. The event handle is passed in
// lpParameter when the thread is started
invoke SetEvent,[lpParameter]
// We're all done so we can terminate the thread
invoke ExitThread,0
ret
endf


For my purposes I was not concerned about a return value from the WM_USER or WM_USER+1 messages however if you need one you can always pass it to the main thread in global memory. Hope some one might find this useful.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

dedndave

interesting idea, Edgar
i have been pondering something along these lines
haven't gotten so far with the project that i have any code written yet

i was thinking of using Sleep, though

in my application, the problem is that if you alter the process priority class, it alters for the main thread, as well as the other, of course
we can set thread priority level seperately, too
but, i wonder how the main thread will behave when i jack the priority class up and the thread level down   :bg
it is still an increased priority, as i understand it

donkey

Thanks Dave,

I'm not sure about the sleep function in a case like this, the loops would be messy since you're waiting for Windows to complete building the message queue and you may be delaying execution unnecessarily with a long enough sleep. I prefer the wait functions and after all an event is pretty simple to manipulate and as far as synchronization objects go the easiest to use. In my case I just needed the main threads CONTEXT structure and its unreliable for a thread to gets its own CONTEXT, actually its just pretty much garbage that you get. The actual application for it is the debugging tools I've been working on, but this function is still a LONGGGGG way off, being only in the experimental stages right now.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

dedndave

oh no - your method sounds much cleaner than using Sleep
being a n00b, i was not too familiar with the wait idea
i may employ something very similar

i assume that wait suspends thread execution in a manner somewhat like sleep
that's really the important thing - to let the other thread have the CPU cycles
i'll have to read up on WaitForSingleObject   :P

donkey

The thread is completely suspended until restarted by the OS when the wait function is satisfied. Running two threads at different priorities could be a nightmare without the wait functions. For example whether you have a single or multiple processors can drastically affect the run of the threads. On a single processor the lower thread will always be preempted by the higher one and on multiple processors they can both run unhindered in separate cores. So the same program will run differently depending on the system architecture. The only way around this is a fairly comprehensive synchronization scheme, I can see ordering problems and race conditions even based on what particular core you're running on (for example cache algorithms become an issue) without the wait functions and critical sections.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

donkey

Hi Dave,

You might be interested to know that in order to suspend the main thread you will have to change the thread handle returned from GetCurrentThread from a pseudo handle to a real one. GetCurrentProcess always returns -1 (the pseudo handle) and GetCurrentThread always returns -2. In order to get a usable thread handle for SuspendThread you use DuplicateHandle:

invoke CreateEvent,NULL,TRUE,FALSE,"PostThreadMessage_Event"
mov ebx,eax
sub esp,4
invoke CreateThread,NULL,0x10000,offset ThreadProc,ebx,NULL,esp
invoke CloseHandle,eax
invoke WaitForSingleObject,ebx,INFINITE
invoke ResetEvent,ebx
pop esi

sub esp,4
mov edx,esp
// This will convert the pseudo handle of the current thread to a regular handle
// Use -1 (current process) and -2 (current thread)
invoke DuplicateHandle,-1,-2,-1,edx,NULL,FALSE,2
pop eax

invoke PostThreadMessage,esi,WM_USER,0,eax
invoke WaitForSingleObject,ebx,INFINITE
invoke CloseHandle,ebx
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

dedndave

cool
i haven't got to that part of the code, yet
but, i have a picture in my head how it all goes together, now   :P