Skip to content

Commit f8e3f5d

Browse files
committed
Add 64-bit atomics
1 parent 94f5646 commit f8e3f5d

File tree

3 files changed

+113
-165
lines changed

3 files changed

+113
-165
lines changed

UNITTESTS/stubs/mbed_critical_stub.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,31 @@ uint32_t core_util_atomic_decr_u32(volatile uint32_t *valuePtr, uint32_t delta)
103103
}
104104

105105

106+
uint64_t core_util_atomic_load_u64(const volatile uint64_t *valuePtr)
107+
{
108+
return 0;
109+
}
110+
111+
void core_util_atomic_store_u64(volatile uint64_t *valuePtr, uint64_t desiredValue)
112+
{
113+
}
114+
115+
bool core_util_atomic_cas_u64(volatile uint64_t *ptr, uint64_t *expectedCurrentValue, uint64_t desiredValue)
116+
{
117+
return false;
118+
}
119+
120+
uint64_t core_util_atomic_incr_u64(volatile uint64_t *valuePtr, uint64_t delta)
121+
{
122+
return 0;
123+
}
124+
125+
uint64_t core_util_atomic_decr_u64(volatile uint64_t *valuePtr, uint64_t delta)
126+
{
127+
return 0;
128+
}
129+
130+
106131
bool core_util_atomic_cas_ptr(void *volatile *ptr, void **expectedCurrentValue, void *desiredValue)
107132
{
108133
return false;

platform/mbed_critical.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,58 @@ uint32_t core_util_atomic_decr_u32(volatile uint32_t *valuePtr, uint32_t delta)
358358

359359
#endif
360360

361+
/* No architecture we support has LDREXD/STREXD, so must always disable IRQs for 64-bit operations */
362+
uint64_t core_util_atomic_load_u64(const volatile uint64_t *valuePtr)
363+
{
364+
core_util_critical_section_enter();
365+
uint64_t currentValue = *valuePtr;
366+
core_util_critical_section_exit();
367+
return currentValue;
368+
}
369+
370+
void core_util_atomic_store_u64(volatile uint64_t *valuePtr, uint64_t desiredValue)
371+
{
372+
core_util_critical_section_enter();
373+
*valuePtr = desiredValue;
374+
core_util_critical_section_exit();
375+
}
376+
377+
bool core_util_atomic_cas_u64(volatile uint64_t *ptr, uint64_t *expectedCurrentValue, uint64_t desiredValue)
378+
{
379+
bool success;
380+
uint64_t currentValue;
381+
core_util_critical_section_enter();
382+
currentValue = *ptr;
383+
if (currentValue == *expectedCurrentValue) {
384+
*ptr = desiredValue;
385+
success = true;
386+
} else {
387+
*expectedCurrentValue = currentValue;
388+
success = false;
389+
}
390+
core_util_critical_section_exit();
391+
return success;
392+
}
393+
394+
uint64_t core_util_atomic_incr_u64(volatile uint64_t *valuePtr, uint64_t delta)
395+
{
396+
uint64_t newValue;
397+
core_util_critical_section_enter();
398+
newValue = *valuePtr + delta;
399+
*valuePtr = newValue;
400+
core_util_critical_section_exit();
401+
return newValue;
402+
}
403+
404+
uint64_t core_util_atomic_decr_u64(volatile uint64_t *valuePtr, uint64_t delta)
405+
{
406+
uint64_t newValue;
407+
core_util_critical_section_enter();
408+
newValue = *valuePtr - delta;
409+
*valuePtr = newValue;
410+
core_util_critical_section_exit();
411+
return newValue;
412+
}
361413

362414
bool core_util_atomic_cas_ptr(void *volatile *ptr, void **expectedCurrentValue, void *desiredValue)
363415
{

platform/mbed_critical.h

Lines changed: 36 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -202,175 +202,16 @@ MBED_FORCEINLINE void core_util_atomic_flag_clear(volatile core_util_atomic_flag
202202
*/
203203
bool core_util_atomic_cas_u8(volatile uint8_t *ptr, uint8_t *expectedCurrentValue, uint8_t desiredValue);
204204

205-
/**
206-
* Atomic compare and set. It compares the contents of a memory location to a
207-
* given value and, only if they are the same, modifies the contents of that
208-
* memory location to a given new value. This is done as a single atomic
209-
* operation. The atomicity guarantees that the new value is calculated based on
210-
* up-to-date information; if the value had been updated by another thread in
211-
* the meantime, the write would fail due to a mismatched expectedCurrentValue.
212-
*
213-
* Refer to https://en.wikipedia.org/wiki/Compare-and-set [which may redirect
214-
* you to the article on compare-and swap].
215-
*
216-
* @param ptr The target memory location.
217-
* @param[in,out] expectedCurrentValue A pointer to some location holding the
218-
* expected current value of the data being set atomically.
219-
* The computed 'desiredValue' should be a function of this current value.
220-
* @note: This is an in-out parameter. In the
221-
* failure case of atomic_cas (where the
222-
* destination isn't set), the pointee of expectedCurrentValue is
223-
* updated with the current value.
224-
* @param[in] desiredValue The new value computed based on '*expectedCurrentValue'.
225-
*
226-
* @return true if the memory location was atomically
227-
* updated with the desired value (after verifying
228-
* that it contained the expectedCurrentValue),
229-
* false otherwise. In the failure case,
230-
* exepctedCurrentValue is updated with the new
231-
* value of the target memory location.
232-
*
233-
* pseudocode:
234-
* function cas(p : pointer to int, old : pointer to int, new : int) returns bool {
235-
* if *p != *old {
236-
* *old = *p
237-
* return false
238-
* }
239-
* *p = new
240-
* return true
241-
* }
242-
*
243-
* @note: In the failure case (where the destination isn't set), the value
244-
* pointed to by expectedCurrentValue is instead updated with the current value.
245-
* This property helps writing concise code for the following incr:
246-
*
247-
* function incr(p : pointer to int, a : int) returns int {
248-
* done = false
249-
* value = *p // This fetch operation need not be atomic.
250-
* while not done {
251-
* done = atomic_cas(p, &value, value + a) // *value gets updated automatically until success
252-
* }
253-
* return value + a
254-
* }
255-
*
256-
* @note: This corresponds to the C11 "atomic_compare_exchange_strong" - it
257-
* always succeeds if the current value is expected, as per the pseudocode
258-
* above; it will not spuriously fail as "atomic_compare_exchange_weak" may.
259-
*/
205+
/** \copydoc core_util_atomic_cas_u8 */
260206
bool core_util_atomic_cas_u16(volatile uint16_t *ptr, uint16_t *expectedCurrentValue, uint16_t desiredValue);
261207

262-
/**
263-
* Atomic compare and set. It compares the contents of a memory location to a
264-
* given value and, only if they are the same, modifies the contents of that
265-
* memory location to a given new value. This is done as a single atomic
266-
* operation. The atomicity guarantees that the new value is calculated based on
267-
* up-to-date information; if the value had been updated by another thread in
268-
* the meantime, the write would fail due to a mismatched expectedCurrentValue.
269-
*
270-
* Refer to https://en.wikipedia.org/wiki/Compare-and-set [which may redirect
271-
* you to the article on compare-and swap].
272-
*
273-
* @param ptr The target memory location.
274-
* @param[in,out] expectedCurrentValue A pointer to some location holding the
275-
* expected current value of the data being set atomically.
276-
* The computed 'desiredValue' should be a function of this current value.
277-
* @note: This is an in-out parameter. In the
278-
* failure case of atomic_cas (where the
279-
* destination isn't set), the pointee of expectedCurrentValue is
280-
* updated with the current value.
281-
* @param[in] desiredValue The new value computed based on '*expectedCurrentValue'.
282-
*
283-
* @return true if the memory location was atomically
284-
* updated with the desired value (after verifying
285-
* that it contained the expectedCurrentValue),
286-
* false otherwise. In the failure case,
287-
* exepctedCurrentValue is updated with the new
288-
* value of the target memory location.
289-
*
290-
* pseudocode:
291-
* function cas(p : pointer to int, old : pointer to int, new : int) returns bool {
292-
* if *p != *old {
293-
* *old = *p
294-
* return false
295-
* }
296-
* *p = new
297-
* return true
298-
* }
299-
*
300-
* @note: In the failure case (where the destination isn't set), the value
301-
* pointed to by expectedCurrentValue is instead updated with the current value.
302-
* This property helps writing concise code for the following incr:
303-
*
304-
* function incr(p : pointer to int, a : int) returns int {
305-
* done = false
306-
* value = *p // This fetch operation need not be atomic.
307-
* while not done {
308-
* done = atomic_cas(p, &value, value + a) // *value gets updated automatically until success
309-
* }
310-
* return value + a
311-
*
312-
* @note: This corresponds to the C11 "atomic_compare_exchange_strong" - it
313-
* always succeeds if the current value is expected, as per the pseudocode
314-
* above; it will not spuriously fail as "atomic_compare_exchange_weak" may.
315-
* }
316-
*/
208+
/** \copydoc core_util_atomic_cas_u8 */
317209
bool core_util_atomic_cas_u32(volatile uint32_t *ptr, uint32_t *expectedCurrentValue, uint32_t desiredValue);
318210

319-
/**
320-
* Atomic compare and set. It compares the contents of a memory location to a
321-
* given value and, only if they are the same, modifies the contents of that
322-
* memory location to a given new value. This is done as a single atomic
323-
* operation. The atomicity guarantees that the new value is calculated based on
324-
* up-to-date information; if the value had been updated by another thread in
325-
* the meantime, the write would fail due to a mismatched expectedCurrentValue.
326-
*
327-
* Refer to https://en.wikipedia.org/wiki/Compare-and-set [which may redirect
328-
* you to the article on compare-and swap].
329-
*
330-
* @param ptr The target memory location.
331-
* @param[in,out] expectedCurrentValue A pointer to some location holding the
332-
* expected current value of the data being set atomically.
333-
* The computed 'desiredValue' should be a function of this current value.
334-
* @note: This is an in-out parameter. In the
335-
* failure case of atomic_cas (where the
336-
* destination isn't set), the pointee of expectedCurrentValue is
337-
* updated with the current value.
338-
* @param[in] desiredValue The new value computed based on '*expectedCurrentValue'.
339-
*
340-
* @return true if the memory location was atomically
341-
* updated with the desired value (after verifying
342-
* that it contained the expectedCurrentValue),
343-
* false otherwise. In the failure case,
344-
* exepctedCurrentValue is updated with the new
345-
* value of the target memory location.
346-
*
347-
* pseudocode:
348-
* function cas(p : pointer to int, old : pointer to int, new : int) returns bool {
349-
* if *p != *old {
350-
* *old = *p
351-
* return false
352-
* }
353-
* *p = new
354-
* return true
355-
* }
356-
*
357-
* @note: In the failure case (where the destination isn't set), the value
358-
* pointed to by expectedCurrentValue is instead updated with the current value.
359-
* This property helps writing concise code for the following incr:
360-
*
361-
* function incr(p : pointer to int, a : int) returns int {
362-
* done = false
363-
* value = *p // This fetch operation need not be atomic.
364-
* while not done {
365-
* done = atomic_cas(p, &value, value + a) // *value gets updated automatically until success
366-
* }
367-
* return value + a
368-
* }
369-
*
370-
* @note: This corresponds to the C11 "atomic_compare_exchange_strong" - it
371-
* always succeeds if the current value is expected, as per the pseudocode
372-
* above; it will not spuriously fail as "atomic_compare_exchange_weak" may.
373-
*/
211+
/** \copydoc core_util_atomic_cas_u8 */
212+
bool core_util_atomic_cas_u64(volatile uint64_t *ptr, uint64_t *expectedCurrentValue, uint64_t desiredValue);
213+
214+
/** \copydoc core_util_atomic_cas_u8 */
374215
bool core_util_atomic_cas_ptr(void *volatile *ptr, void **expectedCurrentValue, void *desiredValue);
375216

376217
/**
@@ -409,6 +250,13 @@ MBED_FORCEINLINE uint32_t core_util_atomic_load_u32(const volatile uint32_t *val
409250
return value;
410251
}
411252

253+
/**
254+
* Atomic load.
255+
* @param valuePtr Target memory location.
256+
* @return The loaded value.
257+
*/
258+
uint64_t core_util_atomic_load_u64(const volatile uint64_t *valuePtr);
259+
412260
/**
413261
* Atomic load.
414262
* @param valuePtr Target memory location.
@@ -457,6 +305,13 @@ MBED_FORCEINLINE void core_util_atomic_store_u32(volatile uint32_t *valuePtr, ui
457305
MBED_BARRIER();
458306
}
459307

308+
/**
309+
* Atomic store.
310+
* @param valuePtr Target memory location.
311+
* @param desiredValue The value to store.
312+
*/
313+
void core_util_atomic_store_u64(volatile uint64_t *valuePtr, uint64_t desiredValue);
314+
460315
/**
461316
* Atomic store.
462317
* @param valuePtr Target memory location.
@@ -493,6 +348,14 @@ uint16_t core_util_atomic_incr_u16(volatile uint16_t *valuePtr, uint16_t delta);
493348
*/
494349
uint32_t core_util_atomic_incr_u32(volatile uint32_t *valuePtr, uint32_t delta);
495350

351+
/**
352+
* Atomic increment.
353+
* @param valuePtr Target memory location being incremented.
354+
* @param delta The amount being incremented.
355+
* @return The new incremented value.
356+
*/
357+
uint64_t core_util_atomic_incr_u64(volatile uint64_t *valuePtr, uint64_t delta);
358+
496359
/**
497360
* Atomic increment.
498361
* @param valuePtr Target memory location being incremented.
@@ -528,6 +391,14 @@ uint16_t core_util_atomic_decr_u16(volatile uint16_t *valuePtr, uint16_t delta);
528391
*/
529392
uint32_t core_util_atomic_decr_u32(volatile uint32_t *valuePtr, uint32_t delta);
530393

394+
/**
395+
* Atomic decrement.
396+
* @param valuePtr Target memory location being decremented.
397+
* @param delta The amount being decremented.
398+
* @return The new decremented value.
399+
*/
400+
uint64_t core_util_atomic_decr_u64(volatile uint64_t *valuePtr, uint64_t delta);
401+
531402
/**
532403
* Atomic decrement.
533404
* @param valuePtr Target memory location being decremented.

0 commit comments

Comments
 (0)