Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
ThreadedProgress.c
/* |
File: ThreadedProgress.c |
Contains: Progress bar implementation using the Thread Manager |
Written by: Chris White |
Copyright: Copyright © 1996-1999 by Apple Computer, Inc., All Rights Reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source code" after having made |
changes. If you're going to re-distribute the source, we require that you make |
it clear in the source that the code was descended from Apple sample source |
code, but that you've made changes. |
Change History (most recent first): |
8/10/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
#pragma segment Core |
// System Includes |
#include <Threads.h> |
// Application includes |
#ifndef __BAREBONES__ |
#include "BareBones.h" |
#endif |
#ifndef __PROTOTYPES__ |
#include "Prototypes.h" |
#endif |
// static prototypes |
static pascal void* ProgressDialogThread ( tThreadedOperationPtr theInfo ); |
static pascal void ThreadTermination ( ThreadID threadTerminated, void* terminationProcParam ); |
OSErr ThreadedProgressOperation ( tThreadedOperation theOperation, void* refCon, |
StringPtr theText, SInt32* operationErr, Boolean bBarberPole ) |
{ |
OSErr theErr = noErr; |
ThreadID operationsThreadID = 0; |
ThreadID progressDlgThreadID = 0; |
tThreadedOperationPtr theInfo = nil; |
theInfo = (tThreadedOperationPtr) NewPtrClear ( sizeof ( tThreadedOperationRec ) ); |
theErr = MemError ( ); |
if ( theErr ) goto CleanupAndbail; |
BlockMoveData ( theText, theInfo->theText, theText[0] + 1 ); |
theInfo->bBarberPole = bBarberPole; |
theInfo->refCon = refCon; |
// Create the Progress Dialog Thread |
theErr = NewThread ( kCooperativeThread, (ThreadEntryProcPtr) ProgressDialogThread, |
(void*) theInfo, kDefaultStackSpace, kNoCreationOptions, |
(void*) operationErr, &progressDlgThreadID ); |
if ( theErr ) goto CleanupAndbail; |
// Install termination routine to decrement the usage count |
// and maybe dispose of the tThreadedOperationRec. |
theInfo->usageCount++; |
theErr = SetThreadTerminator ( progressDlgThreadID, ThreadTermination, (void*) theInfo ); |
// Create the Operation Thread |
theErr = NewThread ( kCooperativeThread, (pascal void* (*) (void*)) theOperation, (void*) theInfo, |
kDefaultStackSpace, kNoCreationOptions, (void*) operationErr, |
&operationsThreadID ); |
if ( theErr ) goto CleanupAndbail; |
theInfo->usageCount++; |
theErr = SetThreadTerminator ( operationsThreadID, ThreadTermination, (void*) theInfo ); |
// Get the dialog drawn _before_ the actual operation is started |
theErr = YieldToThread ( progressDlgThreadID ); |
if ( theErr ) goto CleanupAndbail; |
return noErr; |
CleanupAndbail: |
if ( theInfo ) |
DisposePtr ( (Ptr) theInfo ); |
// Dispose of the threads, and pass the error codes back |
if ( operationsThreadID ) |
DisposeThread ( operationsThreadID, (void*) theErr, false ); |
if ( operationsThreadID ) |
DisposeThread ( progressDlgThreadID, (void*) theErr, false ); |
return theErr; |
} |
static pascal void* ProgressDialogThread ( tThreadedOperationPtr theInfo ) |
{ |
SInt16 theType; |
GrafPtr savePort; |
DialogRef theDialog = nil; |
Handle theHan; |
Rect theRect; |
theDialog = GetNewDialog ( kProgressDialogID, nil, (WindowPtr) -1 ); |
SetWRefCon ( theDialog, (long) theInfo ); |
GetDialogItem ( theDialog, kStaticTextItemID, &theType, &theHan, &theRect ); |
SetDialogItemText ( theHan, theInfo->theText ); |
GetDialogItem ( theDialog, kUserItemID, &theType, &theHan, &theRect ); |
if ( !theInfo->bBarberPole ) |
{ |
SetDialogItem ( theDialog, kUserItemID, theType, (Handle) gOutlineUserItemUPP, &theRect ); |
CallUserItemProc ( gOutlineUserItemUPP, theDialog, kUserItemID ); |
} |
ShowWindow ( theDialog ); |
DrawDialog ( theDialog ); |
while ( theInfo->bCancelled == false ) |
{ |
YieldToAnyThread ( ); |
// Although the user interface doesn't allow you to execute |
// more than one threaded progress bar, we'll be careful to |
// support it here. It's just a case of making sure the port |
// is setup and restored across calls to YieldToAnyThread. |
GetPort ( &savePort ); |
SetPort ( theDialog ); |
if ( theInfo->bBarberPole ) |
{ |
PicHandle thePic; |
static SInt16 theID = 1000; // Gotcha: Non-reentrant, see documentation |
// Gotcha: Some Resource Manager calls can only be made |
// from the main thread on a Mac Plus. See documentation |
thePic = GetPicture ( theID++ ); |
DrawPicture ( thePic, &theRect ); |
if ( theID > 1003 ) |
theID = 1000; |
if ( theInfo->doneAmount == kBarberPoleFinished ) |
break; |
} |
else |
{ |
if ( theInfo->doneAmount != theInfo->drawnAmount ) |
{ |
int theLength; |
float floatDone, floatMax, thePercent; |
// Temporarily adjust the user item rect to draw the bar |
GetDialogItem ( theDialog, kUserItemID, &theType, &theHan, &theRect ); |
theLength = theRect.right - theRect.left; |
floatDone = theInfo->doneAmount; |
floatMax = theInfo->maxAmount; |
thePercent = (floatDone / floatMax) * 100; |
theRect.right = theRect.left + ((thePercent / 100) * theLength); |
theRect.top--; theRect.bottom++; |
FillRect ( &theRect, &qd.black ); |
} |
if ( theInfo->doneAmount == theInfo->maxAmount ) |
break; |
} |
SetPort ( savePort ); |
} |
// A problem occurs if the thread is terminated before the dialog is |
// disposed. Fortunatly, with the current implementation, it can't. |
// However, if this can occur with your implementation, you'll want |
// to dispose of the dialog and (optionally?) restore the port in the |
// termination routine. |
DisposeDialog ( theDialog ); |
return noErr; |
} |
// |
// We don't want to dispose of the record when another thread could still |
// be accessing it. Although they'll both finish about the same time, we |
// don't want to rely on the implementation of the thread scheduler. This |
// approach ensures both threads have finished with the record. |
// |
static pascal void ThreadTermination ( ThreadID threadTerminated, void* terminationProcParam ) |
{ |
#pragma unused(threadTerminated) |
OSErr theErr; |
#if DEBUGGING |
if ( terminationProcParam == nil ) |
DebugStr ( "\p ThreadTermination: terminationProcParam is nil" ); |
#endif |
((tThreadedOperationPtr) terminationProcParam)->usageCount--; |
if ( ((tThreadedOperationPtr) terminationProcParam)->usageCount == 0 ) |
{ |
DisposePtr ( (Ptr) terminationProcParam ); |
#if DEBUGGING |
theErr = MemError ( ); |
if ( theErr ) DebugStrNum ( "\p ThreadTermination: DisposePtr", theErr ); |
#endif |
} |
return; |
} |
// |
// This routine is one of the operations carried out |
// which the progress bar is representing. |
// |
pascal SInt32 ThreadedStandardDemoOperation ( tThreadedOperationPtr theInfo ) |
{ |
OSErr theErr = noErr; |
int i; |
const int max = 100; |
for ( i = 1; i <= max && theInfo->bCancelled == false; i++ ) |
{ |
UInt32 theDelay = 10L; |
Delay ( theDelay, &theDelay ); |
theInfo->doneAmount = i; |
theInfo->maxAmount = max; |
YieldToAnyThread ( ); |
} |
return (SInt32) theErr; |
} |
// |
// This routine is one of the operations carried out |
// which the progress bar is representing. |
// |
pascal SInt32 ThreadedBarberPoleDemoOperation ( tThreadedOperationPtr theInfo ) |
{ |
// A5 is not garanteed to be valid |
OSErr theErr = noErr; |
int i; |
const int max = 100; |
for ( i = 1; i <= max && theInfo->bCancelled == false; i++ ) |
{ |
UInt32 theDelay = 10L; |
Delay ( theDelay, &theDelay ); |
YieldToAnyThread ( ); |
} |
theInfo->doneAmount = kBarberPoleFinished; |
return (SInt32) theErr; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-30