Tasks use special items known as semaphores to control access to shared resources. The following sections explain how they work.
The Problem: Sharing Resources Safely
One job of a multitasking operating system like Portfolio is to provide safe, orderly ways for tasks to share resources. A classic problem in computing is allowing multiple tasks to share the same resource. The problem affects any resource, including memory or a disk file, whose state can be changed by the tasks that share it. If a task changes a resource before another task finishes using it, and the change violates an assumption the first task has about the resource's contents, then crashes and corruption can occur.
The Solution: Locking Shared Resources When Using Them
To share a resource safely, a task must be able to lock the resource while it's using it and unlock it when it finishes using it. This is known as mutual exclusion: Other tasks are excluded from the resource until it is safe for them to use it.
The Implementation: Semaphores
There are many techniques for enforcing mutual exclusion. One of the simplest and most effective techniques uses special data structures known as semaphores. A semaphore keeps track of how many tasks are using or waiting to use a shared resource. This approach-described in all good textbooks on operating systems-is the one Portfolio uses. Tasks using Portfolio semaphores use the routines provided by the kernel for creating semaphores, for deleting them, and for locking and unlocking the resources they control.
On 3DO systems, it is up to each task to ensure that any shared resources it owns-in particular, memory pages that other tasks can write to-are shared safely. (To learn how tasks can grant other tasks write access to their memory, see Managing Memory.) Although tasks are not required to use semaphores, you should always use them, or another mutual-exclusion mechanism, when your task shares any of its memory with another task.
Using Semaphores
To control access to a shared resource it owns, a task
Creates a semaphore
Associates the semaphore with the resource
Tells the other tasks that share the resource there is a semaphore for controlling access to the resource
Each task then must lock the semaphore before using the resource by calling LockSemaphore() and unlocks it as soon as it's done using it by calling UnlockSemaphore(). Except for creating the semaphore by calling CreateSemaphore(), sharing a resource requires the cooperation of the tasks that are involved. The tasks must agree on a way to communicate the information about the new semaphore, such as intertask messages or shared memory, and they need to agree on a format for this information. They must also agree to lock the semaphore before using the resource and unlock it when they're done.
If your task needs access to a shared resource and cannot continue without it, you can ask the kernel to put the task into wait state until the resource becomes available. You do this by including the SEM_WAIT flag in the flags argument of
LockSemaphore() discussed below.