Show EventTasks.c syntax highlighted
/****************************************************************************************
EventTasks.c $Revision: 22 $
<http://rentzsch.com/eventTasks>
Copyright © 1999-2002 Red Shed Software. All rights reserved.
by Jonathan 'Wolf' Rentzsch (jon at redshed dot net)
************************************************************************************/
#include "EventTasks.h"
#include "require.h"
#include "Align.h"
/**************************
*
* Macros
*
**************************/
#pragma mark (Macros)
/* Adventures in Carbonating...
EventTasks is designed to be called at interrupt time. So, it may need to get the
current tick count at interrupt time. It used to call TickCount(). Then Tech Note
1104 : "Interrupt-Safe Routines", came out which revealed:
"TickCount is not interrupt-safe. This is because it supports the (long-obsolete)
Journaling Mechanism, as described in Inside Macintosh I ,page 261. Interrupt time
code should use LMGetTicks instead."
So I replaced my use of TickCount() with LMGetTicks().
Then Carbon came out. It doesn't export LMGetTicks(). The spec says to use TickCount()
instead. However, TickCount() wasn't officially interrupt-safe until CarbonLib 1.0.2.
Then again, a lot of code uses TickCount() at interrupt time and doesn't seem any worse
for it. I suspect call TickCount() at interrupt time on CarbonLib before 1.0.2 won't have
any ill effects.
*/
#if TARGET_API_MAC_CARBON
#define MyTickCount TickCount
#else
#define MyTickCount LMGetTicks
#endif
/**************************
*
* Globals
*
**************************/
#pragma mark -
#pragma mark (Globals)
typedef struct {
Pad padding[ kAtomicAlignment ];
AtomicList list;
} AlignedAtomicList;
AlignedAtomicList alignedPriority;
AlignedAtomicList alignedSchedule;
AlignedAtomicList alignedLoop;
AtomicList *gPriority = nil;
AtomicList *gSchedule = nil;
AtomicList *gLoop = nil;
/**************************
*
* Funky Protos
*
**************************/
#pragma mark -
#pragma mark (Funky Protos)
#define ShouldInitializeEventTasks() gPriority == nil
void
InitializeEventTasks();
Boolean
ScheduleForeverEventTask(
EventTask *task,
UInt32 sleep );
Boolean
SchedulePriorityEventTask(
EventTask *task,
UInt32 sleep );
Boolean
ScheduleLoopEventTask(
EventTask *task,
UInt32 sleep );
Boolean
ScheduleScheduleEventTask(
EventTask *task,
UInt32 sleep );
Boolean
NoUserEvents();
void
PerformPriorityTasks();
void
PerformScheduledTasks();
void
PerformEventLoopTasks();
/****************************************************************************************
*
* Interface
*
****************************************************************************************/
#pragma mark -
#pragma mark (Interface)
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Thu, Apr 15, 1999 Created.
************************************************************************************/
void
InitializeEventTask(
EventTask *task )
{
RequirePtrAlign( task, kAtomicAlignment );
task->element.next = nil;
task->element.list = nil;
task->wakeup = nil;
task->proc = nil;
task->refCon = nil;
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Wed, Apr 14, 1999 Created.
************************************************************************************/
Boolean
ScheduleEventTask(
EventTask *task,
UInt32 sleep )
{
Boolean result;
RequirePtrAlign( task, kAtomicAlignment );
// Make sure there's enough time left in the future to actually schedule the task.
// This could happen when a program is run continually for 2 years and schedules
// a task two years out. Paranoid, yes.
Require( ( 0xFFFFFFFF - MyTickCount() ) > sleep );
if( ShouldInitializeEventTasks() ) {
InitializeEventTasks();
}
switch( sleep ) {
case kForever:
result = ScheduleForeverEventTask( task, sleep );
break;
case kPriority:
result = SchedulePriorityEventTask( task, sleep );
break;
case kEventLoop:
result = ScheduleLoopEventTask( task, sleep );
break;
default:
result = ScheduleScheduleEventTask( task, sleep );
}
return( result );
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Wed, Apr 14, 1999 Created.
wolf Tue, Aug 10, 1999 Added maxTaskTime functionality.
************************************************************************************/
UInt32
PerformEventTasks(
UInt32 maxTaskTime )
{
EventTask *task = nil;
Boolean noUserEvents = false;
UInt32 stopTime = MyTickCount() + maxTaskTime;
if( ShouldInitializeEventTasks() )
InitializeEventTasks();
PerformPriorityTasks();
noUserEvents = NoUserEvents();
if( ( stopTime > MyTickCount() ) && noUserEvents ) {
PerformScheduledTasks();
noUserEvents = NoUserEvents();
}
if( ( stopTime > MyTickCount() ) && noUserEvents ) {
PerformEventLoopTasks();
noUserEvents = NoUserEvents();
}
if( noUserEvents ) {
task = PeekGuardedAtomicListType( gSchedule, EventTask, element );
if( task ) {
if( task->wakeup > MyTickCount() )
return( task->wakeup - MyTickCount() );
else
return( 0 );
} else
return( 0xFFFFFFFF );
} else
return( 0 );
}
/****************************************************************************************
*
* Implementation
*
****************************************************************************************/
#pragma mark -
#pragma mark (Implementation)
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Tue, Feb 22, 2000 Created.
************************************************************************************/
void
InitializeEventTasks()
{
if( ShouldInitializeEventTasks() ) {
gPriority = (AtomicList*) AlignXDirect( &alignedPriority, kAtomicAlignment );
gPriority->next = nil;
RequirePtrAlign( gPriority, kAtomicAlignment );
gSchedule = (AtomicList*) AlignXDirect( &alignedSchedule, kAtomicAlignment );
gSchedule->next = nil;
RequirePtrAlign( gSchedule, kAtomicAlignment );
gLoop = (AtomicList*) AlignXDirect( &alignedLoop, kAtomicAlignment );
gLoop->next = nil;
RequirePtrAlign( gLoop, kAtomicAlignment );
}
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Wed, Sep 29, 1999 Created.
************************************************************************************/
Boolean
ScheduleForeverEventTask(
EventTask *task,
UInt32 sleep )
{
#pragma unused( sleep )
long result = true;
RequirePtrAlign( task, kAtomicAlignment );
Require( sleep == kForever );
if( task->element.list ) {
result = RemoveGuardedAtomicListType( task, (AtomicList*) task->element.list,
EventTask, element );
}
if( result )
task->wakeup = kForever;
return( result ? true : false );
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Mon, Sep 20, 1999 Created.
************************************************************************************/
Boolean
SchedulePriorityEventTask(
EventTask *task,
UInt32 sleep )
{
#pragma unused( sleep )
long result = true;
RequirePtrAlign( gPriority, kAtomicAlignment );
RequirePtrAlign( task, kAtomicAlignment );
Require( sleep == kPriority );
if( task->element.list ) {
result = RemoveGuardedAtomicListType( task, (AtomicList*) task->element.list, EventTask, element );
}
if( result ) {
result = PutLastGuardedAtomicListType( task, gPriority, EventTask, element );
if( result ) {
task->wakeup = kPriority;
if( gThisProcessSerialNumber )
WakeUpProcess( gThisProcessSerialNumber );
}
}
return( result ? true : false );
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Mon, Sep 20, 1999 Created.
************************************************************************************/
Boolean
ScheduleLoopEventTask(
EventTask *task,
UInt32 sleep )
{
#pragma unused( sleep )
long result = true;
RequirePtrAlign( gLoop, kAtomicAlignment );
RequirePtrAlign( task, kAtomicAlignment );
Require( sleep == kEventLoop );
if( task->element.list ) {
result = RemoveGuardedAtomicListType( task, (AtomicList*) task->element.list, EventTask, element );
}
if( result ) {
result = PutLastGuardedAtomicListType( task, gLoop, EventTask, element );
if( result )
task->wakeup = kEventLoop;
}
return( result ? true : false );
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Mon, Sep 20, 1999 Created.
************************************************************************************/
Boolean
ScheduleScheduleEventTask(
EventTask *task,
UInt32 sleep )
{
EventTask *current, *next;
long result = true;
RequirePtrAlign( gSchedule, kAtomicAlignment );
RequirePtrAlign( task, kAtomicAlignment );
Require( sleep != kForever && sleep != kPriority && sleep != kEventLoop );
if( task->element.list ) {
result = RemoveGuardedAtomicListType( task, (AtomicList*) task->element.list, EventTask, element );
}
if( result ) {
task->wakeup = MyTickCount() + sleep;
restart:
current = nil;
if( !IterateGuardedAtomicListType( ¤t, &next, gSchedule, EventTask, element ) )
goto restart;
while( current && task->wakeup > current->wakeup && next && task->wakeup > next->wakeup ) {
if( !IterateGuardedAtomicListType( ¤t, &next, gSchedule, EventTask, element ) )
goto restart;
}
if( current && task->wakeup > current->wakeup )
result = PutAfterGuardedAtomicListType( task, current, next, gSchedule, EventTask, element );
else
result = PutFirstGuardedAtomicListType( task, gSchedule, EventTask, element );
}
return( result ? true : false );
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Mon, Apr 12, 1999 Created.
************************************************************************************/
Boolean
NoUserEvents()
{
EventRecord ignored;
return( !EventAvail( everyEvent, &ignored ) );
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Wed, Apr 14, 1999 Created.
************************************************************************************/
void
PerformPriorityTasks()
{
EventTask *task;
RequirePtrAlign( gPriority, kAtomicAlignment );
task = RemoveFirstGuardedAtomicListType( gPriority, EventTask, element );
while( task ) {
RequirePtrAlign( task, kAtomicAlignment );
task->wakeup = kForever;
RequireProcPtr( task->proc );
task->proc( task );
task = RemoveFirstGuardedAtomicListType( gPriority, EventTask, element );
}
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Wed, Apr 14, 1999 Created.
************************************************************************************/
void
PerformScheduledTasks()
{
EventTask *task;
RequirePtrAlign( gSchedule, kAtomicAlignment );
task = RemoveFirstGuardedAtomicListType( gSchedule, EventTask, element );
while( task ) {
RequirePtrAlign( task, kAtomicAlignment );
if( task->wakeup > MyTickCount() ) {
// Oops. This task isn't ready yet, put it back.
PutFirstGuardedAtomicListType( task, gSchedule, EventTask, element );
return;
}
task->wakeup = kForever;
PerformPriorityTasks();
RequireProcPtr( task->proc );
task->proc( task );
task = RemoveFirstGuardedAtomicListType( gSchedule, EventTask, element );
}
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Wed, Apr 14, 1999 Created.
************************************************************************************/
void
PerformEventLoopTasks()
{
EventTask *task;
DeclareAlignedVariable( AtomicList, list, kAtomicAlignment );
RequirePtrAlign( gLoop, kAtomicAlignment );
RequirePtrAlign( list, kAtomicAlignment );
StealGuardedAtomicList( gLoop, list );
task = RemoveFirstGuardedAtomicListType( list, EventTask, element );
while( task ) {
RequirePtrAlign( task, kAtomicAlignment );
task->wakeup = kForever;
PerformPriorityTasks();
RequireProcPtr( task->proc );
task->proc( task );
task = RemoveFirstGuardedAtomicListType( list, EventTask, element );
}
}
#ifdef __cplusplus
/****************************************************************************************
*
* TEventTask
*
****************************************************************************************/
#pragma mark -
#pragma mark (TEventTask)
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Thu, Sep 2, 1999 Created.
************************************************************************************/
TEventTask::TEventTask(
UInt32 sleep )
{
InitializeEventTask( this );
proc = HandleEventTask_;
refCon = this;
if( sleep != kForever ) {
Boolean scheduled = ::ScheduleEventTask( this, sleep );
Require( scheduled );
}
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Thu, Sep 2, 1999 Created.
************************************************************************************/
TEventTask::~TEventTask()
{
Boolean scheduled = ::ScheduleEventTask( this, kForever );
Require( scheduled );
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Thu, Oct 21, 1999 Created.
************************************************************************************/
Boolean
TEventTask::ScheduleEventTask(
UInt32 sleep )
{
return( ::ScheduleEventTask( this, sleep ) );
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Thu, Sep 2, 1999 Created.
************************************************************************************/
void
TEventTask::HandleEventTask()
{
// stub
}
/****************************************************************************************
Commenter Date Comment
--------- ----------------- -----------------------------------------------------
wolf Thu, Sep 2, 1999 Created.
************************************************************************************/
void
TEventTask::HandleEventTask_(
EventTask *task )
{
RequirePtrAlign( task, kAtomicAlignment );
RequireProcPtr( task->proc );
Require( task->proc == HandleEventTask_ );
TEventTask *thisTask = (TEventTask*) task->refCon;
RequirePtr( thisTask );
thisTask->HandleEventTask();
}
#endif
See more files for this project here