Understanding the TestAndSet() Instruction and its Effects

In summary: TestAndSet returns true, the lock was already taken, so the process must wait for it to be freed.How does that happen? Doesn't TestAndSet return TRUE?No, it returns the previous value of the lock, as described in the problem statement.Why are the values initially TRUE?As you rightfully suspected later on, the first process should have waiting[0] set to true, so we need to start out with it that way.And in the end of the code why do we use:while(TRUE)This is an infinite loop, meaning that the code will continue to run indefinitely until some external event interrupts it. In this case, it seems like the program
  • #1
Peon666
108
0
I understand that the purpose of the TestAndSet() instruction is to test and contents of the register and set new values. (returning the old value). I have a question regarding this instruction:

Code:
function TestAndSet(boolean lock) {
    boolean initial = lock
    lock = true
    return initial
}
-Wikipedia.

In this code, initial is the old value, and lock is the new one. The instruction:

Code:
boolean initial = lock

replaces the old value with the new one. And the instruction:

Code:
 return initial

returns the modified value. But what does this instruction do?:

Code:
  lock = true

I'd be really thankful if someone explains this a bit.
 
Technology news on Phys.org
  • #2
The function implements a simple swap operation. It stores the old value (so it can return it), sets the new value to true, and returns the old value. To understand it in a concurrent context you should consider the entire function body executing like in a critical region, meaning that only one of many concurrent invocations of the function (on the same lock) will be running at a time.

Assuming the lock initially is released (value false) a bunch of concurrent calls to TestAnd Set will only allow one of the calls (namely the first to get access) to return false whereas all the rest will return true indicating the lock has already been taken. Callers can then busy-wait until they get false from TestAndSet and then assume they have the lock until they clear the lock by assigning it false.
 
  • #3
Depending on the cpu and hardware architecture, test and set is usually implemented as a "indivisible" (also described as atomic or non-interruptable) operation, where both the read and write take place without the possibility of another task or cpu intervening between the read and write. For memory operations, it's implemented via a bus "lock" during the "read modify write" cycle. (The memory operation also invalidates any cached images of that memory location in other cpus).

The purpose is to provide multiple tasks and/or cpu's a means of synchronization.

For Intel X86 cpu's XCHG is always indivisible, and a few other instrutions can be made indivisible by using the "LOCK" prefix. For Motorola 68000 family, TAS (test and set) is indivisible.
 
Last edited:
  • #4
Thanks for the response, both of you. I understand the purpose behind TestAndLock(), but I'm having problems understanding a code given in the textbook related to it. Here's the code:

Code:
boolean waiting[n];
boolean lock;
do{
    waiting[i]=TRUE;
    key=TRUE;
    while (waiting[i] && key)
        key=TestAndSet(&lock);
    waiting[i]=FALSE;
    
    // Critical Section.

    j=(i+1)%n;
    while ((j != i) && !waiting[j])
        j=(j+1)%n;
    
    if (j == i)
        lock=FALSE;
    else
        waiting[j]=FALSE;

    // Remainder Section.

}while(TRUE)

The Book says:

"These data structures are initialized to false" (about: boolean waiting[n]; and boolean lock;)

-> Why are they initialized to false? Does this mean that they are NOT occupied as initially?

"P(i) can enter it's critical section only if waiting==FALSE or key=FALSE"

-> I understand the waiting part but what does key=FALSE part mean? (key is a local variable for each process.)

"The value of key can becomes false only if TestAndSet() is executed"

-> How does that happen? Doesn't TestAndSet return TRUE?

