home *** CD-ROM | disk | FTP | other *** search
/ Power GUI Programming with VisualAge C++ / powergui.iso / powergui / thread / threads / thread.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-10-29  |  18.0 KB  |  581 lines

  1. /***************************************************************
  2. * FILE NAME: thread.cpp                                        *
  3. *                                                              *
  4. * DESCRIPTION:                                                 *
  5. *   This file contains the implementation of                   *
  6. *   classes/functions declared in thread.hpp.                  *
  7. *                                                              *
  8. * COPYRIGHT:                                                   *
  9. *   Licensed Materials - Property of Solution Frameworks       *
  10. *   Copyright (C) 1996, Solution Frameworks                    *
  11. *   All Rights Reserved                                        *
  12. ***************************************************************/
  13. #include <iframe.hpp>
  14. #include <istattxt.hpp>
  15. #include <itime.hpp>
  16. #include <icnr.hpp>
  17. #include <ithread.hpp>
  18. #include <ipopmenu.hpp>
  19. #include <icmdevt.hpp>
  20. #include <ireslock.hpp>
  21. #include <icritsec.hpp>
  22. #include <istparse.hpp>
  23. #include <istattxt.hpp>
  24. #include <itimer.hpp>
  25.  
  26. #include "signal.hpp"
  27.  
  28. #include "threads.h"
  29.  
  30. #include "thread.hpp"
  31.  
  32. // Flag bits.
  33. static const unsigned long
  34.   isWaitingForPrivateResource = 1,
  35.   isWaitingForSharedResource  = 2,
  36.   isWaitingForPrivateSignal   = 4,
  37.   isWaitingForSharedSignal    = 8,
  38.   isWaiting                   = 15,
  39.   isSuspended                 = 16,
  40.   hasPrivateResource          = 32,
  41.   hasSharedResource           = 64,
  42.   isPrimary                   = 128;
  43.  
  44. // Style for secondary frames.
  45. static IFrameWindow::Style
  46.   frameStyle = IFrameWindow::titleBar 
  47.                  | IFrameWindow::sizingBorder;
  48.  
  49. // Hidden data members.
  50. struct ThreadData {
  51. // Minimal constructor.
  52.   ThreadData( IContainerControl &cnr )
  53.     : frame      ( title(), IC_DEFAULT_FRAME_ID, frameStyle ),
  54.       status     ( 1, &frame, &frame ),
  55.       counter    ( 2, &frame, &frame ),
  56.       rate       ( 3, &frame, &frame ),
  57.       container  ( cnr ),
  58.       flags      ( 0 ),
  59.       nextAction ( 0 ),
  60.       count      ( 0 ),
  61.       thread     ( 0 ) {
  62.  
  63.     status.setAlignment( IStaticText::centerCenter );
  64.     counter.setAlignment( IStaticText::centerCenter );
  65.     rate.setAlignment( IStaticText::centerCenter );
  66.     frame.setOwner( cnr.parent() );
  67.     frame.setClient( &counter );
  68.     frame.addExtension( &status, IFrameWindow::aboveClient, 25 );
  69.     frame.addExtension( &rate, IFrameWindow::aboveClient, 25 );
  70.     // Position each thread's window at origin of main window,
  71.     // offset a little for each thread.
  72.     IPoint
  73.       nextPos = cnr.parent()->rect().bottomLeft()
  74.                   + IPoint( 30, -30 ) * nextThreadNum;
  75.     frame.moveSizeTo( frame.rect().scaleBy( 0.5, 0.25  )
  76.                                   .centerAt( nextPos ) );
  77.     frame.setDestroyOnClose( false );
  78.     frame.show();
  79.  
  80.     nextThreadNum++;
  81.   }
  82.   ~ThreadData ( ) {
  83.     delete thread;
  84.   }
  85. // Returns the "name" of the next thread.
  86. static IString
  87.   name ( ) {
  88.     return IString( "Thread " ) + IString( nextThreadNum );
  89.   }
  90. // Returns the title of the next thread's frame window.
  91. static IString
  92.   title ( ) {
  93.     return IString( "Process " ) 
  94.            + 
  95.            IApplication::current().id().asString()
  96.            +
  97.            IString( ", " )
  98.            +
  99.            name();
  100.   }
  101. // Data members.
  102. IContainerControl
  103.  *cnr;
  104. IFrameWindow
  105.   frame;
  106. IStaticText
  107.  status,
  108.  counter,
  109.  rate;
  110. IContainerControl
  111.  &container;
  112. unsigned long
  113.   flags;
  114. unsigned long
  115.   nextAction,
  116.   count;
  117. ITime
  118.   startTime;
  119. IThread
  120.  *thread;
  121.  
  122. static unsigned
  123.   nextThreadNum;
  124.  
  125. ThreadData ( const ThreadData& );
  126. operator = ( const ThreadData& );
  127. }; // ThreadData
  128.  
  129. unsigned
  130.   ThreadData::nextThreadNum = 1;
  131.  
  132. /*----------------- Thread ContainerColumns --------------------
  133. | These static members are attached to container controls when |
  134. | you call addColumnsTo.                                       |
  135. --------------------------------------------------------------*/
  136. IContainerColumn
  137.   Thread::iconColumn         ( IContainerColumn::isIcon ),
  138.   Thread::nameColumn         ( IContainerColumn::isIconViewText ),
  139.   Thread::threadIdColumn     ( offsetof( Thread, threadId ) ),
  140.   Thread::statusColumn       ( offsetof( Thread, status ) ),
  141.   Thread::priorityClassColumn( offsetof( Thread, priorityClass ) ),
  142.   Thread::priorityLevelColumn( offsetof( Thread, priorityLevel ) );
  143.  
  144. /*---------------------- Thread::Thread ------------------------
  145. | The constructor requires the container control that the      |
  146. | thread is to be added to and a flag used to designate the    |
  147. | primary thread.                                              |                      
  148. --------------------------------------------------------------*/
  149. Thread :: Thread ( IContainerControl &cnr, Boolean primary )
  150.   : IContainerObject ( ThreadData::name(), 
  151.                        primary ? THREAD1 : THREADS ) {
  152.   // Create hidden data members.
  153.   data = new ThreadData( cnr );
  154.   // Start thread of execution (if necessary).
  155.   if ( primary ) {
  156.     data->flags |= isPrimary;
  157.     data->thread = new IThread( IThread::current() );
  158.     ITimer
  159.       timer( new ITimerMemberFn0<Thread>( *this, Thread::timerTick ) );
  160.   } else {
  161.     data->thread = new IThread( (IThread::OptlinkFnPtr)run, this );
  162.     // Secondary threads run at idle time (by default).
  163.     data->thread->setPriority( IApplication::idleTime, 15 );
  164.   }
  165.   // Refresh container column data.
  166.   this->threadId = data->thread->id().asString();
  167.   this->refreshInfo();
  168.   // Add container object to container and refresh it.
  169.   cnr.addObject( this ).refresh();
  170. }
  171.  
  172. /*--------------------- Thread::~Thread ------------------------
  173. | The destructor resumes the thread if it's suspended (to      |
  174. | ensure it cleans up properly).  If the thread is other       |
  175. | than the primary thread, it is stopped.                      |
  176. --------------------------------------------------------------*/
  177. Thread :: ~Thread ( ) {
  178.   // Resume any suspended threads.
  179.   if ( data->flags & isSuspended ) {
  180.     data->thread->resume();
  181.   }
  182.   // Kill secondary threads.
  183.   if ( !( data->flags & isPrimary ) ) {
  184.     data->thread->stop();
  185.   }
  186.   delete data;
  187. }
  188.  
  189. /*------------------- Thread::refreshInfo ----------------------
  190. | Set the container object fields to reflect the current       |
  191. | status of this thread.                                       |                      
  192. --------------------------------------------------------------*/
  193. void Thread :: refreshInfo ( ) {
  194.   if ( data->thread->isStarted() ) {
  195.     if ( data->flags & isSuspended ) {
  196.       this->status = "suspended";
  197.     } else if ( data->flags & isWaiting ) {
  198.       this->status = "waiting";
  199.     } else {
  200.       this->status = "running";
  201.     }
  202.     this->priorityClass = data->thread->priorityClass();
  203.     this->priorityLevel = data->thread->priorityLevel();
  204.   } else {
  205.     this->status = "stopped";
  206.   }
  207.   IString
  208.     statusLine = status;
  209.   if ( statusLine == "waiting" ) {
  210.     if ( data->flags & isWaitingForPrivateResource ) {
  211.       statusLine += " for private resource...";
  212.     } else if ( data->flags & isWaitingForSharedResource ) {
  213.       statusLine += " for shared resource...";
  214.     } else if ( data->flags & isWaitingForPrivateSignal ) {
  215.       statusLine += " for private signal...";
  216.     } else if ( data->flags & isWaitingForSharedSignal ) {
  217.       statusLine += " for shared signal...";
  218.     }
  219.   }
  220.   data->status.setText( statusLine );
  221. }
  222.  
  223. /*------------------- Thread::addColumnsTo ---------------------
  224. | Create this object's container columns (if necessary) and    |
  225. | add those columns to the details view of the argument        |
  226. | container.                                                   |
  227. --------------------------------------------------------------*/
  228. void Thread :: addColumnsTo ( IContainerControl &cnr ) {
  229.   static Boolean
  230.     doneAlready = false;
  231.  
  232.   if ( !doneAlready ) {
  233.     iconColumn.showSeparators();
  234.     nameColumn.showSeparators().setHeadingText( "Name" );
  235.     threadIdColumn.showSeparators().setHeadingText( "Id" );
  236.     statusColumn.showSeparators().setHeadingText( "Status" );
  237.     priorityClassColumn.showSeparators().setHeadingText( "Class" );
  238.     priorityLevelColumn.showSeparators().setHeadingText( "Level" );
  239.     doneAlready = true;
  240.   }
  241.  
  242.   cnr
  243.     .addColumn( &iconColumn )
  244.     .addColumn( &nameColumn )
  245.     .addColumn( &threadIdColumn )
  246.     .addColumn( &statusColumn )
  247.     .addColumn( &priorityClassColumn )
  248.     .addColumn( &priorityLevelColumn );
  249. }
  250.  
  251. /*------------------ Thread::makePopUpMenu ---------------------
  252. | Create a new pop up menu for this Thread.  We disable the    |    
  253. | items that don't make sense.                                 |    
  254. --------------------------------------------------------------*/
  255. Boolean Thread :: makePopUpMenu ( IMenuEvent &event ) {
  256.   Boolean
  257.     result = false;
  258.  
  259.   IPopUpMenu
  260.    *pMenu = new IPopUpMenu( THREAD_POPUP, event.window() );
  261.  
  262.   Boolean
  263.     running    = data->thread->isStarted(),
  264.     primary    = (Boolean)( data->flags & isPrimary ),
  265.     suspended  = (Boolean)( data->flags & isSuspended ),
  266.     waiting    = (Boolean)( data->flags & isWaiting ),
  267.     blocked    = (Boolean)( suspended | waiting ),
  268.     hasPrivate = (Boolean)( data->flags & hasPrivateResource ),
  269.     hasShared  = (Boolean)( data->flags & hasSharedResource );
  270.  
  271.   // If suspended, change menu option to "Resume".
  272.   if ( suspended ) {
  273.     pMenu->setText( THREAD_SUSPEND, THREAD_RESUME );
  274.   }
  275.  
  276.   pMenu->enableItem( THREAD_SHOW,
  277.                      true );
  278.   pMenu->enableItem( THREAD_SUSPEND,
  279.                      running && !primary );
  280.   pMenu->enableItem( THREAD_STOP,
  281.                      running && !primary );
  282.   pMenu->enableItem( THREAD_CRITSEC,
  283.                      running && !blocked );
  284.   pMenu->enableItem( THREAD_GETPRIVATE,
  285.                      running && !primary && !blocked && !hasPrivate );
  286.   pMenu->enableItem( THREAD_RELPRIVATE,
  287.                      running && !blocked && hasPrivate );
  288.   pMenu->enableItem( THREAD_GETSHARED,
  289.                      running && !primary && !blocked && !hasShared );
  290.   pMenu->enableItem( THREAD_RELSHARED,
  291.                      running && !blocked && hasShared );
  292.   pMenu->enableItem( THREAD_RESETPRIVATE,
  293.                      running && !blocked );
  294.   pMenu->enableItem( THREAD_SIGNALPRIVATE,
  295.                      running && !blocked );
  296.   pMenu->enableItem( THREAD_WAITPRIVATE,
  297.                      running && !primary && !blocked );
  298.   pMenu->enableItem( THREAD_RESETSHARED,
  299.                      running && !blocked );
  300.   pMenu->enableItem( THREAD_SIGNALSHARED,
  301.                      running && !blocked );
  302.   pMenu->enableItem( THREAD_WAITSHARED,
  303.                      running && !primary && !blocked );
  304.   pMenu->enableItem( THREAD_BOOST,
  305.                      running );
  306.   pMenu->enableItem( THREAD_REDUCE,
  307.                      running );
  308.   pMenu->enableItem( THREAD_IDLE,
  309.                      running );
  310.   pMenu->enableItem( THREAD_REGULAR,
  311.                      running );
  312.   pMenu->enableItem( THREAD_TIMECRIT,
  313.                      running );
  314.   pMenu->enableItem( THREAD_FORE,
  315.                      running );
  316.  
  317.   pMenu->setAutoDeleteObject();
  318.   pMenu->show( event.mousePosition() );
  319.   result = true;
  320.  
  321.   return result;
  322. }
  323.  
  324. /*------------------ Thread::handleCommand ---------------------
  325. | Implement the specified action.  Note that we always execute |
  326. | this function on the primary thread.  Actions that must be   |
  327. | executed on the secondary thread are posted to that thread.  |
  328. --------------------------------------------------------------*/
  329. Boolean Thread :: handleCommand ( ICommandEvent &cmd ) {
  330.   Boolean
  331.     result = false;
  332.   switch ( cmd.commandId() ) {
  333.     case THREAD_SHOW:
  334.       data->frame.setFocus();
  335.       break;
  336.  
  337.     case THREAD_SUSPEND:
  338.       if ( data->flags & isSuspended ) {
  339.         data->thread->resume();
  340.         data->flags = data->flags & ~isSuspended;
  341.       } else {
  342.         data->flags = (unsigned long)( data->flags | isSuspended );
  343.         data->thread->suspend();
  344.       }
  345.       result = true;
  346.       break;
  347.  
  348.     case THREAD_STOP:
  349.       data->thread->stop();
  350.       result = true;
  351.       break;
  352.  
  353.     case THREAD_BOOST:
  354.       data->thread->adjustPriority( +1 );
  355.       result = true;
  356.       break;
  357.  
  358.     case THREAD_REDUCE:
  359.       data->thread->adjustPriority( -1 );
  360.       result = true;
  361.       break;
  362.  
  363.     case THREAD_IDLE:
  364.       data->thread->setPriority( IApplication::idleTime, 15 );
  365.       result = true;
  366.       break;
  367.  
  368.     case THREAD_REGULAR:
  369.       data->thread->setPriority( IApplication::regular, 15 );
  370.       result = true;
  371.       break;
  372.  
  373.     case THREAD_TIMECRIT:
  374.       data->thread->setPriority( IApplication::timeCritical, 15 );
  375.       result = true;
  376.       break;
  377.  
  378.     case THREAD_FORE:
  379.       data->thread->setPriority( IApplication::foregroundServer, 15 );
  380.       result = true;
  381.       break;
  382.  
  383.     // Each of these actions is "posted" by setting the thread's
  384.     // "nextAction" data member.  This will be checked by the
  385.     // thread the next time it polls.
  386.     case THREAD_CRITSEC:
  387.     case THREAD_GETPRIVATE:
  388.     case THREAD_RELPRIVATE:
  389.     case THREAD_GETSHARED:
  390.     case THREAD_RELSHARED:
  391.     case THREAD_RESETPRIVATE:
  392.     case THREAD_SIGNALPRIVATE:
  393.     case THREAD_WAITPRIVATE:
  394.     case THREAD_RESETSHARED:
  395.     case THREAD_SIGNALSHARED:
  396.     case THREAD_WAITSHARED:
  397.       data->nextAction = cmd.commandId();
  398.       break;
  399.  
  400.     default:
  401.       break;
  402.   }
  403.   this->refreshInfo();
  404.   return result;
  405. }
  406.  
  407. /*------------------ Thread::performAction ---------------------
  408. | If the nextAction data member is non-zero, perform the       |    
  409. | requested action.  Otherwise, just increment counter         |
  410. | and update output fields.                                    |               
  411. --------------------------------------------------------------*/
  412. Boolean Thread :: performAction () {
  413.   static IPrivateResource
  414.     privateResource;
  415.   static ISharedResource
  416.     sharedResource( "MUTEX" );
  417.   static Signal
  418.     privateSignal,
  419.     sharedSignal( "SIGNAL" );
  420.  
  421.   Boolean
  422.     result = true;
  423.  
  424.   unsigned long
  425.     action = data->nextAction;
  426.   data->nextAction = 0;
  427.  
  428.   switch ( action ) {
  429.     case THREAD_STOP:
  430.       result = false;
  431.       break;
  432.     case THREAD_CRITSEC:
  433.       {
  434.       ICritSec
  435.         criticalSection();
  436.       IThread::current().sleep( 5000 );
  437.       }
  438.       break;
  439.     case THREAD_GETPRIVATE:
  440.       {
  441.       data->flags |= isWaitingForPrivateResource;
  442.       refreshInfo();
  443.       Boolean
  444.         gotIt = true;
  445.       // Wait 60 seconds for the private resource.
  446.       try { privateResource.lock( 60000 ); }
  447.       catch ( ... ) {
  448.         gotIt = false;
  449.       }
  450.       if ( gotIt ) {
  451.         data->flags |= hasPrivateResource;
  452.       }
  453.       data->flags = data->flags & ~isWaitingForPrivateResource; 
  454.       refreshInfo();
  455.       }
  456.       break;
  457.     case THREAD_RELPRIVATE:
  458.       {
  459.       privateResource.unlock();
  460.       data->flags = (Boolean)( data->flags & ~hasPrivateResource );
  461.       }
  462.       break;
  463.     case THREAD_GETSHARED:
  464.       {
  465.       data->flags |= isWaitingForSharedResource;
  466.       refreshInfo();
  467.       Boolean
  468.         gotIt = true;
  469.       // Wait 60 seconds for shared resource.
  470.       try { sharedResource.lock( 60000 ); }
  471.       catch ( ... ) {
  472.         gotIt = false;
  473.       }
  474.       if ( gotIt ) {
  475.         data->flags |= hasSharedResource;
  476.       }
  477.       data->flags = (Boolean)( data->flags & ~isWaitingForSharedResource );
  478.       refreshInfo();
  479.       }
  480.       break;
  481.     case THREAD_RELSHARED:
  482.       {
  483.       sharedResource.unlock();
  484.       data->flags = (Boolean)( data->flags & ~hasSharedResource );
  485.       }
  486.       break;
  487.     case THREAD_RESETPRIVATE:
  488.       {
  489.       privateSignal.reset();
  490.       }
  491.       break;
  492.     case THREAD_SIGNALPRIVATE:
  493.       {
  494.       privateSignal.signal();
  495.       }
  496.       break;
  497.     case THREAD_WAITPRIVATE:
  498.       {
  499.       data->flags |= isWaitingForPrivateSignal;
  500.       refreshInfo();
  501.       try { privateSignal.wait( 60000 ); }
  502.       catch ( ... ) {
  503.       }
  504.       data->flags = (Boolean)( data->flags & ~isWaitingForPrivateSignal );
  505.       refreshInfo();
  506.       }
  507.       break;
  508.     case THREAD_RESETSHARED:
  509.       {
  510.       sharedSignal.reset();
  511.       }
  512.       break;
  513.     case THREAD_SIGNALSHARED:
  514.       {
  515.       sharedSignal.signal();
  516.       }
  517.       break;
  518.     case THREAD_WAITSHARED:
  519.       {
  520.       data->flags |= isWaitingForSharedSignal;
  521.       refreshInfo();
  522.       try { sharedSignal.wait( 60000 ); }
  523.       catch ( ... ) {
  524.       }
  525.       data->flags = (Boolean)( data->flags & ~isWaitingForSharedSignal );
  526.       refreshInfo();
  527.       }
  528.       break;
  529.     default:
  530.       // Increment count and update text fields.
  531.       {
  532.       ITime
  533.         now;
  534.       double
  535.         rate = (double) data->count / 
  536.                (double)( ( now - data->startTime ).asSeconds() + 1 );
  537.       IString
  538.         whole,
  539.         fraction;
  540.       IString(rate) >> whole >> "." >> fraction;
  541.       data->counter.setText( IString( ++data->count ) );
  542.       data->rate.setText( IString( "Loops/second = " ) 
  543.                            + whole 
  544.                            + "."
  545.                            + fraction.leftJustify( 5, '0' ) );
  546.       }
  547.       break;
  548.   }
  549.   return result;
  550. }
  551.  
  552. /*-------------------- Thread::timerTick -----------------------
  553. | This gets called on the primary thread.  We simply call      |
  554. | performAction.                                               |
  555. --------------------------------------------------------------*/
  556. void Thread :: timerTick ( ) {
  557.   performAction();
  558. }
  559.  
  560. /*----------------------- Thread::run --------------------------
  561. | This is the function we run on all secondary threads.  It is |
  562. | passed a pointer to a Thread object as argument.  We simply  |
  563. | spin calling performAction until the thread is stopped.      |
  564. --------------------------------------------------------------*/
  565. void Thread :: run ( void *arg ) {
  566.   Thread
  567.    *thread = (Thread*)arg;
  568.  
  569.   Boolean
  570.     running = true;
  571.   while ( running ) {
  572.     try {
  573.       running = thread->performAction();
  574.     }
  575.     catch ( ... ) {
  576.       running = false;
  577.     }
  578.   }
  579.   // Delete ThreadData?
  580. }
  581.