Skip to content

Commit 2f9cf47

Browse files
authored
Merge pull request #5508 from c1728p9/condition_variable_fix
Fix and add test for ConditionVariable
2 parents f36922c + 93cf15d commit 2f9cf47

File tree

3 files changed

+120
-37
lines changed

3 files changed

+120
-37
lines changed

TESTS/mbedmicro-rtos-mbed/condition_variable/main.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,80 @@ void test_notify_all()
9292
t2.join();
9393
}
9494

95+
96+
class TestConditionVariable : public ConditionVariable {
97+
98+
public:
99+
static void test_linked_list(void)
100+
{
101+
Waiter *list = NULL;
102+
Waiter w1;
103+
Waiter w2;
104+
Waiter w3;
105+
Waiter w4;
106+
107+
TEST_ASSERT_EQUAL(0, validate_and_get_size(&list));
108+
109+
// Add 4 nodes
110+
_add_wait_list(&list, &w1);
111+
TEST_ASSERT_EQUAL(1, validate_and_get_size(&list));
112+
_add_wait_list(&list, &w2);
113+
TEST_ASSERT_EQUAL(2, validate_and_get_size(&list));
114+
_add_wait_list(&list, &w3);
115+
TEST_ASSERT_EQUAL(3, validate_and_get_size(&list));
116+
_add_wait_list(&list, &w4);
117+
TEST_ASSERT_EQUAL(4, validate_and_get_size(&list));
118+
119+
// Remove a middle node
120+
_remove_wait_list(&list, &w2);
121+
TEST_ASSERT_EQUAL(3, validate_and_get_size(&list));
122+
123+
// Remove front node
124+
_remove_wait_list(&list, &w1);
125+
TEST_ASSERT_EQUAL(2, validate_and_get_size(&list));
126+
127+
// remove back node
128+
_remove_wait_list(&list, &w4);
129+
TEST_ASSERT_EQUAL(1, validate_and_get_size(&list));
130+
131+
// remove last node
132+
_remove_wait_list(&list, &w3);
133+
TEST_ASSERT_EQUAL(0, validate_and_get_size(&list));
134+
135+
TEST_ASSERT_EQUAL_PTR(NULL, list);
136+
}
137+
138+
/**
139+
* Validate the linked list an return the number of elements
140+
*
141+
* If this list is invalid then this function asserts and does not
142+
* return.
143+
*
144+
* Every node in a valid linked list has the properties:
145+
* 1. node->prev->next == node
146+
* 2. node->next->prev == node
147+
*/
148+
static int validate_and_get_size(Waiter **list)
149+
{
150+
Waiter *first = *list;
151+
if (NULL == first) {
152+
// List is empty
153+
return 0;
154+
}
155+
156+
int size = 0;
157+
Waiter *current = first;
158+
do {
159+
TEST_ASSERT_EQUAL_PTR(current, current->prev->next);
160+
TEST_ASSERT_EQUAL_PTR(current, current->next->prev);
161+
current = current->next;
162+
size++;
163+
} while (current != first);
164+
return size;
165+
}
166+
167+
};
168+
95169
utest::v1::status_t test_setup(const size_t number_of_cases)
96170
{
97171
GREENTEA_SETUP(10, "default_auto");
@@ -101,6 +175,7 @@ utest::v1::status_t test_setup(const size_t number_of_cases)
101175
Case cases[] = {
102176
Case("Test notify one", test_notify_one),
103177
Case("Test notify all", test_notify_all),
178+
Case("Test linked list", TestConditionVariable::test_linked_list),
104179
};
105180

106181
Specification specification(test_setup, cases);

rtos/ConditionVariable.cpp

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,15 @@
2020
* SOFTWARE.
2121
*/
2222
#include "rtos/ConditionVariable.h"
23-
#include "rtos/Semaphore.h"
2423
#include "rtos/Thread.h"
2524

2625
#include "mbed_error.h"
2726
#include "mbed_assert.h"
2827

2928
namespace rtos {
3029

31-
#define RESUME_SIGNAL (1 << 15)
3230

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)
31+
ConditionVariable::Waiter::Waiter(): sem(0), prev(NULL), next(NULL), in_list(false)
4232
{
4333
// No initialization to do
4434
}
@@ -58,7 +48,7 @@ bool ConditionVariable::wait_for(uint32_t millisec)
5848
Waiter current_thread;
5949
MBED_ASSERT(_mutex.get_owner() == Thread::gettid());
6050
MBED_ASSERT(_mutex._count == 1);
61-
_add_wait_list(&current_thread);
51+
_add_wait_list(&_wait_list, &current_thread);
6252

6353
_mutex.unlock();
6454

@@ -68,7 +58,7 @@ bool ConditionVariable::wait_for(uint32_t millisec)
6858
_mutex.lock();
6959

7060
if (current_thread.in_list) {
71-
_remove_wait_list(&current_thread);
61+
_remove_wait_list(&_wait_list, &current_thread);
7262
}
7363

7464
return timeout;
@@ -79,7 +69,7 @@ void ConditionVariable::notify_one()
7969
MBED_ASSERT(_mutex.get_owner() == Thread::gettid());
8070
if (_wait_list != NULL) {
8171
_wait_list->sem.release();
82-
_remove_wait_list(_wait_list);
72+
_remove_wait_list(&_wait_list, _wait_list);
8373
}
8474
}
8575

