Skip to content

Conversation

kaspar030
Copy link
Contributor

This PR adds functionality to set multiple (32) "flags" per thread, and also wait for them in any combination, efficiently.

While adding 8 bytes to each TCB, this method allows faster conditional sleep than mutexes and is way more flexible. It is possible to implement mutex_wait and msg_wait with these flags, making it easy to wait for, e.g., a button to be pressed, a mutex to become free and a timer to be shot, at the same time.

This is WIP, but as people are asking (#4102), I decided to share.

@kaspar030 kaspar030 added Type: enhancement The issue suggests enhanceable parts / The PR enhances parts of the codebase / documentation State: WIP State: The PR is still work-in-progress and its code is not in its final presentable form yet Area: core Area: RIOT kernel. Handle PRs marked with this with care! Discussion: RFC The issue/PR is used as a discussion starting point about the item of the issue/PR labels Oct 16, 2015
@OlegHahm
Copy link
Member

While adding 8 bytes to each TCB, this method allows faster conditional sleep than mutexes and is way more flexible.

Wow, do you remember us celebrating when we could minimize tcb size by a couple of bytes a year ago or so?

I have to admit that I haven't understood the whole idea of this PR, but trading more RAM against improved runtime seems a to be a bad compromise on IoT devices.

@OlegHahm OlegHahm modified the milestone: Release 2016.03 Dec 8, 2015
@zhuoshuguo
Copy link
Contributor

@kaspar030 Ping, a kind reminder for rebase.

@kaspar030
Copy link
Contributor Author

  • rebased

@kaspar030
Copy link
Contributor Author

I have to admit that I haven't understood the whole idea of this PR

A common use case is what we're usually using mutexes for: wait for an event that might happen in an isr, eg., arrival of a network packet or a byte on a serial line.

Currently, a thread usually waits in a loop:

mutex_lock();
while (1) {
   mutex_lock());
   /* handle event */
 mutex_unlock();
}

and an ISR would unlock the mutex whenever an event occurs. You can find that kind of logic (sometimes using messages) all over the place.

While it kinda works, it has several disadvantages:

  1. it is only possible to wait for one mutex, or a message, ...
  2. if a message is used to pass events, and if the message queue is full, the event is lost (remember "possible lost interrupt"?)

The main concept behind this PR is that events can be atomically flagged and waited for in any combination:

while(1) {
  flag = thread_flag_wait_any(); /* waits for flag */
  switch(flag) {
   case FLAG_MSG:
     msg_receive(&msg);
   case FLAG_TAPDEV_ISR:
   handle_isr();
 }
}

As is, no flags for messages or mutexes are generated, but that's easy to add.

Wow, do you remember us celebrating when we could minimize tcb size by a couple of bytes a year ago or so?

Of course! ;) I will try to move the needed struct out of tcb_t. I had that in an early version, but it was less efficient...

*
* @return The value of *var* before the operation.
*/
static inline int atomic_or(atomic_int_t *var, int val)
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe make this file a PR on its own, and also add xor and not to complete the family?

@OlegHahm
Copy link
Member

Thanks for the explanation.

@kaspar030
Copy link
Contributor Author

I've changed the implementation to use tcb->wait_data for the wait mask, and also changed the flags type to uint16_t. That way, on 32bit platforms, sizeof(tcb_t) doesn't get bigger due to alignment luck, and gains 2 byte on other platforms.

Also, I've changed core/msg.c to set some flags and xtimer_msg_receive_timeout() to make use of them.

@OlegHahm
Copy link
Member

Nice!

@kaspar030
Copy link
Contributor Author

  • changed the message flag semantics so it now always shows whether any message is waiting

@OlegHahm, would you like to review this?

Should I split the the msg changes?

char *sp; /**< thread's stack pointer */
uint16_t status; /**< thread's status */

kernel_pid_t pid; /**< thread's process id */
uint16_t priority; /**< thread's priority */

thread_flags_t flags; /**< currently set flags */
Copy link
Member

Choose a reason for hiding this comment

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

Couldn't this be optional, too, if we have a pseudomodule anyway?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It could. But I've also changed msg.c to use thread flags internally, which makes some things a lot nicer.

e.g., currently in xtimer_receive_msg_timeout, there's a race between receiving the message and deleting the timer, and if the thread has a message queue, that might lead to the timer message to be queued right after the actual received message.

This PR's xtimer_receive_msg_timeout() implementation uses a thread flag to pass the timeout information, solving the race problem.

The pseudomodule basically only disables the thread flags user API.

edit sorry, the messaging changes are unrelated to the xtimer changes.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, I see. I guess it makes sense.

@OlegHahm
Copy link
Member

