To provide a lock, the kernel offers an item called a semaphore, which is defined as an element of a data structure. You can think of the semaphore as an "occupied" sign for a data structure. A polite task checks the semaphore of a data structure before it uses the structure. If the semaphore says that the structure is currently unused, the task can go to work on the structure. If the semaphore says that the structure is in use, the task must either wait until the semaphore says the structure is free or return to execution without using the structure.
To use a data structure with a semaphore, a task makes the function call LockSemaphore()
to lock the structure's semaphore. If the semaphore is unlocked, the kernel locks it and the task can proceed with its business, using the semaphored data structure as it sees fit without interference from other polite tasks. When the task is done, it makes another function call, UnlockSemaphore()
, to unlock the semaphore, releasing the semaphored data structure for use by other tasks.
When a task calls LockSemaphore()
, it specifies what it will do if the semaphore is already locked: wait for the semaphore to be unlocked (putting itself in the wait queue); or return to execution without using the semaphored structure. If the semaphore is locked, the task acts accordingly.
The semaphore is a completely voluntary mechanism; it is what its name implies, only a flag that tells whether a data structure is in use or not. It does not deny write permission to tasks that want to use the data structure without checking the semaphore. If you want to share a data structure with another task, be sure that the other task is written to check for the semaphore before it goes about its business.