how to model a queue in promela? - multithreading
Ok, so I'm trying to model a CLH-RW lock in Promela.
The way the lock works is simple, really:
The queue consists of a tail, to which both readers and writers enqueue a node containing a single bool succ_must_wait they do so by creating a new node and CAS-ing it with the tail.
The tail thereby becomes the node's predecessor, pred.
Then they spin-wait on pred.succ_must_wait until it is false.
Readers first increment a reader counter ncritR and then set their own flag to false, allowing multiple readers at in the critical section at the same time. Releasing a readlock simply means decrementing ncritR again.
Writers wait until ncritR reaches zero, then enter the critical section. They do not set their flag to false until the lock is released.
I'm kind of struggling to model this in promela, though.
My current attempt (see below) tries to make use of arrays, where each node basically consists of a number of array entries.
This fails because let's say A enqueues itself, then B enqueues itself. Then the queue will look like this:
S <- A <- B
Where S is a sentinel node.
The problem now is, that when A runs to completeness and re-enqueues, the queue will look like
S <- A <- B <- A'
In actual execution, this is absolutely fine because A and A' are distinct node objects. And since A.succ_must_wait will have been set to false when A first released the lock, B will eventually make progress, and therefore A' will eventually make progress.
What happens in the array-based promela model below, though, is that A and A' occupy the same array positions, causing B to miss the fact that A has released the lock, thereby creating a deadlock where B is (wrongly) waiting for A' instead of A and A' is waiting (correctly) for B.
A possible "solution" to this could be to have A wait until B acknowledges the release. But that would not be true to how the lock works.
Another "solution" would be to wait for a CHANGE in pred.succ_must_wait, where a release would increment succ_must_wait, rather than reset it to 0.
But I'm intending to model a version of the lock, where pred may change (i.e. where a node may be allowed to disregard some of its predecessors), and I'm not entirely convinced something like the increasing version wouldn't cause an issue with this change.
So what's the "smartest" way to model an implicit queue like this in promela?
/* CLH-RW Lock */
/*pid: 0 = init, 1-2 = reader, 3-4 = writer*/
ltl liveness{
([]<> reader[1]#progress_reader)
&& ([]<> reader[2]#progress_reader)
&& ([]<> writer[3]#progress_writer)
&& ([]<> writer[4]#progress_writer)
}
bool initialised = 0;
byte ncritR;
byte ncritW;
byte tail;
bool succ_must_wait[5]
byte pred[5]
init{
assert(_pid == 0);
ncritR = 0;
ncritW = 0;
/*sentinel node*/
tail =0;
pred[0] = 0;
succ_must_wait[0] = 0;
initialised = 1;
}
active [2] proctype reader()
{
assert(_pid >= 1);
(initialised == 1)
do
:: else ->
succ_must_wait[_pid] = 1;
atomic {
pred[_pid] = tail;
tail = _pid;
}
(succ_must_wait[pred[_pid]] == 0)
ncritR++;
succ_must_wait[_pid] = 0;
atomic {
/*freeing previous node for garbage collection*/
pred[_pid] = 0;
}
/*CRITICAL SECTION*/
progress_reader:
assert(ncritR >= 1);
assert(ncritW == 0);
ncritR--;
atomic {
/*necessary to model the fact that the next access creates a new queue node*/
if
:: tail == _pid -> tail = 0;
:: else ->
fi
}
od
}
active [2] proctype writer()
{
assert(_pid >= 1);
(initialised == 1)
do
:: else ->
succ_must_wait[_pid] = 1;
atomic {
pred[_pid] = tail;
tail = _pid;
}
(succ_must_wait[pred[_pid]] == 0)
(ncritR == 0)
atomic {
/*freeing previous node for garbage collection*/
pred[_pid] = 0;
}
ncritW++;
/* CRITICAL SECTION */
progress_writer:
assert(ncritR == 0);
assert(ncritW == 1);
ncritW--;
succ_must_wait[_pid] = 0;
atomic {
/*necessary to model the fact that the next access creates a new queue node*/
if
:: tail == _pid -> tail = 0;
:: else ->
fi
}
od
}
First of all, a few notes:
You don't need to initialize your variables to 0, since:
The default initial value of all variables is zero.
see the docs.
You don't need to enclose a single instruction inside an atomic {} statement, since any elementary statement is executed atomically. For better efficiency of the verification process, whenever possible, you should use d_step {} instead. Here you can find a related stackoverflow Q/A on the topic.
init {} is guaranteed to have _pid == 0 when one of the two following conditions holds:
no active proctype is declared
init {} is declared before any other active proctype appearing in the source code
Active Processes, includig init {}, are spawned in order of appearance inside the source code. All other processes are spawned in order of appearance of the corresponding run ... statement.
I identified the following issues on your model:
the instruction pred[_pid] = 0 is useless because that memory location is only read after the assignment pred[_pid] = tail
When you release the successor of a node, you set succ_must_wait[_pid] to 0 only and you don't invalidate the node instance onto which your successor is waiting for. This is the problem that you identified in your question, but was unable to solve. The solution I propose is to add the following code:
pid j;
for (j: 1..4) {
if
:: pred[j] == _pid -> pred[j] = 0;
:: else -> skip;
fi
}
This should be enclosed in an atomic {} block.
You correctly set tail back to 0 when you find that the node that has just left the critical section is also the last node in the queue. You also correctly enclose this operation in an atomic {} block. However, it may happen that --when you are about to enter this atomic {} block-- some other process --who was still waiting in some idle state-- decides to execute the initial atomic block and copies the current value of tail --which corresponds to the node that has just expired-- into his own pred[_pid] memory location. If now the node that has just exited the critical section attempts to join it once again, setting his own value of succ_must_wait[_pid] to 1, you will get another instance of circular wait among processes. The correct approach is to merge this part with the code releasing the successor.
The following inline function can be used to release the successor of a given node:
inline release_succ(i)
{
d_step {
pid j;
for (j: 1..4) {
if
:: pred[j] == i ->
pred[j] = 0;
:: else ->
skip;
fi
}
succ_must_wait[i] = 0;
if
:: tail == _pid -> tail = 0;
:: else -> skip;
fi
}
}
The complete model, follows:
byte ncritR;
byte ncritW;
byte tail;
bool succ_must_wait[5];
byte pred[5];
init
{
skip
}
inline release_succ(i)
{
d_step {
pid j;
for (j: 1..4) {
if
:: pred[j] == i ->
pred[j] = 0;
:: else ->
skip;
fi
}
succ_must_wait[i] = 0;
if
:: tail == _pid -> tail = 0;
:: else -> skip;
fi
}
}
active [2] proctype reader()
{
loop:
succ_must_wait[_pid] = 1;
d_step {
pred[_pid] = tail;
tail = _pid;
}
trying:
(succ_must_wait[pred[_pid]] == 0)
ncritR++;
release_succ(_pid);
// critical section
progress_reader:
assert(ncritR > 0);
assert(ncritW == 0);
ncritR--;
goto loop;
}
active [2] proctype writer()
{
loop:
succ_must_wait[_pid] = 1;
d_step {
pred[_pid] = tail;
tail = _pid;
}
trying:
(succ_must_wait[pred[_pid]] == 0) && (ncritR == 0)
ncritW++;
// critical section
progress_writer:
assert(ncritR == 0);
assert(ncritW == 1);
ncritW--;
release_succ(_pid);
goto loop;
}
I added the following properties to the model:
p0: the writer with _pid equal to 4 goes through its progress state infinitely often, provided that it is given the chance to execute some instruction infinitely often:
ltl p0 {
([]<> (_last == 4)) ->
([]<> writer[4]#progress_writer)
};
This property should be true.
p1: there is never more than one reader in the critical section:
ltl p1 {
([] (ncritR <= 1))
};
Obviously, we expect this property to be false in a model that matches your specification.
p2: there is never more than one writer in the critical section:
ltl p2 {
([] (ncritW <= 1))
};
This property should be true.
p3: there isn't any node that is the predecessor of two other nodes at the same time, unless such node is node 0:
ltl p3 {
[] (
(((pred[1] != 0) && (pred[2] != 0)) -> (pred[1] != pred[2])) &&
(((pred[1] != 0) && (pred[3] != 0)) -> (pred[1] != pred[3])) &&
(((pred[1] != 0) && (pred[4] != 0)) -> (pred[1] != pred[4])) &&
(((pred[2] != 0) && (pred[3] != 0)) -> (pred[2] != pred[3])) &&
(((pred[2] != 0) && (pred[4] != 0)) -> (pred[2] != pred[4])) &&
(((pred[3] != 0) && (pred[4] != 0)) -> (pred[3] != pred[4]))
)
};
This property should be true.
p4: it is always true that whenever writer with _pid equal to 4 tries to access the critical section then it will eventually get there:
ltl p4 {
[] (writer[4]#trying -> <> writer[4]#progress_writer)
};
This property should be true.
The outcome of the verification matches our expectations:
~$ spin -search -ltl p0 -a clhrw_lock.pml
...
Full statespace search for:
never claim + (p0)
assertion violations + (if within scope of claim)
acceptance cycles + (fairness disabled)
invalid end states - (disabled by never claim)
State-vector 68 byte, depth reached 3305, errors: 0
...
~$ spin -search -ltl p1 -a clhrw_lock.pml
...
Full statespace search for:
never claim + (p1)
assertion violations + (if within scope of claim)
acceptance cycles + (fairness disabled)
invalid end states - (disabled by never claim)
State-vector 68 byte, depth reached 1692, errors: 1
...
~$ spin -search -ltl p2 -a clhrw_lock.pml
...
Full statespace search for:
never claim + (p2)
assertion violations + (if within scope of claim)
acceptance cycles + (fairness disabled)
invalid end states - (disabled by never claim)
State-vector 68 byte, depth reached 3115, errors: 0
...
~$ spin -search -ltl p3 -a clhrw_lock.pml
...
Full statespace search for:
never claim + (p3)
assertion violations + (if within scope of claim)
acceptance cycles + (fairness disabled)
invalid end states - (disabled by never claim)
State-vector 68 byte, depth reached 3115, errors: 0
...
~$ spin -search -ltl p4 -a clhrw_lock.pml
...
Full statespace search for:
never claim + (p4)
assertion violations + (if within scope of claim)
acceptance cycles + (fairness disabled)
invalid end states - (disabled by never claim)
State-vector 68 byte, depth reached 3115, errors: 0
...
Related
Will the output always be greater than 0 ? PROMELA program
I'm a bit boggled by this question, when I ran this program I got results greater than 0 but I'm not sure if that would always be the case since the program could execute x++ or x-- first in theory. How can I definitively confirm that the results will always be bigger than 0 ? proctype testcount(byte x) { do :: (x != 0 ) -> if :: x ++ :: x -- :: break fi :: else -> break od; printf("counter = %d\n", x); } init {run testcount(1)}
This can be easily verified by extending the model with the property you want to verify: ltl larger_or_equal { [] (testcount[1]:x >= 0) }; ltl strictly_larger { [] (testcount[1]:x > 0) }; larger_or_equal: ``It is always the case that x >= 0'' strictly_larger: ``It is always the case that x > 0'' Complete Model: proctype testcount(byte x) { do :: (x != 0 ) -> if :: x ++ :: x -- :: break fi :: else -> break od; printf("counter = %d\n", x); } init { run testcount(1) } ltl larger_or_equal { [] (testcount[1]:x >= 0) }; ltl strictly_larger { [] (testcount[1]:x > 0) }; Generate a verifier, and run it: ~$ spin -a test.pml ~$ gcc pan.c -o run ~$ ./run -a -N larger_or_equal pan: ltl formula larger_or_equal ... Full statespace search for: never claim + (larger_or_equal) assertion violations + (if within scope of claim) acceptance cycles + (fairness disabled) invalid end states - (disabled by never claim) State-vector 28 byte, depth reached 1031, errors: 0 ... ~$ ./run -a -N strictly_larger pan: ltl formula strictly_larger pan:1: assertion violated !( !((testcount[0].x>0))) (at depth 1) pan: wrote test.pml.trail ... Full statespace search for: never claim + (strictly_larger) assertion violations + (if within scope of claim) acceptance cycles + (fairness disabled) invalid end states - (disabled by never claim) State-vector 20 byte, depth reached 1, errors: 1 ... As witnessed by the result of the above verification, property larger_or_equal is always true whereas property strictly_larger can be false.
Promela modeling with Spin
I am working on a promela model that is fairly simple. Using two different modules, it acts as a crosswalk/Traffic light. The first module is the traffic light that outputs the current signal (green, red, yellow, pending). This module also receives as an input a signal called "pedestrian" which acts as an indicator that there are pedestrians wanting to cross. The second module acts as the crosswalk. It receives output signals from the traffic light module (green, yellow, green). It outputs the pedestrian signal to the traffic light module. This module simply defines whether the pedestrian(s) is crossing, waiting or not present. My issue is that once the count value goes to 60, a timeout occurs. I believe the statement "SigG_out ! 1" is causing the error but I do not know why. I have attached the image of the trace I receive from the command line. I am completely new to Spin and Promela and so I am not sure how to use the information form the trace to find my issue in the code. Any help is greatly appreciated. Here is the code for the complete model: mtype = {red, green, yellow, pending, none, crossing, waiting}; mtype traffic_mode; mtype crosswalk_mode; int count; chan pedestrian_chan = [0] of {byte}; chan sigR_chan = [0] of {byte}; chan sigG_chan = [0] of {byte}; chan sigY_chan = [0] of {byte}; ltl l1 {!<> (pedestrian_chan[0] == 1) && (traffic_mode == green || traffic_mode == yellow || traffic_mode == pending)} ltl l2 {[]<> (pedestrian_chan[0] == 1) -> crosswalk_mode == crossing } proctype traffic_controller(chan pedestrian_in, sigR_out, sigG_out, sigY_out) { do ::if ::(traffic_mode == red) -> count = count + 1; if ::(count >= 60) -> sigG_out ! 1; count = 0; traffic_mode = green; :: else -> skip; fi ::(traffic_mode == green) -> if ::(count < 60) -> count = count + 1; ::(pedestrian_in == 1 & count < 60) -> count = count + 1; traffic_mode = pending; ::(pedestrian_in == 1 & count >= 60) count = 0; traffic_mode = yellow; fi ::(traffic_mode == pending) -> count = count + 1; if ::(count >= 60) -> sigY_out ! 1; count = 0; traffic_mode = yellow; ::else -> skip; fi ::(traffic_mode == yellow) -> count = count + 1; if ::(count >= 5) -> sigR_out ! 1; count = 0; traffic_mode = red; :: else -> skip; fi fi od } proctype crosswalk(chan sigR_in, sigG_in, sigY_in, pedestrian_out) { do ::if ::(crosswalk_mode == crossing) -> if ::(sigG_in == 1) -> crosswalk_mode = none; fi ::(crosswalk_mode == none) -> if :: (1 == 1) -> crosswalk_mode = none :: (1 == 1) -> pedestrian_out ! 1 crosswalk_mode = waiting fi ::(crosswalk_mode == waiting) -> if ::(sigR_in == 1) -> crosswalk_mode = crossing; fi fi od } init { count = 0; traffic_mode = red; crosswalk_mode = crossing; atomic { run traffic_controller(pedestrian_chan, sigR_chan, sigG_chan, sigY_chan); run crosswalk(sigR_chan, sigG_chan, sigY_chan, pedestrian_chan); } }
You are using channels incorrectly, this line in particular I wouldn't even know how to interpret it: :: (sigG_in == 1) -> Your channels are synchronous, which means that whenever a process sends something on one side, another process must listen on the other end of the channel in order to deliver the message. Otherwise, the process blocks until when the situation changes. Your channels are synchronous because you declared them of size 0. To read from a channel, you need to use the proper syntax: int some_var; ... some_channel?some_var; // here some_var contains value received through some_channel It seems to be a bit pointless to use three different channels to send different signals. What about using three different values? mtype = { RED, GREEN, YELLOW }; chan c = [0] of { mtype }; ... c!RED ... // (some other process) ... mtype var; c?var; // here var contains RED ...
LTL properties and promela program
I have the following program that models a FIFO with a process in PROMELA: mtype = { PUSH, POP, IS_EMPTY, IS_FULL }; #define PRODUCER_UID 0 #define CONSUMER_UID 1 proctype fifo(chan inputs, outputs) { mtype command; int data, tmp, src_uid; bool data_valid = false; do :: true -> inputs?command(tmp, src_uid); if :: command == PUSH -> if :: data_valid -> outputs!IS_FULL(true, src_uid); :: else -> data = tmp data_valid = true; outputs!PUSH(data, src_uid); fi :: command == POP -> if :: !data_valid -> outputs!IS_EMPTY(true, src_uid); :: else -> outputs!POP(data, src_uid); data = -1; data_valid = false; fi :: command == IS_EMPTY -> outputs!IS_EMPTY(!data_valid, src_uid); :: command == IS_FULL -> outputs!IS_FULL(data_valid, src_uid); fi; od; } proctype producer(chan inputs, outputs) { mtype command; int v; do :: true -> atomic { inputs!IS_FULL(false, PRODUCER_UID) -> outputs?IS_FULL(v, PRODUCER_UID); } if :: v == 1 -> skip :: else -> select(v: 0..16); printf("P[%d] - produced: %d\n", _pid, v); access_fifo: atomic { inputs!PUSH(v, PRODUCER_UID); outputs?command(v, PRODUCER_UID); } assert(command == PUSH); fi; od; } proctype consumer(chan inputs, outputs) { mtype command; int v; do :: true -> atomic { inputs!IS_EMPTY(false, CONSUMER_UID) -> outputs?IS_EMPTY(v, CONSUMER_UID); } if :: v == 1 -> skip :: else -> access_fifo: atomic { inputs!POP(v, CONSUMER_UID); outputs?command(v, CONSUMER_UID); } assert(command == POP); printf("P[%d] - consumed: %d\n", _pid, v); fi; od; } init { chan inputs = [0] of { mtype, int, int }; chan outputs = [0] of { mtype, int, int }; run fifo(inputs, outputs); // pid: 1 run producer(inputs, outputs); // pid: 2 run consumer(inputs, outputs); // pid: 3 } I want to add wr_ptr and rd_ptr in the program to indicate write and read pointers relative to the depth of FIFO when a PUSH update is performed: wr_ptr = wr_ptr % depth; empty=0; if :: (rd_ptr == wr_ptr) -> full=true; fi and similar chances on POP updates Could you please help me to add this to this program? or should i make it an ltl property and use that to check it? from comments: and i want to verify this property, for example If the fifo is full, one should not have a write request, that is the right syntax?full means that fifo is full and wr_idx is the write pointer, I do not know how to access the full, empty, wr_idx, rd_idx, depth on the fifo process in the properties ltl fifo_no_write_when_full {[] (full -> ! wr_idx)}
Here is an example of the process-based FIFO with size 1 that I gave you here adapted for an arbitrary size, which can be configured with FIFO_SIZE. For verification purposes, I would keep this value as small as possible (e.g. 3), because otherwise you are just widening the state space without including any more significant behaviour. mtype = { PUSH, POP, IS_EMPTY, IS_FULL }; #define PRODUCER_UID 0 #define CONSUMER_UID 1 #define FIFO_SIZE 10 proctype fifo(chan inputs, outputs) { mtype command; int tmp, src_uid; int data[FIFO_SIZE]; byte head = 0; byte count = 0; bool res; do :: true -> inputs?command(tmp, src_uid); if :: command == PUSH -> if :: count >= FIFO_SIZE -> outputs!IS_FULL(true, src_uid); :: else -> data[(head + count) % FIFO_SIZE] = tmp; count = count + 1; outputs!PUSH(data[(head + count - 1) % FIFO_SIZE], src_uid); fi :: command == POP -> if :: count <= 0 -> outputs!IS_EMPTY(true, src_uid); :: else -> outputs!POP(data[head], src_uid); atomic { head = (head + 1) % FIFO_SIZE; count = count - 1; } fi :: command == IS_EMPTY -> res = count <= 0; outputs!IS_EMPTY(res, src_uid); :: command == IS_FULL -> res = count >= FIFO_SIZE; outputs!IS_FULL(res, src_uid); fi; od; } No change to producer, consumer or init was necessary: proctype producer(chan inputs, outputs) { mtype command; int v; do :: true -> atomic { inputs!IS_FULL(false, PRODUCER_UID) -> outputs?IS_FULL(v, PRODUCER_UID); } if :: v == 1 -> skip :: else -> select(v: 0..16); printf("P[%d] - produced: %d\n", _pid, v); access_fifo: atomic { inputs!PUSH(v, PRODUCER_UID); outputs?command(v, PRODUCER_UID); } assert(command == PUSH); fi; od; } proctype consumer(chan inputs, outputs) { mtype command; int v; do :: true -> atomic { inputs!IS_EMPTY(false, CONSUMER_UID) -> outputs?IS_EMPTY(v, CONSUMER_UID); } if :: v == 1 -> skip :: else -> access_fifo: atomic { inputs!POP(v, CONSUMER_UID); outputs?command(v, CONSUMER_UID); } assert(command == POP); printf("P[%d] - consumed: %d\n", _pid, v); fi; od; } init { chan inputs = [0] of { mtype, int, int }; chan outputs = [0] of { mtype, int, int }; run fifo(inputs, outputs); // pid: 1 run producer(inputs, outputs); // pid: 2 run consumer(inputs, outputs); // pid: 3 } Now you should have enough material to work on and be ready to write your own properties. On this regard, in your question you write: I do not know how to access the full, empty, wr_idx, rd_idx, depth on the fifo process in the properties ltl fifo_no_write_when_full {[] (full -> ! wr_idx)} First of all, please note that in my code rd_idx corresponds to head, depth (should) correspond to count and that I did not use an explicit wr_idx because the latter can be derived from the former two: it is given by (head + count) % FIFO_SIZE. This is not just a choice of code cleanliness, because having fewer variables in a Promela model actually helps with memory consumption and running time of the verification process. Of course, if you really want to have wr_idx in your model you are free to add it yourself. (: Second, if you look at the Promela manual for ltl properties, you find that: The names or symbols must be defined to represent boolean expressions on global variables from the model. So in other words, it's not possible to put local variables inside an ltl expression. If you want to use them, then you should take them out from the process's local space and put them in the global space. So, to check fifo_no_write_when_full* you could: move the declaration of count out in the global space add a label fifo_write: here: :: command == PUSH -> if :: count >= FIFO_SIZE -> outputs!IS_FULL(true, src_uid); :: else -> fifo_write: data[(head + count) % FIFO_SIZE] = tmp; count = count + 1; outputs!PUSH(data[(head + count - 1) % FIFO_SIZE], src_uid); fi check the property: ltl fifo_no_write_when_full { [] ( (count >= FIFO_SIZE) -> ! fifo#fifo_write) } Third, before any attempt to verify any of your properties with the usual commands, e.g. ~$ spin -a fifo.pml ~$ gcc -o fifo pan.c ~$ ./fifo -a -N fifo_no_write_when_full you should modify producer and consumer so that neither of them executes for an indefinite amount of time and therefore keep the search space at a small depth. Otherwise you are likely to get an error of the sort error: max search depth too small and have the verification exhaust all of your hardware resources without reaching any sensible conclusion. *: actually the name fifo_no_write_when_full is quite generic and might have multiple interpretations, e.g. the fifo does not perform a push when it is full the producer is not able to push if the fifo is full In the example I provided I chose to adopt the first interpretation of the property.
LongAdder Striped64 wasUncontended implementation detail
This is a question not about how LongAdder works, it's about an intriguing implementation detail that I can't figure out. Here is the code from Striped64 (I've cut out some parts and left the relevant parts for the question): final void longAccumulate(long x, LongBinaryOperator fn, boolean wasUncontended) { int h; if ((h = getProbe()) == 0) { ThreadLocalRandom.current(); // force initialization h = getProbe(); wasUncontended = true; } boolean collide = false; // True if last slot nonempty for (;;) { Cell[] as; Cell a; int n; long v; if ((as = cells) != null && (n = as.length) > 0) { if ((a = as[(n - 1) & h]) == null) { //logic to insert the Cell in the array } // CAS already known to fail else if (!wasUncontended) { wasUncontended = true; // Continue after rehash } else if (a.cas(v = a.value, ((fn == null) ? v + x : fn.applyAsLong(v, x)))){ break; } A lot of things from code are clear to me, except for the : // CAS already known to fail else if (!wasUncontended) { wasUncontended = true; // Continue after rehash } Where does this certainty that the following CAS will fail? This is really confusing for me at least, because this check only makes sense for a single case : when some Thread enters the longAccumulate method for the n-th time (n > 1) and the busy spin is at it's first cycle. It's like this code is saying : if you (some Thread) have been here before and you have some contention on a particular Cell slot, don't try to CAS your value to the already existing one, but instead rehash the probe. I honestly hope I will make some sense for someone.
It's not that it will fail, it's more that it has failed. The call to this method is done by the LongAdder add method. public void add(long x) { Cell[] as; long b, v; int m; Cell a; if ((as = cells) != null || !casBase(b = base, b + x)) { boolean uncontended = true; if (as == null || (m = as.length - 1) < 0 || (a = as[getProbe() & m]) == null || !(uncontended = a.cas(v = a.value, v + x))) longAccumulate(x, null, uncontended); } } The first set of conditionals is related to existence of the long Cells. If the necessary cell doesn't exist, then it will try to accumulate uncontended (as there was no attempt to add) by atomically adding the necessary cell and then adding. If the cell does exist, try to add (v + x). If the add failed then there was some form of contention, in that case try to do the accumulating optimistically/atomically (spin until successful) So why does it have wasUncontended = true; // Continue after rehash My best guess is that with heavy contention, it will try to give the running thread time to catch up and will force a retry of the existing cells.
Where is the race condition?
I had a question on a test recently that basically said to make 3 concurrent processes execute some block of code in order. Example of execution order incase that did not make sense: P1 P2 P3 P1 P2 P3 ... For my answer I wrote this pseudo-ish code shared s[2] = {-1,-1}; void Process1(){ while(1){ if(s[0] < 0 && s[1] < 0){ DO_CS; s[0] = 1; } } } void Process2(){ while(1){ if(s[0] > 0 && s[1] < 0){ DO_CS; s[1] = 1; } } } void Process3(){ int i = 0; while(1){ if(s[1] > 0 && s[0] > 0){ DO_CS; s[0] = -1; s[1] = -1; } } } My teacher wrote race condition and circled the last line in the if statement on Process3 and drew an arrow to the conditional statement in process2. I am having trouble seeing how this could cause a race condition. I am sure it is obvious but I just can't see it. Thanks!
Consider the following order of events: After some time, s = [1, 1]. Within Process2, the thread is in the midst of evaluating the expression in the if statement, and just passed the truthy condition s[0] > 0 and is about to continue. Within Process3, you modify s to be [-1, -1]. Process2 evaluates the rest of the expression and goes into action before Process1.