home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / COOKBO.ZIP / COOKBOOK.TXT next >
Text File  |  1992-02-28  |  51KB  |  991 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18.                                OS/2 THREADS COOKBOOK                               _____________________
  19.  
  20.  
  21.  
  22.  
  23.  
  24.                                     Version 1.2
  25.  
  26.  
  27.  
  28.  
  29.  
  30.  
  31.  
  32.  
  33.  
  34.  
  35.  
  36.  
  37.  
  38.  
  39.  
  40.                                     Stephen Best
  41.                                    P.O. Box 3097
  42.                                 Manuka  A.C.T.  2603
  43.                                      Australia
  44.  
  45.                               Phone:    61-6-281-2147
  46.                               FidoNet:    3:620/243.4
  47.                               CompuServe:  100033,340
  48.  
  49.  
  50.  
  51.  
  52.  
  53.  
  54.  
  55.  
  56.  
  57.  
  58.  
  59.                        Copyright (c) 1991, 1992  Stephen Best
  60.  
  61.  
  62.  
  63.  
  64.  
  65.  
  66.  
  67.  
  68.  
  69.  
  70.  
  71.  
  72.  
  73.  
  74.             This document  is an attempt to collect together and share a
  75.             number of  my observations  and ideas  about programming for
  76.             OS/2 Presentation  Manager using  multiple threads that have
  77.             evolved over  time and  been gleaned (gratefully) from other
  78.             explorers in  this area. A thorough understanding of the use
  79.             of threads is essential for construction of all but the most
  80.             trivial Presentation  Manager programs  and it is hoped that
  81.             the ideas  contained herein  with be  of aid  to programmers
  82.             beginning to  tap into  the exciting  possibilities that the
  83.             use of multiple threads introduce.
  84.  
  85.  
  86.             If you  would like  the  full  C  source  for  the  examples
  87.             discussed herein,  please contact  me via FidoNet/CompuServe
  88.             or  at   the  address   given  with   your   Mastercard/Visa
  89.             particulars. The  cost is  $A45 (approx.  $US34)  with  free
  90.             transfer via  CompuServe. An additional $A10 will be charged
  91.             for postal  delivery if  required  (3.5  inch  media  only).
  92.             Payment entitles  the licensee  to use  the source  from the
  93.             examples in any programs of their own.
  94.  
  95.  
  96.             Also, if you have any comments at all regarding the material
  97.             contained herein, including errors and omissions, I would be
  98.             more than happy to hear of them.
  99.  
  100.  
  101.  
  102.             Stephen Best
  103.  
  104.             28 February, 1992
  105.  
  106.  
  107.  
  108.  
  109.  
  110.  
  111.  
  112.  
  113.  
  114.  
  115.  
  116.                                     Contents
  117.                                     ________
  118.  
  119.  
  120.  
  121.  
  122.  
  123.             Introduction                                               3
  124.  
  125.             What is a thread?                                          4
  126.  
  127.             Message queues                                             5
  128.  
  129.             Performance and restrictions                               7
  130.  
  131.             Managing threads                                           8
  132.  
  133.             DosCreateThread vs. _beginthread                          10
  134.  
  135.             Window data                                               11
  136.  
  137.             Example 1                                                 11
  138.  
  139.             Example 2                                                 13
  140.  
  141.             Other possibilities                                       14
  142.  
  143.             Conclusion                                                15
  144.  
  145.             References                                                16
  146.  
  147.  
  148.  
  149.             OS/2 Threads Cookbook                                page  3
  150.  
  151.  
  152.  
  153.             Introduction            ____________
  154.  
  155.             OS/2  as   a  single   user  system  has  the  potential  to
  156.             substantially change  the user's  perception  as  to  how  a
  157.             personal  computer  should  work.  Programs  using  multiple
  158.             threads can  not only  increase execution  performance (both
  159.             perceived and  actual) but also change the emphasis in user-
  160.             application interaction  to one  where  the  user  has  more
  161.             control and  flexibility and  where the  application  itself
  162.             takes on  a passive  role.  The  program  should  always  be
  163.             receptive to  interaction with the user even if this is just
  164.             the capability  for that  user to  change his/her mind after
  165.             initiating a lengthy activity.
  166.  
  167.             Users that  repeatedly tell  you that  "they don't  need  to
  168.             multitask" will have great difficulty in reverting to single
  169.             threaded software  after having  had the  luxury of  using a
  170.             well designed  and  responsive  multi-threaded  application.
  171.             Thus anyone wishing to compete in the market may have a hard
  172.             time selling  their product  in an increasingly aware public
  173.             arena. It  is also  hoped that  all programmers will want to
  174.             wring the  maximum result  from  an  environment  for  their
  175.             efforts, and I think multiple threads have the potential for
  176.             good returns in this area.
  177.  
  178.             It is  also true,  though not  universally appreciated, that
  179.             programming to  the  multi-threaded  model  has  significant
  180.             impact on the overall program design, and it is important to
  181.             have this  in mind  up front to avoid major restructuring of
  182.             the program at a later stage.
  183.  
  184.  
  185.             This document is aimed at the OS/2 programmer wishing to tap
  186.             into  the  power  that  programming  with  multiple  threads
  187.             provides. As such, I will attempt to cover all the essential
  188.             issues related to threads, a guide to where and when I think
  189.             threads are  applicable and some substantial coding examples
  190.             that I  think demonstrate  this. These examples are in C and
  191.             are for  OS/2 2.x,  though  conversion  to  other  languages
  192.             and/or OS/2  1.x  should  not  be  too  difficult  once  the
  193.             concepts are understood.
  194.  
  195.             All code has been tested with IBM OS/2 2.0 pre-release level
  196.             6.177 on  an IBM  PS/2 Model  80. The  IBM C Set/2 compiler,
  197.             linker and 6.177 toolkit headers were used.
  198.  
  199.  
  200.             It is  my belief  that practically  ALL  programs  for  OS/2
  201.             Presentation  Manager   will  benefit  from  using  multiple
  202.             threads in their design, and indeed have a responsibility to
  203.             do so  given  the  message  switching  architecture  of  PM.
  204.             Comments  (especially  those  from  sources  with  a  vested
  205.             interest in  promoting second  rate software  products) that
  206.  
  207.  
  208.  
  209.             OS/2 Threads Cookbook                                page  4
  210.  
  211.  
  212.             there is  only  a  minimal  requirement  for  multi-threaded
  213.             program design  should be  considered in  the light  of  the
  214.             immediate and  obvious benefits  that their  proper use  can
  215.             achieve.
  216.  
  217.             Polemics over, let's learn about OS/2 threads.
  218.  
  219.  
  220.             What is a thread?            _________________
  221.  
  222.             The thread is the basic level of execution under OS/2 and is
  223.             roughly equivalent  to the  task of other systems. A program
  224.             (or process)  has a  single thread  at the  beginning of its
  225.             execution and  can optionally  split the  activity  of  that
  226.             program over  a number  of threads. Each thread of execution
  227.             will be  time-sliced on  the processor (CPU) of the computer
  228.             together with  other threads  of that application, and those
  229.             of  other   applications  active  concurrently.  A  priority
  230.             mechanism exists  to ensure that the thread with the highest
  231.             priority is  always active,  with control  passing to  other
  232.             threads of  a lower  priority when the higher 'blocks' or is
  233.             waiting on an event. On top of this OS/2 has a sophisticated
  234.             scheduler to  dynamically alter  thread priority  to achieve
  235.             responsive overall  performance or  multitasking within  the
  236.             system.
  237.  
  238.             Note that splitting a single processor intensive task over a
  239.             number of threads does not in itself achieve anything as the
  240.             processor itself is a finite resource which cannot be driven
  241.             beyond its  capacity. Indeed the housekeeping in alternately
  242.             dispatching threads  may slow  down execution  in this case.
  243.             (It may  be worthwhile  though to keep in mind that a future
  244.             version  of   OS/2  may   well  support   multiple  physical
  245.             processors, and  the requirement  for dividing compute bound
  246.             tasks will change in this case).
  247.  
  248.             The  criteria   for  dividing  a  process  into  threads  as
  249.             discussed herein  is aimed  at isolating  the activity  of a
  250.             program by  either priority, functional units or access to a
  251.             resource.
  252.  
  253.             Consider an  application which  presents  the  user  with  a
  254.             number of  child windows  or 'views', of which only one (the
  255.             active window)  can receive the keyboard focus at a time. It
  256.             would in  this case  make sense  to  give  the  active/focus
  257.             window a  higher priority  than the  others,  if  concurrent
  258.             activity  in   other  windows   is  likely   to  impede  the
  259.             responsiveness of the one with which the user is interacting
  260.             at that time. This can be achieved quite easily by assigning
  261.             each window its own 'worker' thread and setting the priority
  262.             of the  active/focus window  thread higher  than that of its
  263.             siblings. In this case the thread for the active window will
  264.             receive the  processor resource  that  it  requires  without
  265.  
  266.  
  267.  
  268.             OS/2 Threads Cookbook                                page  5
  269.  
  270.  
  271.             interference from the other windows, aiding in the perceived
  272.             responsiveness of the application.
  273.  
  274.             Actual overall  efficiency can  be achieved  by  overlapping
  275.             processor intensive  tasks with  those for  input/output eg.
  276.             disk I/O.  OS/2, as  a true pre-emptive multitasking system,
  277.             can balance  the priorities  between processes  to  maximize
  278.             throughput but  it is  the application's  responsibility  to
  279.             separate within  itself lengthy  I/O  tasks  from  processor
  280.             intensive ones,  and especially  those likely  to  interfere
  281.             with servicing  of the  system message  queue (more  on this
  282.             important area  later). For  example, if  a user initiates a
  283.             lengthy file  open/save operation  or printing  activity  it
  284.             should be  possible to  interrupt this  activity if the user
  285.             changes his/her mind, or still interact with other facets of
  286.             the application  in parallel  with the I/O activity. Failure
  287.             to split  this activity off from the primary thread can even
  288.             inhibit the  user's ability  to switch  to another unrelated
  289.             application  on  the  desktop.  In  this  case,  it  may  be
  290.             advantageous to  spawn a  thread specifically  for servicing
  291.             the disk  or printer  asynchronously. The  main thread could
  292.             then off-load  such tasks  and 'queue'  them to a background
  293.             thread, and  get on  business of  interacting with the user.
  294.             Note that in this case it makes no sense to have a number of
  295.             threads for  a  single  resource  (like  a  printer)  as  no
  296.             efficiency is gained.
  297.  
  298.             It may  be helpful  to think of a one for one correspondence
  299.             between threads and 'resources' (be they windows or the disk
  300.             or a  printer), with  a 'master' thread interacting with the
  301.             user (and  hence the  system  message  queue).  It  is  this
  302.             concept of  resource based  threads that  will be  expounded
  303.             upon in the following.
  304.  
  305.  
  306.             Message queues            ______________
  307.  
  308.             Presentation Manager (among other GUI systems) has a message
  309.             switching architecture to facilitate the routing of messages
  310.             of different  types among  the 'windows'  that make  up  the
  311.             presentation layer  for OS/2.  An  application  can  receive
  312.             messages from the system eg. when a user attempts to re-size
  313.             a window, or can send messages to itself or other windows in
  314.             the system.  Messages can  either be  SENT (explicitly  with
  315.             WinSendMsg or  implicitly with  a large  number of other API
  316.             calls eg.  WinSetWindowText) or  POSTed  (with  WinPostMsg).
  317.             Sent messages  (and those  API calls  that  result  in  sent
  318.             messages) will  be turned  into direct  calls to  the window
  319.             procedure for  the window  specified  in  the  send.  Posted
  320.             messages  on   the  other   hand  will   be  queued  in  the
  321.             application's message queue for deferred execution.
  322.  
  323.             The application  message queue is created by the application
  324.             itself with  WinCreateMsgQueue and it is the act of doing so
  325.  
  326.  
  327.  
  328.             OS/2 Threads Cookbook                                page  6
  329.  
  330.  
  331.             that  distinguishes   that  application  as  a  Presentation
  332.             Manager one  (as opposed  to a  character  mode  application
  333.             executing in  its own session). An application may create as
  334.             many message  queues  as  desired  provided  that  only  one
  335.             message queue  exists for  each thread. Message queues other
  336.             than  the   primary  one   are  optional  in  multi-threaded
  337.             applications and  the following  examples  will  attempt  to
  338.             demonstrate where  multiple application message queues might
  339.             be applicable.
  340.  
  341.             Messages queued  on a  message queue  (by the  system or the
  342.             application itself) are un-queued with (generally) WinGetMsg
  343.             in a  message loop  and then  dispatched to  the appropriate
  344.             window procedure  with WinDispatchMsg.  This  WinDispatchMsg
  345.             can be  thought of as turning the POSTed message into a SEND
  346.             for immediate  execution. In  both cases,  the window handle
  347.             given specifies  the appropriate  window procedure  for that
  348.             message  ...  the  association  between  window  handle  and
  349.             procedure (for other than pre-registered classes) is made by
  350.             the application with the combination of WinRegisterClass and
  351.             WinCreate(Std)Window.
  352.  
  353.             The system message queue (of which there is only one for the
  354.             whole Presentation  Manager session)  is provided  to  queue
  355.             those 'messages'  that will  be subsequently  distributed to
  356.             the appropriate  application message  queue(s) at  such time
  357.             that the  context of  the message  can  be  determined.  The
  358.             primary consideration  here is user input (both keyboard and
  359.             mouse  actions)   that  may   occur  asynchronously  to  the
  360.             application message  flow. The  'problem' for PM programs is
  361.             that the  application processing  of any  message can itself
  362.             change the  destination  for  keyboard  and  mouse  messages
  363.             pending in  the system  message queue  (eg. explicitly  with
  364.             calls WinSetFocus or WinSetCapture) and thus it is only when
  365.             PM itself  regains control  from prior  messages that  it is
  366.             possible to  determine the  appropriate application queue in
  367.             which to  place the  keyboard or mouse message. In addition,
  368.             the program  is responsible  for processing messages dealing
  369.             with loss  of focus  and activation before other windows can
  370.             be activated.  The implication  for a  PM program is that it
  371.             should always be available for processing user input events,
  372.             and process all incoming messages quickly.
  373.  
  374.             The methodologies  discussed in  this document  are aimed at
  375.             off-loading the  bulk of  the processing requirement for the
  376.             application from  the 'input'  message thread to other 'non-
  377.             input' threads,  making the  application always receptive to
  378.             user input,  and thus  increasing the  responsiveness of the
  379.             application and  the system as a whole. It may be helpful to
  380.             consider that  serialization of  keyboard and mouse messages
  381.             in the  system queue  is not  so  much  a  'problem'  to  be
  382.             overcome with  adding threads,  but that  the  main  'input'
  383.             thread of  the application  is just  a vehicle for receiving
  384.  
  385.  
  386.  
  387.             OS/2 Threads Cookbook                                page  7
  388.  
  389.  
  390.             input from  the system  and like all shared resources, to be
  391.             treated accordingly.
  392.  
  393.  
  394.             Performance and restrictions            ____________________________
  395.  
  396.             Sent messages  can be  processed faster than posted messages
  397.             because they  never appear  in  the  message  queue  of  the
  398.             application and  thus avoid the message loop altogether. The
  399.             throughput of  inter thread posts will be slower still. This
  400.             is not  to say that posts should be avoided, but that it may
  401.             be desirable  to use  sends rather  than posts when an clear
  402.             choice exists between the two. Sends also have the guarantee
  403.             that any  dynamic  memory  area  addressed  by  the  message
  404.             parameter(s) will  remain current  for the life of the send,
  405.             which is  a benefit  if  more  data  than  the  eight  bytes
  406.             permitted with  the two 32 bit message parameters themselves
  407.             is required.  As a bonus, the return code from the receiving
  408.             window procedure  method is available upon completion of the
  409.             send. Sends  (because they  are translated into calls to the
  410.             window procedure)  will cause  the window procedure(s) to be
  411.             called recursively,  and thus may place excessive demands on
  412.             the program stack with high levels of recursion.
  413.  
  414.             Posts on  the other  hand,  because  of  their  asynchronous
  415.             nature will be serialized in the message queue and processed
  416.             when the  application itself enters message loop processing.
  417.             This means  that  any  dynamic  data  addressed  by  message
  418.             parameters when  the post was issued may no longer be valid.
  419.             This consideration requires a number of differing techniques
  420.             to transfer more data than the message parameters themselves
  421.             permit. Another  important point to note about posts is that
  422.             the message  may not  actually be  posted should the message
  423.             queue be  full at the time of the post. The return code from
  424.             WinPostMsg should  thus be checked to see if the post was in
  425.             fact accepted  and implementing  a  delayed  retry  or  some
  426.             pacing algorithm  to ensure the message is not lost. Despite
  427.             the above,  posts will play a big part in the interaction of
  428.             and communication  between threads  and thus  the techniques
  429.             for  achieving   efficient  and  reliable  use  of  them  is
  430.             presented herein.
  431.  
  432.             Another difference between sends and posts is the context in
  433.             which it is valid to issue them. Posts can be issued without
  434.             restriction between  threads and  will appear in the message
  435.             queue of  the thread with which the window addressed (by the
  436.             window  handle   specified)  was  created.  A  variation  on
  437.             WinPostMsg  is  WinPostQueueMsg  where  the  handle  of  the
  438.             message queue  itself is  specified instead  of  the  window
  439.             handle. This  permits an  application to  queue messages  to
  440.             another thread  (assuming the  receiving thread  has created
  441.             its own  message queue)  when no actual window procedure may
  442.             exist for  that thread.  This variation  will be explored in
  443.             one of the following examples.
  444.  
  445.  
  446.  
  447.             OS/2 Threads Cookbook                                page  8
  448.  
  449.  
  450.  
  451.             Sends on  the other  hand can only be issued between threads
  452.             each having  a message  queue,  and  for  reasons  following
  453.             should be  avoided for  anything  other  than  intra  thread
  454.             communications. Firstly,  sends to  a window  created  on  a
  455.             different thread  than that  from which  the send  is issued
  456.             will still  execute in  the context  of that window's thread
  457.             and thus may incur a performance penalty due to the overhead
  458.             involved in  the required  thread switch. In addition, inter
  459.             thread sends  (and API  calls that  result in sends to other
  460.             threads) may  result in  a  deadlock  situation  should  the
  461.             receiving thread  be waiting (using say a semaphore) on some
  462.             event from  the calling  thread at  the  time  the  send  is
  463.             issued. (Note  that WinMsgMuxSemWait  exists specifically to
  464.             avoid this  deadlock situation.)  The temptation  may be  to
  465.             think that  creating windows  each on  separate threads will
  466.             permit extensive  processing without  interference with  the
  467.             overall message  flow, but  it must  be remembered  that all
  468.             threads that create a (non object) window are subject to the
  469.             same input  restrictions discussed  above. It  is because of
  470.             these reasons  that I propose creation of all windows on the
  471.             initial thread  and exclusive  use of posts for inter thread
  472.             communications in this document.
  473.  
  474.             The above brings up the important concept of distribution of
  475.             responsibilities within the application. The model I use and
  476.             propound herein  is that  the main  (initial) thread be used
  477.             almost exclusively  for window  'management'. Thus  ALL (non
  478.             object) windows  will be created (or 'owned') by this thread
  479.             and  any  activity  likely  to  involve  more  than  minimal
  480.             processing off-loaded  to non-window  'service' threads. The
  481.             main thread  (simply because  of the fact that this is where
  482.             the windows  were created)  will be  the sole 'input' thread
  483.             subject to the keyboard/mouse message restrictions discussed
  484.             above. All  other threads  can  thus  undertake  substantial
  485.             processing  tasks   (or   waits)   without   impacting   the
  486.             application's  ability   to  appear   responsive   to   user
  487.             interaction.   Using    this   demarcation   of   processing
  488.             responsibility, it  is unlikely  that the  problem of  using
  489.             inter thread sends will arise.
  490.  
  491.  
  492.             Managing threads            ________________
  493.  
  494.             Threads (over  and above  the initial one allocated when the
  495.             program  begins   execution)  are  created  explicitly  with
  496.             DosCreateThread.  Each   thread  will  have  its  own  stack
  497.             (allocated and committed dynamically with 2.x) but share all
  498.             code and  data areas  of the parent process. Optionally a 32
  499.             bit parameter  can be  passed to the thread at this time and
  500.             this is  normally used to address thread initialization data
  501.             (or 'thread parameters'). A thread so created will exist for
  502.             the life  of  the  program  execution  (process)  unless  it
  503.  
  504.  
  505.  
  506.             OS/2 Threads Cookbook                                page  9
  507.  
  508.  
  509.             terminates  itself  by  'returning'  or  making  a  call  to
  510.             _endthread or DosExit (with EXIT_THREAD).
  511.  
  512.             Due to  any overhead  in creating/destroying  a thread it is
  513.             normal to  have the  thread life tied to the 'owning' window
  514.             or dialog box or failing that, the entire process. There are
  515.             no rules  as to  how many  threads should  be created in the
  516.             'average' program  as this  will be governed by the activity
  517.             and resource  requirements of  each. One way of deciding the
  518.             number (and more importantly, function) of threads to create
  519.             is to  consider how  many of the elements of the program you
  520.             would like  to run in parallel. Thus a program which creates
  521.             a  number   of  windows  (all  of  which  require  extensive
  522.             graphics) plus provides for background printing may create a
  523.             thread for  each window,  with  another  for  servicing  the
  524.             printer queue. Or maybe all the windows could share a single
  525.             drawing thread  if the  processing requirements are smaller.
  526.             The final  consideration of thread numbers and function will
  527.             depend on  both the degree of interactivity and visible feel
  528.             the programmer  wishes to  create with  the program  and how
  529.             logically functions  are isolated  internally in the program
  530.             itself. (Realistically,  the same end result may be achieved
  531.             by creating  only a  single 'service'  thread in addition to
  532.             the main  thread and  alternately  allocating  time  to  the
  533.             respective resources,  but maintaining  the desired  balance
  534.             may require  duplicating the  function of the OS/2 scheduler
  535.             itself, and hence be self defeating.)
  536.  
  537.             A  number  of  other  API  calls  are  related  to  threads.
  538.             DosWaitThread (new  with  2.x)  allows  the  thread  'owner'
  539.             (actually any  thread) to wait until the specified thread is
  540.             terminated and  thus can  be used  when the  owner itself is
  541.             being destroyed  for clean-up  operations.  DosSuspendThread
  542.             and DosResumeThread allow another thread to temporarily halt
  543.             execution of the specified thread, and resume operation at a
  544.             later time.  Due to  the fact  that it  will probably not be
  545.             possible to  predict the  exact stage  of operation  of  the
  546.             specified thread,  these calls  may not  prove  to  be  that
  547.             useful, and  indeed a  similar effect  can  be  achieved  by
  548.             resetting that thread's priority. DosSetPriority can be used
  549.             to modify  a thread's  priority, or to place the thread in a
  550.             different dispatching  class. DosKillThread  (also new  with
  551.             2.x) can  be used  to terminate secondary threads but at the
  552.             risk of  leaving allocated  resources used  by that  thread.
  553.             DosEnterCritSec  and   DosExitCritSec   can   be   used   to
  554.             temporarily disallow  execution of  all other threads in the
  555.             process when  serialized access  to a  resource of some type
  556.             must be  guaranteed,  and  using  mutex  semaphores  is  not
  557.             appropriate. Finally,  DosSleep can  be used  by a thread to
  558.             surrender the  remainder of its dispatching time slice or to
  559.             delay execution for a specified amount of time.
  560.  
  561.             In addition  to the  above, OS/2  has a  rich set  of inter-
  562.             process communication  facilities, such  as  semaphores  and
  563.  
  564.  
  565.  
  566.             OS/2 Threads Cookbook                               page  10
  567.  
  568.  
  569.             pipes which  may be used for thread control and transferring
  570.             data between threads.
  571.  
  572.  
  573.             DosCreateThread vs. _beginthread            ________________________________
  574.  
  575.             No paper  on OS/2  threads  programming  would  be  complete
  576.             without a  discussion on  the differences between the use of
  577.             the API  function  DosCreateThread  and  the  replacement  C
  578.             compiler run-time extension _beginthread.
  579.  
  580.             The problem  with using  DosCreateThread in  a C  program is
  581.             that a  number of  C run-time  library and  inline functions
  582.             assume a  single instance  of internal  static variables and
  583.             the behaviour  of the  program may  be undefined  when  this
  584.             common data is accessed by two or more threads concurrently.
  585.             Such functions  include malloc/free,  strtok and  rand.  The
  586.             standard  malloc/free   functions,  for   example,    assume
  587.             unrestricted  access   to  the   heap   management   control
  588.             information and  corruption may  occur  if  access  to  this
  589.             information is  preempted  by  a  second  thread  requesting
  590.             access to  the same data. The strtok and rand functions both
  591.             save their  current state  between calls which may result in
  592.             indeterministic behaviour  due  to  dynamics  in  access  of
  593.             threads to the previous state.
  594.  
  595.             The solution  adopted by  a number of vendors of C compilers
  596.             has been  to prevent  these undesirable  effects  by  either
  597.             serializing access  to such  data that  must be  shared,  or
  598.             providing an individual instance of the data for each thread
  599.             created. This  is achieved  firstly by  performing some run-
  600.             time  initialization  of  localized  thread  variables  with
  601.             _beginthread prior to invoking the DosCreateThread function.
  602.             Secondly, a  number of  run-time functions  are modified  to
  603.             either access these local variables or request serialization
  604.             (with DosRequestMutexSem  or DosEnterCritSec)  when the data
  605.             must be  shared.  To  the  programmer,  such  management  is
  606.             transparent provided  that the _beginthread function is used
  607.             exclusively and  the program  is linked with the appropriate
  608.             multi-threading run-time library.
  609.  
  610.             An alternative solution to the above approach is to restrict
  611.             a program's  use of  functions to  those  documented  to  be
  612.             reentrant. True  reentrant routines  will use  a stack-based
  613.             local copy  of any  data (where required) and thus avoid any
  614.             contention from other threads as each has its own individual
  615.             stack. The  IBM C Set/2 Subsystem run-time library (with the
  616.             heap  management   functions  replaced   with  use  of  OS/2
  617.             suballocation routines)  may well  support this alternative.
  618.             Such may  be desired  to minimize  the run-time  overhead in
  619.             providing contention support when none is desired.
  620.  
  621.  
  622.  
  623.             OS/2 Threads Cookbook                               page  11
  624.  
  625.  
  626.             Both examples below use _beginthread for creation of threads
  627.             and are  compiled with the multi-threading switch and linked
  628.             with the supporting run-time library.
  629.  
  630.  
  631.             Window data            ___________
  632.  
  633.             Each window  procedure associated  with a  window class will
  634.             have some  data to  be retained over the life of the window,
  635.             or between processing of messages. This 'static' data can be
  636.             initialized when the window procedure receives its WM_CREATE
  637.             or WM_INITDLG  message and  updated depending  on subsequent
  638.             message flow.  It is  common practise to place such 'static'
  639.             data in a dynamically allocated area of memory and have this
  640.             addressed by  a  window  'pointer'.  Thus  an  area  of  the
  641.             appropriate size  will be  allocated (with  malloc) when the
  642.             window is  created and  the address  of this area saved in a
  643.             window 'word' with WinSetWindowPtr. The address of this area
  644.             will be  retrieved with  WinQueryWindowPtr immediately prior
  645.             to processing  of all other messages for the window, and the
  646.             memory  area   disposed  of   (with  free)   in   WM_DESTROY
  647.             processing. Thus  if multiple  'instances' of the window are
  648.             created, each  window can be assured of integrity of its own
  649.             data. This  can have  an added benefit in reducing the total
  650.             EXE file  size, and more importantly promotes what I believe
  651.             to be a good 'object oriented' programming style. Though not
  652.             directly related  to using  threads,  the  concept  of  data
  653.             encapsulation  will  be  rigidly  exploited  in  the  coding
  654.             examples contained herein.
  655.  
  656.  
  657.             Example 1            _________
  658.  
  659.             The first example below is the complete window procedure for
  660.             a file  search dialog.  This dialog provides the user with a
  661.             means to  search a  number of disks for a specified file, or
  662.             ones matching the given 'mask' criteria. The user enters the
  663.             desired file name (with or without free characters), selects
  664.             a number  of disks  and presses the 'start' button. Once the
  665.             search is initiated, the 'start' button changes its function
  666.             to 'stop' to enable the user to interrupt the active search.
  667.             As files are found that match the search criteria, they will
  668.             be added  to a  list box  which can be scrolled and an entry
  669.             selected even  though the  search is  still active, enabling
  670.             the user  to exit with the selected file without waiting for
  671.             the search  to complete.  The 'stop'  button reverts  to its
  672.             'start' function  when the  search is  complete. Whilst this
  673.             search is  in progress,  the user can move the dialog window
  674.             or interact with other applications on the desktop.
  675.  
  676.             The virtue  of using  a separate  thread for  this  type  of
  677.             dialog is  that the  I/O intensive  logic for  scanning  the
  678.             directory list(s)  for the specified files can be segregated
  679.             from that  of interacting  with the  user. The end result is
  680.  
  681.  
  682.  
  683.             OS/2 Threads Cookbook                               page  12
  684.  
  685.  
  686.             that maximum  flexibility of interaction is achieved without
  687.             impacting the speed of the actual search.
  688.  
  689.  
  690.             This dialog  window procedure  creates the  search thread in
  691.             the WM_INITDLG  processing  and  terminates  the  thread  in
  692.             WM_DESTROY, thus  the thread  exists for  the  life  of  the
  693.             dialog session.  The search  thread issues a mux wait on two
  694.             event semaphores: a 'trigger' to initiate a new search and a
  695.             'terminate' event  to signal  thread termination.  Once  the
  696.             search is  active, it  can be  interrupted  by  setting  the
  697.             fInterrupt flag  TRUE, and this flag is checked periodically
  698.             in the search process.
  699.  
  700.             As files  are found  that match  the specified criteria, the
  701.             search  thread   posts  a  UM_SEARCHUPDATE  message  to  the
  702.             'owning' thread  to signal  that the  found entry  should be
  703.             added to  the list  box. In  this case,  we cannot  use  the
  704.             message parameters  on the post to fully contain the data to
  705.             be transferred  as the  file name length clearly exceeds the
  706.             eight bytes available. What has been done in this example is
  707.             to use  a simplified  form of  circular buffer, with an 'in'
  708.             and 'out'  count. Thus  entries can  be added  to the buffer
  709.             when the  'in' count  does not exceed the 'out' count by the
  710.             total number  of entries  in the  buffer, otherwise we would
  711.             overlay data  that had  not  been  accepted  by  the  owning
  712.             thread. As  the buffer  and counters  are accessible by both
  713.             threads, all that is required is to signal the owning thread
  714.             that new  data has  been added  to the  list and  should  be
  715.             processed. This  is done here by equating UM_SEARCHUPDATE to
  716.             WM_SEM2 and  using message  parameter 1  as a progress flag,
  717.             with TRUE indicating completion of the search. The WM_SEM1-4
  718.             messages are special in that the messages are not stacked in
  719.             the message queue, but accumulated into one message with the
  720.             message parameter  1 seen  by the  recipient being the OR'ed
  721.             result from  all the  messages  parameters  posted.  WM_SEM2
  722.             (rather than  WM_SEM1) was  selected as the priority of this
  723.             message is  lower than  that of keyboard/mouse messages thus
  724.             avoiding any  impact on user interaction whilst transferring
  725.             data. (If you move the mouse pointer around rapidly you will
  726.             notice that the search will slow down.)
  727.  
  728.             A few  other observations  on this  example. Because  of the
  729.             nature of the WM_SEMx messages, there is no risk of flooding
  730.             the application  message queue  (and hence losing a post) in
  731.             that there can be only one message of this type in the queue
  732.             at any  time. Also,  it is  likely that  a number  of  found
  733.             entries can  be transferred  for each  post the  main thread
  734.             sees, hence improving the efficiency of the transfer. If the
  735.             circular buffer  is full  (indicated by  the  value  of  the
  736.             difference  in   the  counters)  the  search  thread  issues
  737.             DosSleep to  surrender the remainder of its dispatching time
  738.             slice and  thus allowing  the main  thread  to  process  the
  739.             queued entries and free up the slots required.
  740.  
  741.  
  742.  
  743.             OS/2 Threads Cookbook                               page  13
  744.  
  745.  
  746.  
  747.             Another  important   element  is   that  the  dialog  window
  748.             procedure  has   been  structured  to  not  have  to  depend
  749.             synchronously on  the action  of the search thread, allowing
  750.             the search to be interrupted and end without the main thread
  751.             logic having  to issue  a wait.  If it  is possible to avoid
  752.             such waits,  an extra  level of semaphore handshaking can be
  753.             omitted.
  754.  
  755.  
  756.             Example 2            _________
  757.  
  758.             The second  example is a window procedure (together with its
  759.             'service'  thread)   for  utilizing   'shadow'  bitmaps   to
  760.             facilitate fast  paints and  to off-load  the  bulk  of  the
  761.             processing requirement  to a  'non input'  thread. A  shadow
  762.             bitmap (as  used in  this example)  is the  context for  the
  763.             drawing operations  which can  proceed offline from the main
  764.             window procedure  and be  quickly transferred  to the window
  765.             context  with   GpiBitBlt  in   the  WM_PAINT  method.  This
  766.             implementation is  ideal when an application can present the
  767.             completed drawing,  rather than show the drawing activity in
  768.             progress. Also,  if the destination window is to be restored
  769.             (eg. after  being covered  by another)  a subsequent call to
  770.             the processor intensive graphics functions is avoided.
  771.  
  772.             This example  differs from  the first  in that  the  service
  773.             thread allocates  its own  message queue, and communications
  774.             between  threads   is  achieved   with  posts  (rather  than
  775.             semaphores). Thus,  a  request  for  some  activity  can  be
  776.             'queued' to  the service  thread (with  WinPostQueueMsg)  by
  777.             specifying the handle of the message queue itself. Note that
  778.             WinPostMsg could  not be  used in  this case  as the service
  779.             thread has  not actually  created any  windows and  hence no
  780.             window handle  exists to  enable PM to determine which queue
  781.             is applicable.  The service  thread has its own message loop
  782.             to un-queue the posted requests and route to the appropriate
  783.             logic based on message ID, and in this sense is no different
  784.             from  a  normal  window  procedure.  When  the  activity  is
  785.             complete, the  service thread  posts a completion message to
  786.             the 'owning'  thread to  trigger the appropriate action (eg.
  787.             paint). Lastly,  the service thread is terminated by posting
  788.             WM_QUIT to  its message  queue  which  causes  the  loop  to
  789.             terminate.
  790.  
  791.             The service  thread in  this example  exists for the life of
  792.             its 'owning'  window, created in WM_CREATE and terminated in
  793.             WM_DESTROY. As  the main  procedure  must  insure  that  the
  794.             service thread's  message queue is valid, a semaphore is set
  795.             by the  service thread when the queue handle is available to
  796.             its owner.
  797.  
  798.             If multiple instances of this window are required, each will
  799.             have its  own service  thread and  this enables  a  priority
  800.  
  801.  
  802.  
  803.             OS/2 Threads Cookbook                               page  14
  804.  
  805.  
  806.             mechanism to  exist to ensure that the active window will be
  807.             drawn before  other, non-active windows. This is achieved in
  808.             this example  buy raising  or lowering  the  service  thread
  809.             priority  (in  WM_ACTIVATE)  so  that  the  active  window's
  810.             priority is  always higher  that its  siblings. The priority
  811.             mechanism is  absolute in  that the  service thread  for the
  812.             active window  must 'block'  (in WinGetMsg) before the other
  813.             windows will  receive any  processor resource.  Note that as
  814.             implemented in  this example this set priority will still be
  815.             lower than  that of  the main  'input' thread  to reduce any
  816.             interference with desktop operations.
  817.  
  818.             When using  this message  queue technique, it is possible to
  819.             optionally check  for pending  messages posted  in the queue
  820.             with a  call to WinQueryQueueStatus. In this example, as all
  821.             output posts  from the  service thread  are the  same,  some
  822.             processing may be saved if processing of the current message
  823.             is aborted  in favour  of pending messages of the same type.
  824.             This should only be attempted when it can be quaranteed that
  825.             the sequence of incoming messages is not disturbed.
  826.  
  827.             This example  has been  structured so  that the  main window
  828.             thread never  has to  explicitly wait  for completion  of  a
  829.             posted task  (other than for thread termination and recovery
  830.             from  failed   posts).  If   serialization   is   necessary,
  831.             semaphores 'posted'  by the  service thread  can be  used to
  832.             delay  execution  until  desired.  Alternatively,  the  main
  833.             thread can  wait for  a posted  completion message  by using
  834.             WinGetMsg  and  specifying  the  message  identity.  In  the
  835.             example given
  836.  
  837.                WinGetMsg (pw->hab, &qmsg, (HWND) hwnd,
  838.                         UM_WINDOWUPDATE, UM_WINDOWUPDATE);
  839.  
  840.             would delay  the main thread until the requested service was
  841.             complete. Note that either of the above (using semaphores or
  842.             waiting for  completion messages) issued from the main input
  843.             thread will  have the  effect of  stopping flow  in the main
  844.             message queue  of the  program, and  delay incoming keyboard
  845.             and mouse  messages system-wide  (as discussed  above).  The
  846.             goal should  thus be  to structure  the program so that such
  847.             serialized dependencies are minimized (or ideally avoided).
  848.  
  849.  
  850.             Other possibilities            ___________________
  851.  
  852.             The  above   two  examples   represent  a   sample  of   the
  853.             possibilities of  managing program  activity  with  multiple
  854.             threads. A  number of  other methodologies  exist which  may
  855.             prove applicable to different program requirements.
  856.  
  857.             A variation  on the  shadow bitmap  example above is to give
  858.             drawing control  of  the  window  presentation  space  to  a
  859.             service  thread.  This  has  the  similar  benefit  in  that
  860.  
  861.  
  862.  
  863.             OS/2 Threads Cookbook                               page  15
  864.  
  865.  
  866.             processor intensive  graphics functions can be off-line from
  867.             the main  input thread  with the  bonus that the application
  868.             user can  see the  drawing in progress, rather than wait for
  869.             the shadow  bitmap to  be completed.  To do this the program
  870.             would (probably in WM_CREATE) associate a presentation space
  871.             to the  window context  with WinOpenWindowDC and GpiCreatePS
  872.             and pass  this presentation  space  handle  to  the  drawing
  873.             thread. The  drawing thread would thus receive requests from
  874.             the main  thread and  invoke the graphics functions required
  875.             to draw  directly upon  the window  presentation space. Some
  876.             provision may  need to  be made for retaining the results of
  877.             the drawing  activity should  a full  or partial re-paint be
  878.             required due to window sizing or restoral.
  879.  
  880.             An extension  of using  threads with their own message queue
  881.             is to  create object  windows (windows created with a parent
  882.             of HWND_OBJECT).  Activity in  such 'windows'  is  initiated
  883.             with WinPostMsg  as the object window handle is specified to
  884.             identify the  appropriate message queue and window procedure
  885.             for that window. In all other respects, this is identical to
  886.             the message  queue example  above. The use of object windows
  887.             may be  applicable when  a thread exists to support a number
  888.             of resources and no overlap in processing is required.
  889.  
  890.  
  891.             Conclusion            __________
  892.  
  893.             It is  hoped that  by now  the  reader  has  understood  the
  894.             fundamentals of  why multiple threads are applicable to OS/2
  895.             Presentation Manager  programs  for  improving  the  overall
  896.             responsiveness of  the desktop dictated by the message queue
  897.             architecture, and  the implications  for presentation  of  a
  898.             flexible  user-application   interface.  The   existence  of
  899.             threads in  OS/2 provides  the application  designer with  a
  900.             rich set  of techniques  to distribute  function within  the
  901.             program itself  and co-ordinate  activity. The  goal of  the
  902.             application designer should be to identify opportunities for
  903.             parallel operation,  and  to  build  the  program  with  the
  904.             appropriate threads  to achieve  this, whilst  allowing  the
  905.             user to interrupt or abort any lengthy activity in progress.
  906.  
  907.             Multiple  threads,  I  feel,  offer  the  means  to  totally
  908.             transform a  user's expectation  of  how  personal  computer
  909.             software should  work and  hopefully this document will help
  910.             bring about  this new  age of  more responsive  and flexible
  911.             software.
  912.  
  913.  
  914.  
  915.             OS/2 Threads Cookbook                               page  16
  916.  
  917.  
  918.  
  919.             References            __________
  920.  
  921.             The following  references may  be useful  in  expanding  the
  922.             reader's understanding  of OS/2  multi-threading  techniques
  923.             and possibilities  as  applicable  to  Presentation  Manager
  924.             programming:
  925.  
  926.  
  927.  
  928.             Utilizing OS/2 Multithread Techniques in Presentation            _____________________________________________________
  929.             Manager Applications, Charles Petzold            ____________________
  930.  
  931.             Microsoft Systems Journal Vol. 3 No. 2
  932.  
  933.  
  934.             Planning and Writing a Multithreaded OS/2 Program with            ______________________________________________________
  935.             Microsoft C, Richard Hale Shaw            ___________
  936.  
  937.             Microsoft Systems Journal Vol. 4 No. 2
  938.  
  939.  
  940.             OS/2 PM Programming: A Performance Guide, P.G. Toghill            ________________________________________
  941.  
  942.             IBM Personal Systems Developer, Winter 1991
  943.  
  944.  
  945.             A Multithread CPU Monitor, Marc Cohen            _________________________
  946.  
  947.             OS/2 Notebook, The Best of the IBM Personal Systems
  948.             Developer, Microsoft Press
  949.  
  950.  
  951.             Programming for Multithreaded Drawing, Charles Petzold            _____________________________________
  952.  
  953.             PC Magazine, Vol. 9 Nos. 10-12
  954.  
  955.  
  956.             Programming the OS/2 Presentation Manager, Charles Petzold            _________________________________________
  957.  
  958.             Microsoft Press
  959.  
  960.  
  961.             Inside OS/2, Gordon Letwin            ___________
  962.  
  963.             Microsoft Press
  964.  
  965.  
  966.             Microsoft OS/2 Programmer's Reference Vol. 1            ____________________________________________
  967.  
  968.             Microsoft Press
  969.  
  970.  
  971.  
  972.             OS/2 Threads Cookbook                               page  17
  973.  
  974.  
  975.  
  976.  
  977.  
  978.             Programming Guide            _________________
  979.  
  980.             IBM OS/2 Programming Tools and Information, Version 1.2
  981.  
  982.  
  983.             The Design of OS/2, H.M. Deitel and M.S. Kogan            __________________
  984.  
  985.             Addison-Wesley
  986.  
  987.  
  988.             IBM C Set/2 User's Guide            ________________________
  989.  
  990.             IBM Publication number S10G-4444-0
  991.