More on Messages and Threads
Back to Introduction to Messages
Back to Win32 ASM Page
Threads and windows
Whenever a window is created, it is "bound" to the current
thread. That thread is the only thread that can execute the
window procedure for that particular window. Consequently, it is
the only thread that can process messages for that window. In a
sense, the thread "owns" the window.
However, another window (of the same class) can be created in
another thread, allowing the two windows to process messages
concurrently. Because more than one thread can be executing a
given window procedure, the following discussions talk about
functions being "called by the thread" instead.
Preempting or nonpreempting?
While the thread system is preemptive, the message system is not.
A thread is preempted solely to let other threads run. A thread
is never preempted to change its point of execution. Thus a
thread, in general, won't respond immediately to new messages.
[But see same-thread sends.]
That's because message systems have three basic components:
senders, queues, and receivers. A sender routine will put a
message in some designated queue, and a receiver routine must
pull it out before a message handler can process it. If a
receiver routine isn't called by a thread, the thread cannot
handle any messages sitting in the queue. Windows provides two
functions for receiving messages: GetMessage and PeekMessage.
Threads and message queues
"Posted" messages are delivered to threads. That means each
thread that expects messages will have its own message queue.
Because each window is associated with exactly one thread,
PostMessage can determine which thread to post to (without
needing a thread argument).
"Sent" messages are, on the surface, delivered to windows.
Because threads do the work of handling received messages, "sent"
messages must actually be delivered to threads. Except for
same-thread messages [see Same-thread
sends], such messages must be placed in a thread message
queue -- one that is associated with the destination window.
Whether or not this queue is the same as the "PostMessage" queue
is unimportant. It's only important for the system to know if a
message is from a "SendMessage" function or a "PostMessage"
function.
Thread blocking and SendMessage
When sending a message to a window, the sending function may need
to wait for a reply. While it is waiting, the
thread which called the function is said to be
blocked. This situation prevents the thread from
handling any messages for other windows it "owns". Thus window
contents don't get updated, and when a window is uncovered, it
doesn't get redrawn.
When SendMessage sends a message to a window in another
thread, it waits for a reply which is issued when the receiving
thread calls ReplyMessage. An explicit reply is usually not
necessary because when a window procedure exits, ReplyMessage is
automatically called. An explicit ReplyMessage will be necessary
to avoid deadlock if there is chain of unfinished
SendMessage calls that involves more than one thread in a
circular fashion. [See Deadlock
Theory.]
For example:
The SendMessage chain
(window A, thread 1) --> (B, thread 2) --> ...
--> (C, thread n) --> (D, [back to] thread 1)
will deadlock when C (in thread n) sends a message to D,
if none of the window procedures have called ReplyMessage. If any
of the invoked window procedures (other than D) call ReplyMessage
before calling SendMessage, the chain is broken.
Same-thread sends
SendMessage, a thread blocking function, is used quite often to
send a message to a window in the same thread. Under
normal rules, this function would put the message on a queue and
then force the thread to wait for itself to reply. Since it's
waiting, it can't reply--deadlock. In this special case, a
"same-thread" send, Windows will directly call the proper window
procedure as if it were a subroutine. In effect, this is thread
preemption for the purpose of responding to new messages.
Because it doesn't require the message to be pulled out of a
queue, this behavior allows messages to be processed without a
message loop. For example, CreateWindowEx can invoke your special
WM_CREATE code, and draw the window frame before your app enters
its message loop. Also, any window (created in the same thread)
can be updated immediately by sending it the appropriate message.
But be aware that this immediate response occurs only
when a message is sent (with SendMessage) to a window in the
same thread.
Forms of message transmission
SendMessage is a thread blocking function. It always waits for a
reply.
SendMessageTimeout blocks like SendMessage, but it unblocks
if the receiver takes too long to reply. You specify the timeout
interval.
SendNotifyMessage is a nonblocking function. It doesn't wait
for a reply, unless the destination window is in the same thread.
SendMessageCallback is another nonblocking function. It also
doesn't wait for a reply, unless the destination window is in the
same thread. You specify a callback routine which will be
executed when the receiving thread replies.
PostMessage is completely nonblocking. It never waits for a
reply. You must target a window. The window determines which
thread will receive the message.
PostThreadMessage is nonblocking and can't target a window.
You must target a thread.
PostQuitMessage is nonblocking and posts a WM_QUIT message to
the current thread.