Finally, I have serious problem understanding this code more specifically (it's a part of the above code):

Code:
j=(i+1)%n;
while ((j != i) && !waiting[j])
    j=(j+1)%n;

Besides, in the code initially:
Code:
do{
    waiting[i]=TRUE;
    key=TRUE;

Why are the values initially TRUE?

And in the end of the code why do we use:

Code:
while(TRUE)

I tried to find explanations online but couldn't find them of much help. I'd highly appreciate if anyone would take some time to help me clarify these concepts. If alternatively, I'd be thankful if you provide me some online resource explaining these problems in detail.

Thanks.
 
  • #5
I have no idea what the rest of the code is trying to accomplish. A better example would be these functions:

Code:
volatile int lock;

void Lock(int * lock)
{
    while(0 != testandset(&lock));
}

void Unlock(int *lock)
{
    *lock = 0;    // may require some means to force a cache flush
}

Assume testandset(&lock) always sets lock to 1. At start up, lock needs to be initialized to zero. When testandset() is called, if lock is currently set to 1, then it returns a 1 and re-writes a 1 to lock, with no effective change to lock (although the write operation may invalidate any cached values for it). If lock is currently set to 0, then it's changed to a 1 without possiblity of any other task or cpu reading lock before that instance of testandset() changes lock to a 1, and testandset() will return a 0, indicating that lock was not set before the call.

The issue with this crude implemenation is that any cpu waiting for "lock" to be freed is stuck in a tight loop, rather than running other tasks. Normally stuff like this is incoported into an multi-tasking OS with multi-cpu support. In the case of Windows, semaphores and mutex's are the common means to synchronize multiple threads and/or tasks.
 
Last edited:
  • #6
Peon666 said:
Why are they initialized to false? Does this mean that they are NOT occupied as initially?
Yes. From what I can gather from the code you quoted, the lock variable is used to create a critical section in which the waiting flags (and possibly other stuff in the section marked "remaining code") can be updated without race condition, so it has to start out false. If it was initialized to true (or if a process forgot to set lock to false when exiting its critical region) the lock will no longer be obtainable for any process.

I understand the waiting part but what does key=FALSE part mean? (key is a local variable for each process.)

The key variable is just used to hold the result of the TestAndSet call. As the code appears here where key only seems to be used in the initial wait for lock, the code could be rewritten without use of that variable by testing directly on the return value from TestAndSet.
Doesn't TestAndSet return TRUE?
This is what I tried explain earlier. The TestAndSet method returns false to the caller who obtains the lock. Every other caller will get a true value, which means that they must try again "later" to obtain the lock.

Finally, I have serious problem understanding this code more specifically (it's a part of the above code):

Code:
j=(i+1)%n;
while ((j != i) && !waiting[j])
    j=(j+1)%n;

Besides, in the code initially:
Code:
do{
    waiting[i]=TRUE;
    key=TRUE;

Why are the values initially TRUE?

The loop finds another process who is also waiting and if it does, marks that process as no longer waiting, and if it doesn't it releases the lock early (I assume the process also releases the lock in some of the code not shown). Note that only one process at a time can do this search as it is guarded by the lock.

The process looks like a variation of the dining philosophers, so I assume waiting means waiting for a fork :smile:

And in the end of the code why do we use:

Code:
while(TRUE)
It is a common idiom and is simply used to make the process loop forever.

I tried to find explanations online but couldn't find them of much help. I'd highly appreciate if anyone would take some time to help me clarify these concepts. If alternatively, I'd be thankful if you provide me some online resource explaining these problems in detail.

I am not aware of any on-line resources on this, but I assume there must be plenty. I got my training long time ago reading Principles of Concurrent and Distributed Programming: Algorithms and Models, Ben-Ari, Prentice-Hall International Series in Computer Science, 1982.
 
Last edited:
  • #7
I don't have an example similar to your text, but did I write an example Windows multi-threaded program that just copies a file. One thread reads a file, the other thread writes a file. It shows how to use mutexes and semphores, using them to support inter-thread communication via single linked list fifo messages. One key aspect of this is WaitForMultipleObjects(), which combines wait for mutex lock, wait for non-zero semaphore count, and decrement semaphore count, into one indivisible operation, eliminating any priority issues between threads using the messaging functions I wrote.

Although there's a significant coding overhead to set the messaging system up, the two main threads that use the messaging functions, ThreadFile0() and ThreadFile1(), are very simple and small.

http://jeffareid.net/misc/mtcopy.zip
 
Last edited:
  • #8
Thanks a lot again, both of you. This really helped me understand the problem more clearly. And thanks for the resources as well. I guess I need more practice.

Regards.
 

FAQ: Understanding the TestAndSet() Instruction and its Effects

What is the purpose of the TestAndSet() instruction?

The TestAndSet() instruction is used in computer programming to ensure that only one process can access a shared resource at a time. It is commonly used in multi-threaded and multi-processor systems to prevent race conditions and ensure data integrity.

How does the TestAndSet() instruction work?

The TestAndSet() instruction works by atomically setting a flag or variable to a specific value and returning its previous value. This allows a process to check if the resource is currently being used by another process and if not, it can proceed to access the resource. If the resource is already in use, the process will wait until the flag is released before trying again.

What are the potential effects of using the TestAndSet() instruction?

The main effect of using the TestAndSet() instruction is that it introduces synchronization and mutual exclusion in accessing shared resources. This means that only one process can access a shared resource at a time, ensuring data integrity and preventing race conditions. However, it can also lead to potential performance issues if multiple processes are frequently trying to access the same resource.

How does the TestAndSet() instruction differ from other synchronization methods?

The TestAndSet() instruction differs from other synchronization methods, such as semaphores and mutexes, in that it is a low-level instruction that directly manipulates hardware. This means that it is more efficient and less prone to errors, but also more difficult to implement and requires careful consideration to avoid deadlocks and other issues.

Can the TestAndSet() instruction be used in any programming language?

Yes, the TestAndSet() instruction can be used in any programming language, as long as it has built-in support for atomic operations. Many high-level programming languages, such as Java and C#, have libraries or language constructs that allow for the use of atomic operations, including TestAndSet(). However, some low-level languages may require direct access to hardware in order to use the instruction.

Similar threads

Replies
3
Views
2K
Replies
30
Views
5K
Replies
1
Views
1K
Replies
8
Views
2K
Replies
7
Views
2K
Replies
5
Views
4K
Replies
2
Views
2K
Back
Top