@OlegHahm, would you like to review this?

I can give it a try.

Should I split the the msg changes?

Would make it easier to review.

@OlegHahm OlegHahm added the CI: needs squashing Commits in this PR need to be squashed; If set, CI systems will mark this PR as unmergable label Feb 24, 2016
@kaspar030
Copy link
Contributor Author

  • cut out msg and xtimer changes

@kaspar030 kaspar030 assigned OlegHahm and unassigned Kijewski Feb 24, 2016
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2013 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* This file is subject to the terms and thread_conditions of the GNU Lesser
Copy link
Contributor Author

Choose a reason for hiding this comment

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

ups

@kaspar030
Copy link
Contributor Author

I this still going into the release? Or should we postpone it?

let me rebase and have another look. IMHO it should be ready.

@kaspar030 kaspar030 added the State: waiting for other PR State: The PR requires another PR to be merged first label Mar 29, 2016
@kaspar030
Copy link
Contributor Author

  • rebased on 4923

@haukepetersen IMHO ready to merge.

thread_flags_t thread_flags_wait_all(thread_flags_t mask);

/**
* @brief Wait for any flags in mask to become set (blocking), one at a time
Copy link
Contributor

Choose a reason for hiding this comment

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

most or least significant first? Please document

Copy link
Contributor

Choose a reason for hiding this comment

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

addressed

@haukepetersen
Copy link
Contributor

Code looks good and works as expected (at least the test application)

ACK when addressed, rebased and squashed.

@OlegHahm
Copy link
Member

Needs a rebase after #4923 got merged.

@kaspar030 kaspar030 added CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR and removed State: waiting for other PR State: The PR requires another PR to be merged first labels Mar 30, 2016
@kaspar030
Copy link
Contributor Author

  • rebased

@kaspar030
Copy link
Contributor Author

Dammit, removed the "waiting for pr" label too late...

@kaspar030 kaspar030 added CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR and removed CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Mar 30, 2016
@kaspar030
Copy link
Contributor Author

@cgundogan would you take another look?

@kaspar030
Copy link
Contributor Author

@cgundogan Ah, not needed, there are already two ACKs. ;)

@cgundogan
Copy link
Member

if you don't want my personal ACK (;

@kaspar030
Copy link
Contributor Author

Don't want to overuse your ACKs, I know reviewing is hard! :)

{
unsigned state = irq_disable();
mask &= thread->flags;
thread->flags &= ~mask;
Copy link
Member

Choose a reason for hiding this comment

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

I am not 100% sure, but I think

mask &= thread->flags;
thread->flags &= ~mask;

can be reduced to thread->flags &= ~mask; only?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, as the function needs to return the actually cleared flags.

Copy link
Member

Choose a reason for hiding this comment

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

dammit, I didn't look that far..

@kaspar030 kaspar030 added the Type: new feature The issue requests / The PR implemements a new feature for RIOT label Mar 30, 2016
@kaspar030
Copy link
Contributor Author

Murdock is fine -> go.

@kaspar030 kaspar030 merged commit 5f81284 into RIOT-OS:master Mar 30, 2016
@kaspar030 kaspar030 deleted the add_thread_flags branch March 30, 2016 10:17
_thread_flags_wait_any(mask);
thread_t *me = (thread_t*) sched_active_thread;
unsigned tmp = me->flags & mask;
return _thread_flags_clear_atomic(me, thread_flags_clear(1 << bitarithm_lsb(tmp)));
Copy link
Member

Choose a reason for hiding this comment

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

If you are trying to unset all bits, but not the least significant one, then you may use tmp &= -tmp, or tmp &= ~tmp + 1 (equivalent) instead of 1 << bitarithm_lsb(tmp).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, I'm trying to unset only the least signigicant bit. :)

Copy link
Member

Choose a reason for hiding this comment

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

1 << bitarithm_lsb(tmp) isn't this the same as unsetting all bits but not the lsb?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yes.

>>> a = 0x1110;  a &= -a; print(bin(a))
0b10000
>>>

Is this due to pythons signed bit handling?

Copy link
Member

Choose a reason for hiding this comment

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

yes, this only works if the number is unsigned. The number gets negated and 1 is added to the result for negative unsigned numbers .. AFAIK

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See #5200. Saves a whopping 32 bytes of code. ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(16byte on 32bit ARM).

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! CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Community: Hack'n'ACK candidate This PR is a candidate for review and discussion during one of RIOT's monthly Hack'n'ACK parties Type: enhancement The issue suggests enhanceable parts / The PR enhances parts of the codebase / documentation Type: new feature The issue requests / The PR implemements a new feature for RIOT
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants