|
10 | 10 |
|
11 | 11 | #define STACKSIZE (256 + CONFIG_TEST_EXTRA_STACKSIZE) |
12 | 12 |
|
13 | | -#if defined(CONFIG_USERSPACE) && defined(CONFIG_DYNAMIC_OBJECTS) |
14 | | - |
15 | 13 | static K_THREAD_STACK_DEFINE(dyn_thread_stack, STACKSIZE); |
16 | 14 | static K_SEM_DEFINE(start_sem, 0, 1); |
17 | 15 | static K_SEM_DEFINE(end_sem, 0, 1); |
@@ -106,25 +104,56 @@ static void test_dyn_thread_perms(void) |
106 | 104 | TC_PRINT("===== must have access denied on k_sem %p\n", &end_sem); |
107 | 105 | } |
108 | 106 |
|
109 | | -#else /* (CONFIG_USERSPACE && CONFIG_DYNAMIC_OBJECTS) */ |
| 107 | +static struct k_thread *dynamic_threads[CONFIG_MAX_THREAD_BYTES * 8]; |
110 | 108 |
|
111 | | -static void prep(void) |
| 109 | +static void test_thread_index_management(void) |
112 | 110 | { |
113 | | -} |
| 111 | + int i, ctr = 0; |
114 | 112 |
|
115 | | -static void create_dynamic_thread(void) |
116 | | -{ |
117 | | - TC_PRINT("Test skipped. Userspace and dynamic objects required.\n"); |
118 | | - ztest_test_skip(); |
119 | | -} |
| 113 | + /* Create thread objects until we run out of ids */ |
| 114 | + while (true) { |
| 115 | + struct k_thread *t = k_object_alloc(K_OBJ_THREAD); |
120 | 116 |
|
121 | | -static void test_dyn_thread_perms(void) |
122 | | -{ |
123 | | - TC_PRINT("Test skipped. Userspace and dynamic objects required.\n"); |
124 | | - ztest_test_skip(); |
125 | | -} |
| 117 | + if (t == NULL) { |
| 118 | + break; |
| 119 | + } |
126 | 120 |
|
127 | | -#endif /* !(CONFIG_USERSPACE && CONFIG_DYNAMIC_OBJECTS) */ |
| 121 | + dynamic_threads[ctr] = t; |
| 122 | + ctr++; |
| 123 | + } |
| 124 | + |
| 125 | + zassert_true(ctr != 0, "unable to create any thread objects"); |
| 126 | + |
| 127 | + TC_PRINT("created %d thread objects\n", ctr); |
| 128 | + |
| 129 | + /* Show that the above NULL return value wasn't because we ran out of |
| 130 | + * heap space/ |
| 131 | + */ |
| 132 | + void *blob = k_malloc(256); |
| 133 | + zassert_true(blob != NULL, "out of heap memory"); |
| 134 | + |
| 135 | + /* Free one of the threads... */ |
| 136 | + k_object_free(dynamic_threads[0]); |
| 137 | + |
| 138 | + /* And show that we can now create another one, the freed thread's |
| 139 | + * index should have been garbage collected. |
| 140 | + */ |
| 141 | + dynamic_threads[0] = k_object_alloc(K_OBJ_THREAD); |
| 142 | + zassert_true(dynamic_threads[0] != NULL, |
| 143 | + "couldn't create thread object\n"); |
| 144 | + |
| 145 | + /* TODO: Implement a test that shows that thread IDs are properly |
| 146 | + * recycled when a thread object is garbage collected due to references |
| 147 | + * dropping to zero. For example, we ought to be able to exit here |
| 148 | + * without calling k_object_free() on any of the threads we created |
| 149 | + * here; their references would drop to zero and they would be |
| 150 | + * automatically freed. However, it is known that the thread IDs are |
| 151 | + * not properly recycled when this happens, see #17023. |
| 152 | + */ |
| 153 | + for (i = 0; i < ctr; i++) { |
| 154 | + k_object_free(dynamic_threads[i]); |
| 155 | + } |
| 156 | +} |
128 | 157 |
|
129 | 158 | /** |
130 | 159 | * @ingroup kernel_thread_tests |
@@ -160,7 +189,8 @@ void test_main(void) |
160 | 189 | ztest_test_suite(thread_dynamic, |
161 | 190 | ztest_unit_test(test_kernel_create_dyn_user_thread), |
162 | 191 | ztest_user_unit_test(test_user_create_dyn_user_thread), |
163 | | - ztest_unit_test(test_dyn_thread_perms) |
| 192 | + ztest_unit_test(test_dyn_thread_perms), |
| 193 | + ztest_unit_test(test_thread_index_management) |
164 | 194 | ); |
165 | 195 | ztest_run_test_suite(thread_dynamic); |
166 | 196 | } |
0 commit comments