Skip to content

Commit 0128dd2

Browse files
authored
Merge pull request #2562 from c1728p9/init_race_condition
Fix GCC lazy init race condition and add test
2 parents 3bb149c + ef45ef8 commit 0128dd2

File tree

2 files changed

+171
-0
lines changed

2 files changed

+171
-0
lines changed

TESTS/mbed_drivers/race_test/main.cpp

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
* Copyright (c) 2016-2016, ARM Limited, All Rights Reserved
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License"); you may
6+
* not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
#include "mbed.h"
18+
#include "rtos.h"
19+
#include "greentea-client/test_env.h"
20+
#include "unity/unity.h"
21+
#include "utest/utest.h"
22+
#include "SingletonPtr.h"
23+
#include <stdio.h>
24+
25+
using namespace utest::v1;
26+
27+
#define TEST_STACK_SIZE 1024
28+
static uint32_t instance_count = 0;
29+
30+
class TestClass {
31+
public:
32+
TestClass() {
33+
printf("TestClass ctor start\r\n");
34+
Thread::wait(500);
35+
instance_count++;
36+
printf("TestClass ctor end\r\n");
37+
}
38+
39+
void do_something() {
40+
printf("Do something called\r\n");
41+
}
42+
43+
~TestClass() {
44+
instance_count--;
45+
}
46+
};
47+
48+
static TestClass* get_test_class()
49+
{
50+
static TestClass tc;
51+
return &tc;
52+
}
53+
54+
static SingletonPtr<TestClass> test_class;
55+
56+
static void main_func_race()
57+
{
58+
get_test_class();
59+
}
60+
61+
static void main_class_race()
62+
{
63+
test_class->do_something();
64+
}
65+
66+
void test_case_func_race()
67+
{
68+
printf("Running function race test\r\n");
69+
Callback<void()> cb(main_func_race);
70+
Thread *t1 = new Thread(osPriorityNormal, TEST_STACK_SIZE);
71+
Thread *t2 = new Thread(osPriorityNormal, TEST_STACK_SIZE);
72+
73+
// Start start first thread
74+
t1->start(cb);
75+
// Start second thread while the first is inside the constructor
76+
Thread::wait(250);
77+
t2->start(cb);
78+
79+
// Wait for the threads to finish
80+
t1->join();
81+
t2->join();
82+
83+
delete t1;
84+
delete t2;
85+
86+
TEST_ASSERT_EQUAL_UINT32(1, instance_count);
87+
88+
// Reset instance count
89+
instance_count = 0;
90+
}
91+
92+
void test_case_class_race()
93+
{
94+
printf("Running class race test\r\n");
95+
Callback<void()> cb(main_class_race);
96+
Thread *t1 = new Thread(osPriorityNormal, TEST_STACK_SIZE);
97+
Thread *t2 = new Thread(osPriorityNormal, TEST_STACK_SIZE);
98+
99+
// Start start first thread
100+
t1->start(cb);
101+
// Start second thread while the first is inside the constructor
102+
Thread::wait(250);
103+
t2->start(cb);
104+
105+
// Wait for the threads to finish
106+
t1->join();
107+
t2->join();
108+
109+
delete t1;
110+
delete t2;
111+
112+
TEST_ASSERT_EQUAL_UINT32(1, instance_count);
113+
114+
// Reset instance count
115+
instance_count = 0;
116+
}
117+
118+
Case cases[] = {
119+
Case("function init race", test_case_func_race),
120+
Case("class init race", test_case_class_race),
121+
};
122+
123+
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
124+
{
125+
GREENTEA_SETUP(20, "default_auto");
126+
return greentea_test_setup_handler(number_of_cases);
127+
}
128+
129+
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
130+
131+
int main()
132+
{
133+
Harness::run(specification);
134+
}

hal/common/retarget.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,43 @@ extern "C" void __env_unlock( struct _reent *_r )
712712
{
713713
__rtos_env_unlock(_r);
714714
}
715+
716+
#define CXA_GUARD_INIT_DONE (1 << 0)
717+
#define CXA_GUARD_INIT_IN_PROGRESS (1 << 1)
718+
#define CXA_GUARD_MASK (CXA_GUARD_INIT_DONE | CXA_GUARD_INIT_IN_PROGRESS)
719+
720+
extern "C" int __cxa_guard_acquire(int *guard_object_p)
721+
{
722+
uint8_t *guard_object = (uint8_t *)guard_object_p;
723+
if (CXA_GUARD_INIT_DONE == (*guard_object & CXA_GUARD_MASK)) {
724+
return 0;
725+
}
726+
singleton_lock();
727+
if (CXA_GUARD_INIT_DONE == (*guard_object & CXA_GUARD_MASK)) {
728+
singleton_unlock();
729+
return 0;
730+
}
731+
MBED_ASSERT(0 == (*guard_object & CXA_GUARD_MASK));
732+
*guard_object = *guard_object | CXA_GUARD_INIT_IN_PROGRESS;
733+
return 1;
734+
}
735+
736+
extern "C" void __cxa_guard_release(int *guard_object_p)
737+
{
738+
uint8_t *guard_object = (uint8_t *)guard_object_p;
739+
MBED_ASSERT(CXA_GUARD_INIT_IN_PROGRESS == (*guard_object & CXA_GUARD_MASK));
740+
*guard_object = (*guard_object & ~CXA_GUARD_MASK) | CXA_GUARD_INIT_DONE;
741+
singleton_unlock();
742+
}
743+
744+
extern "C" void __cxa_guard_abort(int *guard_object_p)
745+
{
746+
uint8_t *guard_object = (uint8_t *)guard_object_p;
747+
MBED_ASSERT(CXA_GUARD_INIT_IN_PROGRESS == (*guard_object & CXA_GUARD_MASK));
748+
*guard_object = *guard_object & ~CXA_GUARD_INIT_IN_PROGRESS;
749+
singleton_unlock();
750+
}
751+
715752
#endif
716753

717754
void *operator new(std::size_t count)

0 commit comments

Comments
 (0)