1

I am wondering whether timestamp can be used to solve process synchronization problem, when race condition occurs?
Below is an algorithm for entry as well as exit sections for every process who wants to enter in critical section. Entry section uses FCFS (First Come First Serve) technique to give access to critical section.

    interested[N] is shared array of N integers where N is number of processes.

    // This section executed when process enters critical section.
    entry_section (int processNumber) {
        interested [processNumber] = getCurrentTimeStamp ();  // Gets the current timestamp.
        index = findOldestProcessNumber (interested);         // Find the process number with least timestamp.
        while (index != processNumber);
    }

    // This section executed when process leaves critical section.
    exit_section (int processNumber) {
        interested [processNumber] = NULL;
    }

According to me, this algorithm satisfies all conditions for synchronization, i.e., Mutual Exclusion, Progress, Bounded waiting and Portability. So, Am I correct?

Thanks for giving your time.

Shiv
  • 122
  • 2
  • 16
  • What did you find out when you tested it? – Martin James Nov 24 '19 at 14:25
  • @MartinJames I have not implemented it in any language but I was thinking whether this could be done or not? – Shiv Nov 25 '19 at 08:09
  • @MartinJames There is a good solution for 2 processes, i.e., Peterson's Algo which satisfies all conditions mentioned in the question but I think this solution can be applicable to more than 2 processes also. – Shiv Nov 25 '19 at 08:46
  • why someone has down voted the question? Is it not right to ask this type question? – Shiv Nov 27 '19 at 11:15
  • 1
    @Shiv - I was just wondering, what data do your processes share? What happen if two or more processes use the share data at exactly the same time timestamp? What happen if for some reason two or more processes start with the same timestamp? That could happen as a 2Ghz CPU can do two billion cycles per second. So technically, multiple operations can happen at the same millisecond. So if your timestamp is only up to the second, then that problem will become worst. – Kevin Ng Nov 30 '19 at 07:42
  • @KevinNg yeah I have got your point. In that case we can use ProcessId to break the conflict and can allow a process with lower processId to enter the Critical Section. Please correct me if I'm wrong. – Shiv Dec 01 '19 at 14:12
  • @Shiv - You are correct but if it is like that, why not just use unique process ID alone instead of combining it with the timestamp? – Kevin Ng Dec 01 '19 at 19:11
  • @KevinNg timestamp is used to provide fairness to all process, i.e., process, which enters first in critical section, will execute first (Kind of FCFS) due to **findOldestProcessNumber**. This will remove **starvation problem**. And processId will be useful in case of conflict between timestamp. Correct me if anything wrong. – Shiv Dec 02 '19 at 05:32
  • @Shiv - It may be right in your case for first come first serve. But, what happened if the younger process arrived first but have the same timestamp as the older process. If you only rely on FCFS rules and nothing else, why not let the processor figuring that out without expending any computational power for obtaining the timestamps? Your case can work in a condition where you are queuing tasks. But even then, the first task that is placed in the queue could also be the first task out without any timestamps. – Kevin Ng Dec 02 '19 at 05:38
  • @KevinNg If younger process arrived first having same timestamp as older process then processId can be used to break the conflict. Now if timestamp is not used then there can be a case where only few processes are keep accessing the Critical Section and rest are waiting for long time hence starvation and no bounded waiting, which is not good in multiprocess environment. Only processId alone can not serve that purpose. – Shiv Dec 02 '19 at 05:56
  • @Shiv - you can also queue the process on an array or a similar data structure and implement first come first serve rules without any timestamps. Depend on the size of that array, the processes that are being queued can be a few to as much as you want to scale it by memory and resource limitation. – Kevin Ng Dec 02 '19 at 06:21
  • @KevinNg yeah definitely we can use queuing but it may violate **mutual exclusion** property and can lead to race condition. – Shiv Dec 02 '19 at 06:47

2 Answers2

2

Short and sweet, here are the two issues with this approach.

  1. All your processes are busy-waiting. This means that even though the process cannot enter the critical section, it still cannot rest. Which means that the os scheduler needs to constantly keep scheduling all interested processes even though they're not producing a meaningful output. This hurts performance and power consumption.
  2. This is the big one. There is no guarantee that two processes will not have the same timestamp. It may be unlikely, but likelihood is not what you're looking for when you want to guarantee mutual exclusion to prevent a race condition.
kag0
  • 5,624
  • 7
  • 34
  • 67
1

Your code is just a sketch, but most likely it will not work in all the cases.

If there are no locks and all the functions are using non-atomic operations, there are no guarantees that the code will execute correctly. It is essentially the same as the first example here, except that you are using an array and assuming you don't need the atomicity since each process will only access its own element.

Let me try to come up with a counterexample.

Few minor clarifications.

As far as I understand the omitted portion of each process is running in a loop

while(!processExitCondition)
{
    // some non-critical code
    ...
    // your critical section as in the question
    entry_section (int processNumber) {
        interested [processNumber] = getCurrentTimeStamp ();  // Gets the current timestamp.
        index = findOldestProcessNumber (interested);         // Find the process number with least timestamp.
        while (index != processNumber);
    }

    // This section executed when process leaves critical section.
    exit_section (int processNumber) {
        interested [processNumber] = NULL;
    }
    // more non-critical code
    ...
}

It seems to me that the scheduling portion should be busy-waiting, constantly getting the oldest process as such:

while (findOldestProcessNumber (interested) != processNumber);

as otherwise, all your threads can immediately hang in an infinite while loop, except for the first one which will execute once and hang right after that.

Now your scheduling function findOldestProcessNumber (interested); has some finite execution time and if my assumption about the presence of a process outer loop while(!processExitCondition) correct, this execution time might happen to be slower than the execution of code inside, before or after the critical section. As a result, the completed process can get back into the interested array before while findOldestProcessNumber (interested); iterates over it and if getCurrentTimeStamp (); is low fidelity (say seconds) you can get two processes entering critical section at once. Imagine adding a long sleep into findOldestProcessNumber (interested); and it will be easier to see how that might happen.

You can say it is an artificial example, but the point is that there are no guarantees of how the processes will interleave with each other, so your synchronization relies on the assumption that certain portions of the code execute certain time "large" or "small" enough. This is just an attempt to fake an atomic operation using those assumptions.

You can come up with the counter-ideas to make it work. Say you can implement getCurrentTimeStamp () to return a unique timestamp for each caller. Either a simple atomic counter with the hardware guarantee that only one process can increment it or by internally using an atomic lock (mutex), it's own critical section and busy waiting for this lock to provide each caller process a distinct system clock value if you wish to have it as a real-time. But with a separate findOldestProcessNumber (interested) call I find it hard to think of a way to make it guaranteed. I can't claim it is impossible, but the more complicated it gets the more likely you are just hiding absence of the Mutual Exclusion guarantee.

So the simplest solution with a lock (mutex) is the best. In your code snippet add a mutex around your critical section with your current entry and exit code used only for the scheduling on the first-come-first-serve basis and mutex with giving you mutual exclusion guarantee.

If you want a lockless solution you can use Boost lockfree queue or implement a lockless ring buffer to push the processes number the queue in entry_section and then wait for it to get its turn, although performance can be worse as contr-intuitive as it seems.

isp-zax
  • 3,833
  • 13
  • 21