Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Atomic extensions: 64-bit, bool, exchange #9600

Merged
merged 5 commits into from
Feb 13, 2019

Conversation

kjbracey
Copy link
Contributor

@kjbracey kjbracey commented Feb 4, 2019

Description

Three extensions to the atomic functions in mbed_critical.h:

  • Exchange operations
  • Operations on bool (load, store, exchange, cas)
  • Operations on uint64_t (load, store, exchange, cas, incr, decr)

Bool exchange could have been used by #9520 - slightly tidying and shrinking the code.

Exchange and 64-bit operations are anticipated to be needed as part of ARMv6-M retargetting for ARMC6 atomics, as per its library documentation.

Pull request type

[ ] Fix
[ ] Refactor
[ ] Target update
[X] Functionality change
[ ] Docs update
[ ] Test update
[ ] Breaking change

Release notes

(Also covering #9247)

  • mbed_critical.h now also offers atomic_load, atomic_store and atomic_exchange functions.
  • mbed_critical.h now supports atomic bool, int8_t, int16_t, int32_t, uint64_t and int64_t.

Previously code had to just do plain C loads and stores of "atomic" variables, as no atomic operations were provided by the library. Such direct accesses are indeed atomic in ARM hardware and compilers (up to 32-bit when properly aligned), but did not provide any ordering semantics. Having real atomic accesses provides a full and safe replacement for volatile for interthread or thread-to-interrupt synchronisation.

Old style example (very synthetic):

uint32_t data; 
volatile bool ready;

irq_handler() { // assume single-shot, so doesn't re-run while foreground processes
    data = REG;
    ready = true;  // XXX ready could be written before data!
}

// volatile qualifier forces reload each time
while (!ready) {
}
do_something(data); // XXX data could be loaded before "wait for ready" loop.

A workaround for the ordering problems above is to also mark data as volatile, but this scales poorly, and can impede optimisation of work on data. Plus it would still not be safe on a multi-CPU system - the compiler wouldn't reorder, but the CPU still might, leading to a different order visible on another CPU.

New style example:

uint32_t data; 
bool ready; // volatile not needed - atomic functions do the work

irq_handler() { // assume single-shot, so doesn't re-run while foreground processes
    data = REG;
    core_util_atomic_store_bool(&ready, true); // data is written first - atomic store "releases" it
}

// Compiler will reload each time
while (!core_util_atomic_load_bool(&ready) {
}
do_something(data); // data is loaded after seeing ready set (atomic load "acquires" the data)

Note that you don't need to mark atomic data or atomic-protected data as volatile, just as you don't need to mark mutexes or mutex-protected data as volatile. The atomics and mutexes are inherently multi-thread safe, and if they correctly synchronise the data, the data is not subject to racey accesses.

Migration guide

  • Check for existing uses of the volatile type qualifier in code. It should not be used anywhere except for accessing memory-mapped I/O registers. All other uses (eg inter-thread communication or thread<->interrupt communication) can be replaced with atomic operations on non-volatile types.
  • Check to see if uses of atomic_cas can be simplified to atomic_exchange - particularly for bool.
  • Remove casting or select a better type from the new atomic types available.
  • Use 64-bit atomic functions rather than rolling your own with enter/exit critical.

Reviewers

@pan-, @VeijoPesonen

@ciarmcom ciarmcom requested review from pan-, VeijoPesonen and a team February 4, 2019 12:00
@ciarmcom
Copy link
Member

ciarmcom commented Feb 4, 2019

@kjbracey-arm, thank you for your changes.
@VeijoPesonen @pan- @ARMmbed/mbed-os-core @ARMmbed/mbed-os-maintainers please review.

@ciarmcom ciarmcom requested a review from a team February 4, 2019 12:00
@kjbracey
Copy link
Contributor Author

kjbracey commented Feb 4, 2019

Note that "exchange" naming is inconsistent with "cas" compare-and-swap, but I would rather stay close to C11/C++11. Maybe at some point "cas" gets renamed to match C++11.

Even when we move to C++11, we may not necessarily be able to totally abandon these functions in favour of built-in compiler atomics. Some compiler versions might use spinlocks that are not safe versus interrupts or hard RTOS thread priorities, and might insert unneeded SMP barriers. It seems compilers+libraries tend to assume "application code with non-hard thread priorities".

@kjbracey kjbracey force-pushed the atomic_exchange_64 branch 2 times, most recently from 4645f1e to 546abe7 Compare February 4, 2019 12:40
platform/mbed_critical.c Outdated Show resolved Hide resolved
platform/mbed_critical.c Outdated Show resolved Hide resolved
All signed implementations are inline - either directly or as inline
calls to out-of-line unsigned definitions.
@kjbracey
Copy link
Contributor Author

kjbracey commented Feb 5, 2019

Bit more added - signed versions of everything. Can see people needing it and being forced to do their own ugly casting.

Copy link
Member

@pan- pan- left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great additions. What would be missing to provide mbed::Atomic<T> ?

@kjbracey
Copy link
Contributor Author

kjbracey commented Feb 5, 2019

Great additions. What would be missing to provide mbed::Atomic ?

Low value for amount of code written ratio?

There are very few atomic users, and many of them seem to be problematic (some tidy+fix patches pending from me soon). This PR is partly as the result of that tree search - seeing the people whose code was ugly due to lack of bool, signed or exchange.

But if my concerns above do hold that the off-the-shelf C library atomics might not be suitable for protecting privileged code against interrupts, then maybe it is worth fleshing this out further, including templates.

On the whole though - I'm usually happier seeing people using mutexes. Code is more likely to be correct. I almost want don't want to encourage people to use this :/ Templates might make it too easy.

@0xc0170
Copy link
Contributor

0xc0170 commented Feb 11, 2019

@kjbracey-arm Please add release notes section, following https://os.mbed.com/docs/mbed-os/v5.11/contributing/workflow.html#functionality-change

@kjbracey
Copy link
Contributor Author

Added a chunk of release note text.

@@ -100,6 +100,9 @@ void core_util_critical_section_exit(void)
}
}

/* Inline bool implementations in the header use uint8_t versions to manipulate the bool */
MBED_STATIC_ASSERT(sizeof(bool) == sizeof(uint8_t), "Surely bool is a byte");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😮

@cmonr
Copy link
Contributor

cmonr commented Feb 11, 2019

CI started

@mbed-ci
Copy link

mbed-ci commented Feb 12, 2019

Test run: SUCCESS

Summary: 12 of 12 test jobs passed
Build number : 1
Build artifacts

@0xc0170
Copy link
Contributor

0xc0170 commented Feb 12, 2019

Final review from @ARMmbed/mbed-os-core requested

@0xc0170
Copy link
Contributor

0xc0170 commented Feb 12, 2019

@AnotherButler Please review docs and release notes section

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants