Forráskód Böngészése

o patch by Nico Erfurth:

* Fix for InterLockedCompareExchange on ARMEL

InterLockedCompareExchange would not return the current data on failure.
Getting this to work correctly is a bit tricky. As kuser_cmpxchg does
not return the set value, we have to load it.
There is a tiny chance that we get rescheduled between calling
kuser_cmpxchg and loading the value. If the value changed in between
there is the possibility that we would return the Comperand without
having done an actual swap. Which might cause havoc and destruction.

So, if the exchange failed, compare the value and loop again in case
of CurrentValue == Comperand.

* Improve testing of InterLockedCompareExchange

Added a test to check for the case when Comperand is different from the
current value.

git-svn-id: trunk@20514 -
florian 13 éve
szülő
commit
e9c5458dd2
2 módosított fájl, 27 hozzáadás és 6 törlés
  1. 17 5
      rtl/arm/arm.inc
  2. 10 1
      tests/test/units/system/interlocked1.pp

+ 17 - 5
rtl/arm/arm.inc

@@ -635,7 +635,7 @@ asm
   mvn r3, #0x0000f000
   sub r3, r3, #0x3F
 
-  add r1, r0, #1 // Decrement value
+  add r1, r0, #1 // Increment value
   blx r3	 // Call kuser_cmpxchg, sets C-Flag on success
 
   movcs r0, r1	 // We expect that to work most of the time so keep it pipeline friendly
@@ -765,7 +765,6 @@ asm
 {$if defined(LINUX) and defined(CPUARMEL)}
 
   stmfd r13!, {r4, lr}
-
   mvn   r3, #0x0000f000
   sub   r3, r3, #0x3F
 
@@ -773,10 +772,23 @@ asm
   mov   r2, r0
   mov   r0, r4 // Use r4 because we'll need the new value for later
 
+  // r1 and r2 will not be clobbered by kuser_cmpxchg
+  // If we have to loop, r0 will be set to the original Comperand
+  .Linterlocked_compare_exchange_loop:
+
   blx   r3       // Call kuser_cmpxchg sets C-Flag on success
-  ldrcc r0, [r2] // Load the currently set value on failure
-  movcs r0, r4   // Load the previous value on success
-  ldmfd r13!, {r4, pc}
+  movcs r0, r4   // Return the previous value on success
+  ldmcsfd r13!, {r4, pc}
+  // The error case is a bit tricky, kuser_cmpxchg does not return the current value
+  // So we may need to loop to avoid race conditions
+  // The loop case is HIGHLY unlikely, it would require that we got rescheduled between
+  // calling kuser_cmpxchg and the ldr. While beeing rescheduled another process/thread
+  // would have the set the value to our comperand
+  ldr	r0, [r2] // Load the currently set value
+  cmp   r0, r4   // Return if Comperand != current value, otherwise loop again
+  ldmnefd r13!, {r4, pc}
+  // If we need to loop here, we have to
+  b	.Linterlocked_compare_exchange_loop
 
 {$else}
 // lock

+ 10 - 1
tests/test/units/system/interlocked1.pp

@@ -7,9 +7,18 @@ begin
   InterLockedCompareExchange(target,4321,1234);
   if target<>4321 then
     halt(1);
+
   ctarget:=1234;
   InterLockedCompareExchange(ctarget,4321,1234);
   if ctarget<>4321 then
-    halt(1);
+    halt(2);
+
+  // Test what happens if we use a comparend which is NOT currently set
+  target := 12345;
+  if(InterLockedCompareExchange(target, 54321, 123) <> 12345) then
+    halt(3);
+  if target<>12345 then
+    halt(4);
+
   writeln('ok');
 end.