@@ -88,41 +78,50 @@ void ConditionVariable::notify_all()
8878
MBED_ASSERT(_mutex.get_owner() == Thread::gettid());
8979
while (_wait_list != NULL) {
9080
_wait_list->sem.release();
91-
_remove_wait_list(_wait_list);
81+
_remove_wait_list(&_wait_list, _wait_list);
9282
}
9383
}
9484

95-
void ConditionVariable::_add_wait_list(Waiter * waiter)
85+
void ConditionVariable::_add_wait_list(Waiter **wait_list, Waiter *waiter)
9686
{
97-
if (NULL == _wait_list) {
87+
if (NULL == *wait_list) {
9888
// Nothing in the list so add it directly.
99-
// Update prev pointer to reference self
100-
_wait_list = waiter;
89+
// Update prev and next pointer to reference self
90+
*wait_list = waiter;
91+
waiter->next = waiter;
10192
waiter->prev = waiter;
10293
} else {
10394
// Add after the last element
104-
Waiter *last = _wait_list->prev;
105-
last->next = waiter;
95+
Waiter *first = *wait_list;
96+
Waiter *last = (*wait_list)->prev;
97+
98+
// Update new entry
99+
waiter->next = first;
106100
waiter->prev = last;
107-
_wait_list->prev = waiter;
101+
102+
// Insert into the list
103+
first->prev = waiter;
104+
last->next = waiter;
108105
}
109106
waiter->in_list = true;
110107
}
111108

112-
void ConditionVariable::_remove_wait_list(Waiter * waiter)
109+
void ConditionVariable::_remove_wait_list(Waiter **wait_list, Waiter *waiter)
113110
{
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;
111+
Waiter *prev = waiter->prev;
112+
Waiter *next = waiter->next;
113+
114+
// Remove from list
115+
prev->next = waiter->next;
116+
next->prev = waiter->prev;
117+
*wait_list = waiter->next;
118+
119+
if (*wait_list == waiter) {
120+
// This was the last element in the list
121+
*wait_list = NULL;
125122
}
123+
124+
// Invalidate pointers
126125
waiter->next = NULL;
127126
waiter->prev = NULL;
128127
waiter->in_list = false;

rtos/ConditionVariable.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <stdint.h>
2626
#include "cmsis_os.h"
2727
#include "rtos/Mutex.h"
28+
#include "rtos/Semaphore.h"
2829

2930
#include "platform/NonCopyable.h"
3031

@@ -192,9 +193,17 @@ class ConditionVariable : private mbed::NonCopyable<ConditionVariable> {
192193

193194
~ConditionVariable();
194195

195-
private:
196-
void _add_wait_list(Waiter * waiter);
197-
void _remove_wait_list(Waiter * waiter);
196+
protected:
197+
struct Waiter {
198+
Waiter();
199+
Semaphore sem;
200+
Waiter *prev;
201+
Waiter *next;
202+
bool in_list;
203+
};
204+
205+
static void _add_wait_list(Waiter **wait_list, Waiter *waiter);
206+
static void _remove_wait_list(Waiter **wait_list, Waiter *waiter);
198207
Mutex &_mutex;
199208
Waiter *_wait_list;
200209
};

0 commit comments

Comments
 (0)