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

core: Atomic counters #2321

Merged
merged 8 commits into from
May 8, 2015
Merged

Conversation

jnohlgard
Copy link
Member

This PR adds atomic_inc and atomic_dec as discussed in #2302

I have created basic implementations for some of the CPUs as an example.

@jnohlgard jnohlgard added Process: API change Integration Process: PR contains or issue proposes an API change. Should be handled with care. Discussion: RFC The issue/PR is used as a discussion starting point about the item of the issue/PR Area: core Area: RIOT kernel. Handle PRs marked with this with care! Platform: ARM Platform: This PR/issue effects ARM-based platforms Platform: MSP Platform: This PR/issue effects MSP-based platforms Type: enhancement The issue suggests enhanceable parts / The PR enhances parts of the codebase / documentation labels Jan 18, 2015
int atomic_inc(int *val)
{
int old_val;
dINT();
Copy link
Contributor

Choose a reason for hiding this comment

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

this should be a pair of state=disableIRQ()/restoreIRQ(state)

Copy link
Member Author

Choose a reason for hiding this comment

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

I did not check if the msp430 platform was up to date with regards to IRQ functions, since the above atomic_set_return also used eINT/dINT.

I will change this to match the Cortex-M0 implementation.

@kaspar030
Copy link
Contributor

The 'naive' implementation looks like a good candidate for a generic fallback implementation that could be used whenever an architecture doesn't define an optimized version.

Could you do something like, #ifndef ARCH_HAS_ATOMIC_INT; #define atomic_inc atomic_inc_generic; #endif?

@jnohlgard
Copy link
Member Author

@kaspar030 I get the feeling that your suggested #ifdef will only make the platform configuration more verbose. Do we really have that many CPU architectures that we need this? Otherwise I suggest to just copy the generic implementation to each CPU family as a starting point, then write optimized versions for each CPU whenever someone feel like improving it.

@kaspar030
Copy link
Contributor

@gebart Well, I prefer one #define ARCH_HAS_ATOMIC_INC in the per-cpu header than a copied generic version in the generic case. Seems more natural: initially, we don't have a specialized version. Without any change, the generic version will be used. Once someone 'overrides' the generic version, he adds the corresponding define.

That way, if any problem shows up, it's also easy to disable any specialized function by commenting out the define...

@jnohlgard jnohlgard added the CI: needs squashing Commits in this PR need to be squashed; If set, CI systems will mark this PR as unmergable label Jan 25, 2015
@jnohlgard
Copy link
Member Author

@kaspar030 I updated the PR according to your suggestions. I also removed copies of the generic atomic_arch_set_return to reduce code duplication further. The generic interface is defined in core/atomic.c and protected by an #if ARCH_HAS_ATOMIC_INC != 0

Missing is an implementation for x86 (x86_atomic.c) because I am not familiar enough with x86 assembly language to implement it.

@jnohlgard
Copy link
Member Author

should I remove the API CHANGE label? This PR only adds functions to the core API.

@haukepetersen
Copy link
Contributor

I just stumbled upon a use case, where you need to increment/decrement a value atomically by a given number (e.g. inc by 3). What is your opinion for including this into the proposed API, say something like

int atomic_arch_inc(int *val, int howmuch);

?

@jnohlgard
Copy link
Member Author

@haukepetersen What is your use case?

Maybe we should introduce @Kijewski 's suggestion of Compare And Swap?

Then you would write:

atomic_cas(&val, val, val+3);

My first use case is: Atomic counter as a counting semaphore to introduce inhibiting of low power modes when modules are using hardware devices which need the CPU to stay alive. i.e. a reference count on power modes. In this case we only need up/down counting in steps of 1.

@haukepetersen
Copy link
Contributor

My use case is a simple usage counter, that keeps track of the number of threads that are being passed a (read-)pointer to a data structure. If a thread is done reading from it, it decrements the counter again (atomically, of course). If I send this pointer to 4 different threads, I have to increment it (atomically) 4 times before sending. This would be best looking with such function...

I like @Kijewski 's suggestion, works very well for me. Does it even make sense to just have this one function, as you can do everything we need with it?

@jnohlgard
Copy link
Member Author

@haukepetersen , @Kijewski , it does make sense to implement atomic_cas.
Any objections to rewriting this code as @Kijewski 's suggestion in #2302 (comment) ?

I would be leaving atomic_cas as a public function which can be used for special cases of atomic variables, such as @haukepetersen 's use case, but the simplest way to use the API for everyday use would still be to call either atomic_inc/atomic_dec or atomic_set/atomic_reset.

@haukepetersen
Copy link
Contributor

+1 for implementing atomic_cas.

But I would really think of slimming the interface down to this one function, for the sake of resource efficiency. This is a kernel function that is normally only used by people who know what they are doing, it's not an everyday interface as e.g. threading or mutexes...

@kaspar030
Copy link
Contributor

@haukepetersen Well, if you need more than one line to do atomic_inc, something's wrong with the API.
And you need that if there's only atomic_cas, right?

When we're touching this, we should think about introducing special types for atomic values.

@jnohlgard
Copy link
Member Author

@haukepetersen I liked the static inline version of the counters that @Kijewski wrote. Makes the code using it clean and simple to understand, without knowing the details of atomic_cas and why it has to be inside a loop

@haukepetersen
Copy link
Contributor

ok, I guess you convinced me...

@jnohlgard
Copy link
Member Author

@kaspar030 how do you propose these atomic types would be used?
If I wrote this in C++ I would overload a bunch of operators on them to keep the code clear and readable, but I guess in C we'll have to do something similar to byteorder.h, or am I missing something?

@jnohlgard
Copy link
Member Author

simply doing the following will not warn if passed a regular int variable:

typedef int atomic_int_t;

How can I enforce strict type checking in C? Should I change atomic_int_t into a union instead?

@jnohlgard jnohlgard force-pushed the pr/atomic-counters branch 2 times, most recently from d26d8c0 to c461171 Compare March 10, 2015 17:42
@jnohlgard
Copy link
Member Author

Rebased and updated with a generic atomic_cas function for all atomic operations as per @Kijewski 's suggestion.

TODO (in a separate PR): Update mutex.c etc to use atomic_int_t and atomic_set/clear instead of old atomic_set_return.

@jnohlgard jnohlgard added the Platform: AVR Platform: This PR/issue effects AVR-based platforms label Mar 10, 2015
int atomic_cas(atomic_int_t *var, int old, int now)
{
unsigned int mask = disableIRQ();
if (var->val != old) {
Copy link
Contributor

Choose a reason for hiding this comment

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

you need to restore the IRQs.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thank you, updated the PR accordingly.

@jnohlgard jnohlgard added Platform: native Platform: This PR/issue effects the native platform x86 labels May 7, 2015
#ifndef _ARM_CPU_H
#define _ARM_CPU_H
#ifndef ARM_CPU_H_
#define ARM_CPU_H_
Copy link
Contributor

Choose a reason for hiding this comment

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

%s,#endif // _ARM_CPU_H,#endif /* ARM_CPU_H_ */,

Copy link
Member Author

Choose a reason for hiding this comment

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

done.

@Kijewski
Copy link
Contributor

Kijewski commented May 7, 2015

But for my last comment the PR should be ready to merge.

@jnohlgard jnohlgard force-pushed the pr/atomic-counters branch from 759cd6b to f8f86d3 Compare May 7, 2015 14:45
@jnohlgard
Copy link
Member Author

@kaspar030 ACK still holds?

OK to squash?

@kaspar030
Copy link
Contributor

Yes!

@jnohlgard jnohlgard force-pushed the pr/atomic-counters branch from f8f86d3 to 83c735a Compare May 7, 2015 16:51
@jnohlgard jnohlgard removed the CI: needs squashing Commits in this PR need to be squashed; If set, CI systems will mark this PR as unmergable label May 7, 2015
Joakim Gebart and others added 8 commits May 7, 2015 18:52
 - Move generic implementation of atomic_set_return to core/atomic.c
 - Generic implementation of atomic compare and swap in core/atomic.c
 - atomic_cas is used to implement atomic counters in core/include/atomic.h
 - atomic_int_t is an atomic integer type
 - ATOMIC_INIT can be used as an initializer for atomic_int_t
 - ATOMIC_VALUE gets a reference to the value of an atomic integer
The removed implementation is the same as the generic implementation in core/atomic.c
@jnohlgard jnohlgard force-pushed the pr/atomic-counters branch from 83c735a to f15fc17 Compare May 7, 2015 16:52
@jnohlgard
Copy link
Member Author

Squashed and rebased, waiting for Travis.
@Kijewski Can I take your comment as an ACK?

@OlegHahm
Copy link
Member

OlegHahm commented May 8, 2015

Travis fell asleep for arm7 build tests. Kicked it.

@OlegHahm
Copy link
Member

OlegHahm commented May 8, 2015

ACK and go.

OlegHahm added a commit that referenced this pull request May 8, 2015
@OlegHahm OlegHahm merged commit 021226f into RIOT-OS:master May 8, 2015
@jnohlgard jnohlgard deleted the pr/atomic-counters branch May 10, 2015 08:26
miri64 added a commit to miri64/RIOT that referenced this pull request Feb 16, 2016
lebrush pushed a commit to lebrush/RIOT that referenced this pull request Jun 27, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: core Area: RIOT kernel. Handle PRs marked with this with care! Platform: ARM Platform: This PR/issue effects ARM-based platforms Platform: AVR Platform: This PR/issue effects AVR-based platforms Platform: MSP Platform: This PR/issue effects MSP-based platforms Platform: native Platform: This PR/issue effects the native platform Process: API change Integration Process: PR contains or issue proposes an API change. Should be handled with care. Type: enhancement The issue suggests enhanceable parts / The PR enhances parts of the codebase / documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants