Semaphore implementation without IPC#6155
Conversation
| * | ||
| * @return 0, when returned after mutex was locked | ||
| * @return -1, when the timeout occcured | ||
| */ |
There was a problem hiding this comment.
I would prefer this change to xtimer to be introduced in a separate PR.
There was a problem hiding this comment.
Might make sense, it's in a separate commit in any case :-)
There was a problem hiding this comment.
Yes, but from a review perspective it makes even more sense. The know-how about semaphores and xtimer can be assumed to be split among the reviewers, so this PR would essentially need to ACKs: one from a person knowing about xtimer and one about semaphores ;-).
sys/include/sema.h
Outdated
| * | ||
| * @return Current value of the semaphore. | ||
| */ | ||
| #define SEMA_VALUE(sema) ATOMIC_VALUE(sema->value) |
There was a problem hiding this comment.
Can this be a inline function instead.
| priority_queue_t queue; /**< list of threads waiting for the semaphore */ | ||
| atomic_int_t value; /**< value of the semaphore */ | ||
| sema_state_t state; /**< state of the semaphore */ | ||
| mutex_t mutex; /**< mutex of the semaphore */ |
There was a problem hiding this comment.
Do you really need thread flags and mutex?
There was a problem hiding this comment.
Absolutely right, I removed the flags dependency 👍
There was a problem hiding this comment.
mh.... actually I meant it the other way around. sema used to be based on flags (or rather posix_semaphore was before we took the base implementation out of the POSIX wrapper and made it message-based) and it didn't work out as we thought it might (see also size of the current sema_t struct vs how it was before).
There was a problem hiding this comment.
Before it was unsigned int + priority_queue_node_t, assuming a 32-bit processor this is 4 + 12 = 16 byte. Now it is unsigned int + enum + mutex_t which is 4+4+4 = 12 bytes. This is 4 byte less. One could remove the enum and use signed int for the value (negative meaning destroy) and could save 4 bytes more (the posix implementation uses a signed int).
I liked the point to remove the flags dependency because adding the flags adds 2 bytes to every thread stack even if the semaphore is only used by some of the threads.
There was a problem hiding this comment.
Okay, I somehow thought priority_queue_node_t was just 4 byte...
|
I found two race conditions after creating the PR. I'll tackle them tomorrow. |
|
I am inclined to remove the "enhancement" label because you removed the |
|
@Kijewski good point. The same situation could be claimed however for the mutex: what if you receive a message while in a mutex? both mutex and semaphore are concurrency control mechanisms. When you wait in your code for something (i.e. mutex) you wait for it, the messages are going to be queued. In this case I modified the sema implementation so it doesn't use messages anymore, hence the |
e979f92 to
7d5ab34
Compare
|
@miri64 I fixed the race conditions and:
|
| * @return -EAGAIN, if the thread received a message while waiting for the lock. | ||
| */ | ||
| int sema_wait_timed_msg(sema_t *sema, uint64_t timeout, msg_t *msg); | ||
|
|
There was a problem hiding this comment.
If you remove that: Doesn't lwIP needs fixing, too?
There was a problem hiding this comment.
No, lwIP was calling sema_wait_timed which was calling sema_wait_timed_msg. However, your next point about the return values makes that lwIP requires fixing, I'll rollback the return values to the original ones.
| * @return -EINVAL, if semaphore is invalid. | ||
| * @return -ECANCELED, if the semaphore was destroyed. | ||
| * @return -1 on error (i.e. semaphore was destroyed) | ||
| */ |
There was a problem hiding this comment.
Here and in other places: Changing these return values changes the behavior of the POSIX wrapper (it uses the result to set errno, as specified by the POSIX specifications). Either we drop this behavior (and don't care about POSIX compatibility and also fix the POSIX wrapper accordingly) or these values needs to stay the same.
There was a problem hiding this comment.
I restored the "old" return codes 👍
39e14d2 to
813a9bb
Compare
This PR removes the dependency of IPC from sema, so you won't receive a message while waiting for a semaphore. Did I get this right @lebrush? |
|
Right |
813a9bb to
3147907
Compare
sys/include/sema.h
Outdated
| * | ||
| * @return Current value of the semaphore. | ||
| */ | ||
| static inline unsigned int sema_value(sema_t *sema) |
There was a problem hiding this comment.
Why is this prefered over just taking sema->value (either remove function or rationalize in Doxygen).
There was a problem hiding this comment.
Actually it doesn't make a difference, so I removed it 👍 Then there was no need to touch the semaphore.h file :)
| * @author Christian Mehlis <[email protected]> | ||
| * @author Martine Lenders <[email protected]> | ||
| * @author René Kijewski <[email protected]> | ||
| * @author Víctor Ariño <[email protected]> |
There was a problem hiding this comment.
Keeping us in the Copyright but removing our authorship? ;-)
(though given the amount of code left I understand the latter step)
There was a problem hiding this comment.
Yes, I'm not very clear with that, sorry. Maybe we should write a guideline on when to add/remove an author from a file (I think there's currently any, right?). The criteria I followed is:
sema.h I did some significant additions but most of the code remained, so I added myself. In sema.c the code changed completely, so there was no code left from the previous authors, neither the authors.
I didn't want to remove the copyright because this is the legal thingy... but maybe I should remove it as well. Or do you prefer that I keep all authors and all copyrights?
There was a problem hiding this comment.
I don't really care... Just thought it looked quaint that the copyright was amended and the authors stripped ;-)
3147907 to
b2203b8
Compare
miri64
left a comment
There was a problem hiding this comment.
Last review. One comment that might need some documentation: I see a problem if the semaphore is destroyed and immediately reused afterwards (using sema_create()). Maybe it should be documented somewhere that a semaphore shall only be reused after all its subscribers received the semaphore's destruction.
sys/include/sema.h
Outdated
| * @author René Kijewski <[email protected]> | ||
| * @author Víctor Ariño <[email protected]> | ||
| */ | ||
| #ifndef SEM_H_ |
There was a problem hiding this comment.
Minor legacy error that might be fixable in this PR: s/SEM_H_/SEMA_H_/g
sys/include/sema.h
Outdated
| * @return Statically initialized semaphore. | ||
| */ | ||
| #define SEMA_CREATE(value) { (value), PRIORITY_QUEUE_INIT } | ||
| #define SEMA_CREATE() { (0), SEMA_OK, MUTEX_INIT() } |
There was a problem hiding this comment.
I see benefit in initializing a semaphore with a non-zero value e.g. to let more than one thread pass initially (furthermore, you also kept it for dynamic-equivalent sema_create()). So why change it? Or why must it be 0 (as per documentation).
There was a problem hiding this comment.
Good point, actually it can only be initialized with a value different from 0. In order to initialize with 0 I need to initialize the mutex as locked, and this requires a change in the mutex.h file to make it clean (see #6210).
If that get's merged fast then I'll update this PR, if this PR gets merged first, I'll create a new one :-) The only addition would be:
/**
* @brief Creates 0 initialized semaphore statically.
* @return Statically initialized semaphore.
*/
#define SEMA_CREATE_EMPTY() { (value), SEMA_OK, MUTEX_INIT_LOCKED }| * @param[in] value Initial value for the semaphore. | ||
| * | ||
| * @return 0 on success. | ||
| * @return -EINVAL, if semaphore is invalid. |
There was a problem hiding this comment.
Here and below: please document the assertions you make in the implementation using the @pre doxygen command.
b2203b8 to
934b02b
Compare
sys/include/sema.h
Outdated
| * | ||
| * @param[in] value Initial value for the semaphore. | ||
| * @param[in] value Initial value for the semaphore (can't be 0). For a 0 | ||
| * initialized semaphore @see SEMA_CREATE_EMPTY |
There was a problem hiding this comment.
SEMA_CREATE_EMPTYdoes not exist- I don't understand the reasoning behind this restriction.
There was a problem hiding this comment.
See #6155 (comment) the restriction is that when the semaphore value is 0 the mutex needs to be locked.
There was a problem hiding this comment.
Ah sorry, github hid that from me.
|
@lebrush wrote in a hidden comment
I would call the the macro something like |
c339950 to
73e80da
Compare
|
Your wishes, my commands ;-) done! now there's an issue with the |
|
Aand #6158 got merged. |
|
Super cool! 💃 I just rebased and squashed this one as well |
|
ping @miri64 |
sys/include/sema.h
Outdated
| * @return 0 on success. | ||
| * @return -EINVAL, if semaphore is invalid. | ||
| */ | ||
| int sema_destroy(sema_t *sema); |
There was a problem hiding this comment.
Might be problematic, since it is an API change, but since we only have one state after return it might make sense to set the return type to void.
There was a problem hiding this comment.
I left it to keep the old return values meaningful. This implementation can't fail here so the return value makes no difference, but the same applies to the creation of the semaphore.
I'm happy to change it to void... I've no strong opinion about this one... your call.
There was a problem hiding this comment.
void would minimize the code which in our use case is priority no. 1 ;-). So please go ahead.
| * @param[in] value Initial value for the semaphore. | ||
| * | ||
| * @return 0 on success. | ||
| * @return -EINVAL, if semaphore is invalid. |
There was a problem hiding this comment.
Might be problematic, since it is an API change, but since we only have one state after return it might make sense to set the return type to void.
There was a problem hiding this comment.
There was a problem hiding this comment.
About this you already complained earlier (here [1] and here [2]).
Mh, I would argue that [1] was about the change of the value itself and [2] was about the undocumented precondition (which is a different scope than return values because an unmet precondition leads to a crash, while an error reported by a return-value can be handled gracefully). However the comment in OP of this conversation was about arguing to remove, because according to this documentation the function only has one possible return value (0) and will always succeed, so having a return value seems redundant.
sys/include/sema.h
Outdated
| * | ||
| * @return 0 on success | ||
| * @return -EINVAL, if semaphore is invalid. | ||
| * @return >= 0, on success number of left posts |
There was a problem hiding this comment.
Me English no native. Will fix ;-)
|
Sorry, lost track of this one :-/ |
|
@miri64 I changed Regarding the return values for Let me know if this is Ok for you and I'll squash :-) |
miri64
left a comment
There was a problem hiding this comment.
Tests are still working. Needs squashing.
|
I made them in separate commits for you to easy spot them. Gonna squash :-) |
06a6081 to
6a84655
Compare
|
The error Jenkins reports was my bad in #5937 and is revealed, since you removed the include of diff --git a/pkg/lwip/contrib/sock/lwip_sock.c b/pkg/lwip/contrib/sock/lwip_sock.c
index 8ae799b..20b5faa 100644
--- a/pkg/lwip/contrib/sock/lwip_sock.c
+++ b/pkg/lwip/contrib/sock/lwip_sock.c
@@ -19,6 +19,7 @@
#include "net/ipv4/addr.h"
#include "net/ipv6/addr.h"
#include "net/sock.h"
+#include "timex.h"
#include "lwip/err.h"
#include "lwip/ip.h" |
|
Thanks for being so responsive :-) just added your patch. |
That you have to ask @cgundogan or @smlng. My browser (chrome) just opens the .log files instead of downloading them |
|
mmm locally it works, just rebased to trigger Mr. Murdock again. |
|
@miri64 green lights everywhere!! 🎉 |
|
Then go. |
This is a working semaphore implementation using mutex and thread_flags. It seems to work fine but firstly this is a request for comments.
(fix for #6151)
Depends on: #6158