home *** CD-ROM | disk | FTP | other *** search
-
- MULTI-THREADING PM APPLICATIONS MONTE COPELAND
-
- Over the years, many different styles have emerged to
- multi-thread PM applications. One approach is to start a new
- thread whenever the application is asked to perform a lengthy
- task. Another approach is to start one thread when the
- application starts, then give it work to do.
-
- What is really important is that your PM applications ARE
- multi-threaded. The best ones are.
-
- The style put forth in this article is the start-a-thread,
- keep-it-around, and give-it-work approach. This approach has its
- merits. Such a thread is blocked in the kernel scheduler, and it
- does not incur any system overhead as it waits for work to do.
- The start-one-when-needed approach is more costly, for
- there is a non-trivial overhead in the kernel to create a thread.
-
- The problem to solve is this: how best to harness a work-horse
- thread?
-
- One approach is to set up a pair of semaphores: one for signaling
- the thread to start a task, and the other for the thread to
- signal completion of the task. Another approach might be to use
- the OS/2 queueing subsystem, the queue calls, in QUECALLS.DLL in
- order to pass around messages.
-
- The approach presented in this article is based on PM message
- queues. It is a reasonable choice because PM applications are
- required to have message queues already. Furthermore, it is
- possible to achieve multi-threading without the use of
- semaphores.
-
-
-
- WHY MULTI-THREADED PM APPLICATIONS ARE A MUST
-
- PM apps, like all OS/2 applications, call into the operating
- system for services via the OS/2 API's. However, PM apps are
- required to provide functions for the Presentation Manager to
- call, too. These functions are called window procedures.
-
- PM delivers messages to a PM application by calling its window
- procedures. These messages include menu selections, mouse
- clicks, and termination notification messages. PM sends a
- message to the application when it is time to repaint the window.
- PM can also deliver user-defined messages.
-
- In order for the Presentation Manager to stay in synch with all
- the applications on the desktop, it delivers some messages one at
- a time. These are "sent" messages. Stated differently, when the
- Presentation Manager sends a message to your window procedure, it
- stops sending messages elsewhere until your procedure returns.
-
- Therefore, PM apps must respond to sent messages in a timely
- manner; one-tenth second is an often-cited response time that has
- come to be known as the one-tenth-second rule. When an
- application takes too long, unacceptable behavior occurs:
-
-
- . user unable to minimize the application
-
- . user unable to switch to another application
-
- . hourglass pointer over entire desktop
-
- . the system dialog: "Application is not responding to system
- requests. Press Enter to end it."
-
-
- Use a second thread in PM apps to bust the 1/10th second rule and
- perform tasks that take time: diskcopy, upload/download, file
- input/output, SQL queries. This article describes a simple,
- robust two-threaded PM application architechure. Applications
- coded in this style obey the 1/10th second rule, and they can
- perform lengthy tasks.
-
-
- THREAD DUTIES
-
- Thread one is responsible for presentation. Thread one operates
- the first message queue created by the application. This message
- queue gets and dispatches messages to one or more application
- windows on the desktop such as the client window, dialog windows,
- and message boxes. It receives messages from menus and child
- controls like buttons and listboxes. It processes messages
- generated by the frame window controls. Thread one is devoted to
- the operation of all the application's visible windows on the
- desktop.
-
- Thread two will create and operate another message queue. This
- message queue will deliver messages to an object window and its
- window procedure. The PM-defined window HWND_OBJECT is both the
- parent and owner window of the application's object window.
- Object windows are invisible; they do not appear on the desktop.
- Best of all, they are not bound by the 1/10th-second rule!
-
- When object window procedures execute on thread two, they are
- perfect for doing time-consuming tasks. While the object window
- is busy working on a task, the main window procedure is still
- getting and dispatching messages in a timely manner -- as it
- must.
-
-
-
- POST A MESSAGE, DO SOME WORK
-
- Thread one creates thread two, and thread two creates its own
- message queue for its object window. Then thread two blocks in
- WinGetMsg in the message loop (see OBJECT.C) until there is work
- to do.
-
- Scenario: the user selects a task from a pull-down menu. PM
- sends a menu message to the client window procedure on thread
- one. Thread one calls WinPostMsg and posts a user-defined
- message to the object window on thread two. Thread two performs
- the task in the object window procedure. When the task is
- complete, thread two posts an acknowledgement back to the
- originating window.
-
-
-
-
- client/dialog object window
- window on thread 1 on thread 2
- --------------- --------------- |
- | | | waiting in | |
- | user | WinPostMsg( | WinGetMsg | |
- | requests | hwndObject, | | |
- | a lengthy | WM_USER_WORKITEM, | | |
- | task | hwndToAck, | | |
- | | null ) | | |
- | | --------------------> | |
- | window | | perform | time
- | disabled | | lengthy | |
- | while | | task | |
- | busy | | | |
- | | WinPostMsg( | | |
- | | hwndToAck, | | |
- | | WM_USER_ACK, | | |
- | | WM_USER_WORKITEM, | task | |
- | | result code ) | complete | |
- | | <-------------------- | | |
- | enable | | | |
- | again | | | |
- --------------- --------------- |
- |
- \ /
- .
-
-
-
- There is a convention used with the two message parameters of
- WinPostMsg when posting a user-defined work message to the object
- window: message parameter one is the window handle of the
- originating window. By having the window handle of the
- originator, the object window will know which window to
- acknowledge upon completion of the task.
-
- The sample code in APP.C only shows the client window procedure
- originating tasks, but dialog boxes can certainly originate
- tasks, too.
-
-
-
-
-
- DISABLE WHEN BUSY
-
- In the sample code, the application disables its client window
- and selected menu items, then posts a message to the object
- window to perform the lengthy task. This prevents the user from
- initiating another work item while the object window is busy.
-
- Note: when the client window is disabled, its message queue does
- not stop working. Click on the client window while the object
- window is busy. The beep you hear is proof that the client
- window's message queue is processing messages as it should.
-
- The frame window is not disabled; therefore, the user can size or
- minimize the frame window and/or switch to another application
- while the application is busy.
-
- Notice that the mouse pointer changes to an hourglass when it
- passes over the window of the busy application. When the mouse
- pointer leaves the application window, it changes back to the
- normal pointer. The application keeps a busy flag and references
- it during processing of WM_MOUSEMOVE in the client window
- procedure.
-
- This is a simplistic (if not rigid) approach, but it is one that
- can be modified by the application programmer: the programmer
- chooses which items are grayed on which menus. If a given object
- window (there could be many) was responsible for tasks A and B,
- then the programmer would gray items A and B while the object
- window was busy working on either A or B.
-
-
-
-
-
-
-
-
-
-
-
-
-
- OBJECT WINDOW ACKNOWLEDGES COMPLETION
-
- Upon completion of the lengthy task, the object window posts a
- user acknowledgement message to the window that originated the
- task. This informs the originating window that the task is
- complete, and it can re-enable itself and menu items as required.
-
- There is a convention used with message parameters on the
- acknowledgement message: message parameter one is the
- user-defined message posted to the object window. With this
- parameter, the originating window can discern which activity is
- now complete. The second message parameter is a result code.
-
-
-
-
-
-
-
- TERMINATION CONSIDERATIONS
-
- The act of terminating (closing) the application is like a chain
- reaction between the two threads. Usually, client windows post
- themselves a quit message upon receipt of a close message. Not
- here!
-
- When this client window receives a close message, it posts a quit
- message to the object window, then returns. When the object
- window receives the quit message, it leaves the message loop,
- posts a quit message back to the client, cleans up, then exits.
- When the client window on thread one gets the quit message, it
- leaves its message loop, cleans up, waits for thread two to exit,
- then exits itself (and the process).
-
- Note that the object window thread calls WinCancelShutdown. This
- tells PM not to send a close message to the object window message
- queue if the user shuts down the system while the application is
- running. PM will send a close message to the client window
- message queue, and the chain reaction begins.
-
-
-
-
-
-
-
-
-
-
- COMMON DATA
-
- Both threads share a common data space. This space is defined by
- the GLOBALS structure in app.h. WM_CREATE processing allocates
- this space and passes the pointer to thread two on the call to
- _beginthread.
-
- The client and object window procedures keep a pointer to this
- memory in their window words. The number of extra words per
- window is set by the WinRegisterClass calls. In this sample,
- both the client and object windows have four extra bytes of
- window words by virtue of the last parameter to WinRegisterClass.
-
- Dialog box procedures must obtain the pointer to shared memory
- and store it in their window words when they initialize. By
- default, dialog boxes have enough window words to hold a
- 32-bits-long pointer.
-
-
-
-
-
- PMASSERT
-
- The pmassert macro is a debugging tool. It works much like the C
- language assert macro. Anywhere in the source code, the programmer
- can assert that a Boolean expression is true. At runtime, nothing
- happens if the expression is true. If false, the macro displays the
- failed assertion in a message box along with the line number and the C
- source file name where the assertion failed.
-
- Because pmassert is a macro, it is easy to redefine the macro to
- be a "no operation" once the application is debugged. In the C
- language tradition, this is accomplished by defining the symbol
- NDEBUG. This approach to program building yields "debug" and
- "retail" versions of your program. See pmassert.h in the sample
- code.
-
-
-
-
- SIMILARITIES TO OTHER SAMPLE PROGRAMS
-
- By now you may have recognized this architecture from the OS/2
- Toolkit Print Sample (PRTSAMP.EXE). It is the same.
-