Book Contents Previous Chapter Next Chapter
This chapter describes actors, objects that represent threads of execution in Magic Cap's multitasking kernel. This chapter also discusses classes that are closely related to actors, such as semaphores and timers. If you want your packages to perform more than one task at a time or do background work by creating separate threads of execution, you should read this chapter.
Magic Cap provides a kernel that supports multitasking among actors, which are multiple threads of execution. You can use actors in your package if you want to able to perform more than one task at a time, such as doing some lengthy work in the background so that the user can continue without waiting for the lengthy task to be completed.
Each thread is represented by an actor that has code associated with it. At any given moment, exactly one actor is running, and its code is executing. There may be other actors that are not running. These other actors can be ready, which means they are eligible to run, or waiting, which means they will not run until some specific event takes place. Actors are dying if they are in the process of being destroyed.
Magic Cap defines semaphore classes to allow various actors to share resources that don't permit simultaneous use. Semaphores represent resources that are shared among actors. For example, the modem is represented by a semaphore because only one actor can use the modem at any time. You can use semaphores to decide which one of a group of related actors should run. You can switch among these actors by having your actor wait until it gains access to a particular semaphore. Because the semaphore will only permit access by one actor at a time, other actors that request access will wait until the semaphore is released by the actor that has it.
The Magic Cap kernel includes a scheduler that decides which actor should run and which ones should wait. Whenever the running actor calls any Magic Cap operation, the scheduler can stop the actor and run another. Future versions of Magic Cap may switch actors more frequently, not necessarily waiting for an operation to be called.
This section describes the features provided by actors and related classes.
If you don't explicitly create and use actors in your package, Magic Cap handles the job for you. Magic Cap creates the appropriate actors and calls operations of your objects without any explicit work by your package.
Magic Cap uses an actor called the user actor to perform most of its work. The user actor is the main thread of execution in Magic Cap, handling all drawing and user input, switching to packages, and performing most other tasks. Magic Cap uses other actors for various special functions.
When the user sends or receives electronic mail, a connection actor is created to handle the task of connecting to the mail service and transferring information. Because the connection is an actor, Magic Cap can handle sending and receiving mail in the background while the user performs another task.
You might want to use actors to manage streams of data going into or out of a Magic Cap communicator. Often, managing streams requires only that some actions be synchronized with interrupts from a device such as a modem or serial port. Actors can be suspended while waiting for these interrupts, allowing the CPU to either remain idle to conserve power or to allow the main actor to continue to process the actions of the user.
If you want to create and use your own actors in your package, you should make an object of class Actor or one of its subclasses at runtime. When the scheduler runs your actor, it calls the actor's main function. You can specify the actor's main function in parameter actorParameters->mainFunction when you create the actor.
The main function in your actor performs the actor's basic task. For example, class Connection uses its main function to connect to the mail service and start transferring information. Your actor's main function should be designed to run for as long as you want your actor to run. If your main function ever exits, Magic Cap will destroy your actor.
You can trace the call chain of all running code back to the main function of an actor. Code is said to be running on a particular actor if its call chain traces back to that actor. For example, when Magic Cap is about to connect to an electronic mail service, it creates a connection actor and calls its main function. Code called directly or indirectly by the connection actor's main function is running on the connection actor.
When you want to create an actor, you can call NewTransient or another Magic Cap operation that creates new objects, passing the desired class of actor as a parameter. When you create a new actor, the scheduler adds it to the list of ready actors and runs it in turn. You should create your actor in transient RAM, allowing it to be destroyed when the communicator powers off.
When you create an actor, Magic Cap allocates a stack for the actor. You can pass a size for the stack when you create it. If you don't pass a value for stack size, Magic Cap creates a 4K byte stack, which is usuallly adequate. You must be sure your actor has enough stack space. If it doesn't, the stack will overflow and the CPU will overwrite RAM. However, you should avoid allocating unneeded stack space, as Magic Cap typically runs in constricted memory spaces. The more Magic Cap operations your actor calls, the more stack space it will require.
Magic Cap provides some operations to help you determine and manage your actor's stack requirements. Call StackFree to determine how much free space remains on your actor's stack. If your actor includes a function that uses especially large amounts of stack space, you can use CallWithStack to call the function with its own separate stack.
Magic Cap defines a special actor, the user actor, for all tasks that require drawing on the screen. Magic Cap automatically creates and manages the user actor. Your package doesn't have to take any action to create this actor. If you don't create and use actors in your package, most of your package's code will run on the user actor.
If you create any code that doesn't run on the user actor, such as a client for an electronic mail service, your alternate-actor code must not take any action that would result in drawing. Instead, you can call the RunSoon operation from your code that runs on the alternate actor to ensure that the drawing code runs on the user actor. When you call RunSoon, you pass the function that you want to run, a parameter indicating whether the function must run on the user actor, and any parameters that should be passed to the function itself.
If you create your own interrupt handler, you can use RunSoon to call operations that require the user actor from your interrupt handler.
WARNING: If the function you pass to RunSoon is to run on the user actor, you must be sure it does not stop the user actor. For example, your RunSoon function must not call Access to wait for a semaphore. If it does, the user actor will wait until the semaphore is available, and Magic Cap will appear to stop.
The scheduler considers switching to other actors whenever an operation is called. If you create your own actor, you should keep it waiting as much as possible so that Magic Cap can be responsive to the user. You can keep your actor waiting by creating a semaphore that controls when it should run, then calling Access on the semaphore.
For more information on how the scheduler decides which actor to run, see the Actor Information section.
You can determine the current status of an actor by checking its Status attribute. The following values are defined for an actor's status:
------------------------------------------------------------ actor status description ------------------------------------------------------------kDying
The actor is being destroyed.kRunning
This is the currently executing actor.kReady
The actor is in the queue of actors to be run.kWaiting
The actor is waiting to acquire a semaphore. ------------------------------------------------------------
Note that only one actor is running at any time, the one with status kRunning. Actors with status kReady will be run when the scheduler gets to them. Actors with status kWaiting are waiting for a semaphore and will have their status changed to kReady when they acquire the desired semaphore.
Every actor has a priority setting that the scheduler uses in deciding which actor will run. When an actor calls a Magic Cap operation, the scheduler determines which actor will run next. If there are no ready actors, the current actor continues running. If there are other ready actors, the scheduler runs the one with the highest priority. If there is more than one ready actor with the same priority, the scheduler switches among them in a round robin.
By default, actors in packages are created with priority 0. If you want to create a high-priority actor, you should give it priority 1024. You can call SetPriority to change an actor's priority, but you should only use priority values 0 and 1024. You can call Priority to get an actor's priority.
WARNING: If you create an actor with priority 1024, the scheduler will always call it when it is ready. For this reason, if you create a high-priority actor, you must keep it waiting when not in use, or no other actors will run.
If you create more than one actor, and you want to cause one actor to throw a particular exception while your code is running on another actor, you can call FailSoon. When you call FailSoon, you'll pass the actor you want to throw the exception and the desired exception number as parameters.
If you call FailSoon, the actor you want to throw the exception must not have access to any semaphores. If it does, a deadlock will result, and Magic Cap will appear to stop. You can avoid this problem by using a fail-safe semphore instead of a regular semaphore. See this chapter's Subclasses section for more information.
You can call RegisterHandler to specify a function that will be called when a particular exception occurs. This operation is intended mainly for low-level software.
For more information on exceptions in Magic Cap, see the Handling Exceptions chapter of this book.
This section describes other classes you might use when working with actors, such as semaphores and timers.
Magic Cap defines class Semaphore for objects that control access to resources shared among various actors. For example, various Magic Cap actors require use of the modem, but only one can use it at a time, so the modem's access is controlled by a semaphore. Each semaphore determines how many actors can access it at one time. Most semaphore allow only one access. The number of simultaneous accesses for a semaphore is determined by a parameter to the New call that creates it.
Semaphores ensure that the shared resource is only made available to the specified number of actors at once, preventing access conflicts. Getting access to a semaphore is also called acquiring the semaphore. Relinquishing access to a semaphore is called releasing the semaphore.
You can call Access if you want your actor to acquire a semaphore. If no other actor has the semaphore when you call Access, your actor will acquire the requested semaphore and will continue running. However, if the semaphore is already accessed by another actor when you call Access, the scheduler will stop running your actor and its status will change to kWaiting. There is no way to tell which actor has acquired a semaphore.
When your actor calls Access to request a semaphore that is already acquired, your actor will be added to a list of actors that have requested the semaphore. If more than one actor is waiting for the semaphore when it is released by its current holder, Magic Cap will not necessarily grant access in the same order that the requests were made.
You can call Release to relinquish access when your actor no longer needs a semaphore. When you call Release, the semaphore will be acquired by an actor that was waiting for it, if any. Otherwise, the semaphore will be available for the next access request.
When an actor calls Access on a semaphore, the actor waits until the semaphore is available.
You should create your semaphore in transient RAM, allowing it to be destroyed when the communicator powers off. When a semaphore is destroyed, it releases any actors that are waiting for it.
Magic Cap defines class Timer to allow you to keep your actors stopped and waiting for a specified amount of time, rather than waiting for a particular semaphore. Timers are implemented as a subclass of semaphores with many of the details handled automatically.
Call BlockFor to make an actor wait for a specific number of milliseconds. You can also call BlockUntil to make an actor wait until a specified time. If you simply want to delay without using the scheduler to make your actor wait, you can call DelayMilliseconds.
You can use the RunAt operation if you want to call a particular function at a specified time, or RunIn to call a function after a specified number of milliseconds has elapsed.
You may want to have an actor wait for a semaphore for a certain length of time, then signal if it hasn't acquired the semaphore. You can do this by calling RunIn or RunAt, then calling Access to get the semaphore. If the actor is still waiting for the semaphore at the time specified by RunIn or RunAt, the function passed to RunIn or RunAt will be called. You should call ReleaseSoon in that function to remove the actor's request for the semaphore.
If you create your own interrupt handler, you can use ReleaseSoon from your interrupt handler.
You can stop a timer by calling StopTimer. If you are about to destroy a timer, you should call StopTimer to stop the timer first.
Although many timer values are given in milliseconds, some Magic Cap platforms are limited to lower precision. In particular, Sony Magic Link and Motorola Envoy have a precision of 1/128 second, or about 8 milliseconds.
When a communicator is first powered up, a special 32-bit timer begins counting in milliseconds. When this timer reaches its maximum value, it simply rolls over and begins counting again at zero. You can get the value of this timer by calling Now, an intrinsic operation of class Timer. You can use Now to perform an action while waiting for some amount of time to elapse. To determine the time of day, call LocalTime, described in this book's Date and Time chapter.
NOTE: Because the timer used by Now rolls over to zero, you must be careful when comparing values returned by Now. In particular, you should avoid the following syntax, which can fail if the timer rolls over:
/* Don't do it this way */
time = Now();
while (Now() < time+interval)
{ /* do something */ };
Instead, use the following syntax, which avoids the timer rollover problem:
/* Do it this way */
time = Now();
while (Now()-time < interval)
{ /* do something */ };
Magic Cap defines three subclasses of semaphore that you might use in your packages. One subclass, Timer, is discussed in the preceding section. Class FailsafeSemaphore defines a field that refers to the last actor that had access to the semaphore. If that actor is destroyed, Magic Cap will release its fail-safe semaphore.
For more information, see the following topics in Magic Cap Class and Method Reference:
class Actor
operations and attributes:
CallWithStack FailSoon Main Priority Resume SetPriority StackFree Status
class Timer
operations and attributes:
BlockFor BlockUntil Now RunAt RunIn StopTimer
class UserActor
class Semaphore
operations and attributes:
Access Release RunSoon
class Utilities
operations and attributes:
Attribute DelayMilliseconds PermitIdle
class ExceptionServer
operations and attributes:
RegisterHandler
class TimeoutSemaphore
Book Contents Previous Chapter Next Chapter