Skip to content

Commit c45dd26

Browse files
authored
Merge pull request #3648 from c1728p9/condition_variable
Add ConditionVariable to mbed rtos
2 parents 2b39144 + d01c7fa commit c45dd26

File tree

6 files changed

+477
-3
lines changed

6 files changed

+477
-3
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/* mbed Microcontroller Library
2+
* Copyright (c) 2017 ARM Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
#include "mbed.h"
17+
#include "greentea-client/test_env.h"
18+
#include "unity.h"
19+
#include "utest.h"
20+
#include "rtos.h"
21+
22+
#if defined(MBED_RTOS_SINGLE_THREAD)
23+
#error [NOT_SUPPORTED] test not supported
24+
#endif
25+
26+
using namespace utest::v1;
27+
28+
#define TEST_STACK_SIZE 512
29+
#define TEST_DELAY 10
30+
31+
static int change_counter = 0;
32+
static Mutex mutex;
33+
static ConditionVariable cond(mutex);
34+
35+
void increment_on_signal()
36+
{
37+
mutex.lock();
38+
39+
cond.wait();
40+
change_counter++;
41+
42+
mutex.unlock();
43+
}
44+
45+
void test_notify_one()
46+
{
47+
Thread t1(osPriorityNormal, TEST_STACK_SIZE);
48+
Thread t2(osPriorityNormal, TEST_STACK_SIZE);
49+
50+
change_counter = 0;
51+
t1.start(increment_on_signal);
52+
t2.start(increment_on_signal);
53+
54+
wait_ms(TEST_DELAY);
55+
TEST_ASSERT_EQUAL(0, change_counter);
56+
57+
mutex.lock();
58+
cond.notify_one();
59+
mutex.unlock();
60+
61+
wait_ms(TEST_DELAY);
62+
TEST_ASSERT_EQUAL(1, change_counter);
63+
64+
mutex.lock();
65+
cond.notify_one();
66+
mutex.unlock();
67+
68+
t1.join();
69+
t2.join();
70+
}
71+
72+
void test_notify_all()
73+
{
74+
Thread t1(osPriorityNormal, TEST_STACK_SIZE);
75+
Thread t2(osPriorityNormal, TEST_STACK_SIZE);
76+
77+
change_counter = 0;
78+
t1.start(increment_on_signal);
79+
t2.start(increment_on_signal);
80+
81+
wait_ms(TEST_DELAY);
82+
TEST_ASSERT_EQUAL(0, change_counter);
83+
84+
mutex.lock();
85+
cond.notify_all();
86+
mutex.unlock();
87+
88+
wait_ms(TEST_DELAY);
89+
TEST_ASSERT_EQUAL(2, change_counter);
90+
91+
t1.join();
92+
t2.join();
93+
}
94+
95+
utest::v1::status_t test_setup(const size_t number_of_cases)
96+
{
97+
GREENTEA_SETUP(10, "default_auto");
98+
return verbose_test_setup_handler(number_of_cases);
99+
}
100+
101+
Case cases[] = {
102+
Case("Test notify one", test_notify_one),
103+
Case("Test notify all", test_notify_all),
104+
};
105+
106+
Specification specification(test_setup, cases);
107+
108+
int main()
109+
{
110+
return !Harness::run(specification);
111+
}

rtos/ConditionVariable.cpp

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/* mbed Microcontroller Library
2+
* Copyright (c) 2017-2017 ARM Limited
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*/
22+
#include "rtos/ConditionVariable.h"
23+
#include "rtos/Semaphore.h"
24+
#include "rtos/Thread.h"
25+
26+
#include "mbed_error.h"
27+
#include "mbed_assert.h"
28+
29+
namespace rtos {
30+
31+
#define RESUME_SIGNAL (1 << 15)
32+
33+
struct Waiter {
34+
Waiter();
35+
Semaphore sem;
36+
Waiter *prev;
37+
Waiter *next;
38+
bool in_list;
39+
};
40+
41+
Waiter::Waiter(): sem(0), prev(NULL), next(NULL), in_list(false)
42+
{
43+
// No initialization to do
44+
}
45+
46+
ConditionVariable::ConditionVariable(Mutex &mutex): _mutex(mutex), _wait_list(NULL)
47+
{
48+
// No initialization to do
49+
}
50+
51+
void ConditionVariable::wait()
52+
{
53+
wait_for(osWaitForever);
54+
}
55+
56+
bool ConditionVariable::wait_for(uint32_t millisec)
57+
{
58+
Waiter current_thread;
59+
MBED_ASSERT(_mutex.get_owner() == Thread::gettid());
60+
MBED_ASSERT(_mutex._count == 1);
61+
_add_wait_list(&current_thread);
62+
63+
_mutex.unlock();
64+
65+
int32_t sem_count = current_thread.sem.wait(millisec);
66+
bool timeout = (sem_count > 0) ? false : true;
67+
68+
_mutex.lock();
69+
70+
if (current_thread.in_list) {
71+
_remove_wait_list(&current_thread);
72+
}
73+
74+
return timeout;
75+
}
76+
77+
void ConditionVariable::notify_one()
78+
{
79+
MBED_ASSERT(_mutex.get_owner() == Thread::gettid());
80+
if (_wait_list != NULL) {
81+
_wait_list->sem.release();
82+
_remove_wait_list(_wait_list);
83+
}
84+
}
85+
86+
void ConditionVariable::notify_all()
87+
{
88+
MBED_ASSERT(_mutex.get_owner() == Thread::gettid());
89+
while (_wait_list != NULL) {
90+
_wait_list->sem.release();
91+
_remove_wait_list(_wait_list);
92+
}
93+
}
94+
95+
void ConditionVariable::_add_wait_list(Waiter * waiter)
96+
{
97+
if (NULL == _wait_list) {
98+
// Nothing in the list so add it directly.
99+
// Update prev pointer to reference self
100+
_wait_list = waiter;
101+
waiter->prev = waiter;
102+
} else {
103+
// Add after the last element
104+
Waiter *last = _wait_list->prev;
105+
last->next = waiter;
106+
waiter->prev = last;
107+
_wait_list->prev = waiter;
108+
}
109+
waiter->in_list = true;
110+
}
111+
112+
void ConditionVariable::_remove_wait_list(Waiter * waiter)
113+
{
114+
// Remove this element from the start of the list
115+
Waiter * next = waiter->next;
116+
if (waiter == _wait_list) {
117+
_wait_list = next;
118+
}
119+
if (next != NULL) {
120+
next = waiter->prev;
121+
}
122+
Waiter * prev = waiter->prev;
123+
if (prev != NULL) {
124+
prev = waiter->next;
125+
}
126+
waiter->next = NULL;
127+
waiter->prev = NULL;
128+
waiter->in_list = false;
129+
}
130+
131+
ConditionVariable::~ConditionVariable()
132+
{
133+
MBED_ASSERT(NULL == _wait_list);
134+
}
135+
136+
}

0 commit comments

Comments
 (0)