-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Semaphore implementation without IPC #6155
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First superficial look.
* @return 0, when returned after mutex was locked | ||
* @return -1, when the timeout occcured | ||
*/ | ||
int xtimer_mutex_lock_timeout(mutex_t *mutex, uint64_t us); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer this change to xtimer to be introduced in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might make sense, it's in a separate commit in any case :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 ;-).
* | ||
* @return Current value of the semaphore. | ||
*/ | ||
#define SEMA_VALUE(sema) ATOMIC_VALUE(sema->value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be a inline function instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done 👍
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you really need thread flags and mutex?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely right, I removed the flags dependency 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 -ECANCELED, if the semaphore was destroyed. | ||
* @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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you remove that: Doesn't lwIP needs fixing, too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
@@ -129,22 +117,28 @@ int sema_wait_timed(sema_t *sema, uint64_t timeout); | |||
* @param[in] sema A semaphore. | |||
* | |||
* @return 0 on success | |||
* @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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lwIP wrappers in (Sorry, thought the API change was more significant).pkg/lwip/contrib/sys_arch.c
need adaption too, btw.
* | ||
* @return Current value of the semaphore. | ||
*/ | ||
static inline unsigned int sema_value(sema_t *sema) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this prefered over just taking sema->value
(either remove function or rationalize in Doxygen).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 <mehlis@inf.fu-berlin.de> | ||
* @author Martine Lenders <mlenders@inf.fu-berlin.de> | ||
* @author René Kijewski <kijewski@inf.fu-berlin.de> | ||
* @author Víctor Ariño <victor.arino@zii.aero> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really care... Just thought it looked quaint that the copyright was amended and the authors stripped ;-)
3147907
to
b2203b8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
@@ -18,13 +18,14 @@ | |||
* @author Martine Lenders <mlenders@inf.fu-berlin.de> | |||
* @author Christian Mehlis <mehlis@inf.fu-berlin.de> | |||
* @author René Kijewski <kijewski@inf.fu-berlin.de> | |||
* @author Víctor Ariño <victor.arino@zii.aero> | |||
*/ | |||
#ifndef SEM_H_ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor legacy error that might be fixable in this PR: s/SEM_H_/SEMA_H_/g
* | ||
* @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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 }
@@ -58,7 +68,6 @@ typedef struct { | |||
* @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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and below: please document the assertions you make in the implementation using the @pre
doxygen command.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
b2203b8
to
934b02b
Compare
@@ -33,23 +35,35 @@ extern "C" { | |||
/** | |||
* @brief Creates semaphore statically. | |||
* | |||
* @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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SEMA_CREATE_EMPTY
does not exist- I don't understand the reasoning behind this restriction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some minor kinks left.
* @param[in] sema The semaphore to destroy. | ||
* | ||
* @return 0 on success. | ||
* @return -EINVAL, if semaphore is invalid. | ||
*/ | ||
int sema_destroy(sema_t *sema); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
void
would minimize the code which in our use case is priority no. 1 ;-). So please go ahead.
@@ -58,92 +78,76 @@ typedef struct { | |||
* @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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
* | ||
* @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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird sentence structure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 :-) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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