1 /*
2  * Copyright (c) 2021 Stephanos Ioannidis <root@stephanos.io>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /*
8  * @file Newlib thread-safety stress test
9  *
10  * This file contains a set of tests to verify that the C standard functions
11  * provided by newlib are thread safe (i.e. synchronised) and that the thread-
12  * specific contexts are properly handled (i.e. re-entrant).
13  */
14 
15 #include <zephyr/kernel.h>
16 #include <zephyr/ztest.h>
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <malloc.h>
21 
22 #define THREAD_COUNT	(64)
23 #define STACK_SIZE	(512 + CONFIG_TEST_EXTRA_STACK_SIZE)
24 #define TEST_INTERVAL	(30) /* seconds */
25 
26 #ifdef CONFIG_USERSPACE
27 #define THREAD_OPT	(K_USER | K_INHERIT_PERMS)
28 #else
29 #define THREAD_OPT	(0)
30 #endif /* CONFIG_USERSPACE */
31 
32 static struct k_thread tdata[THREAD_COUNT];
33 static K_THREAD_STACK_ARRAY_DEFINE(tstack, THREAD_COUNT, STACK_SIZE);
34 
malloc_thread(void * p1,void * p2,void * p3)35 static void malloc_thread(void *p1, void *p2, void *p3)
36 {
37 	static ZTEST_BMEM atomic_t count;
38 	bool *aborted = p1;
39 	int val;
40 	int *volatile ptr;
41 
42 	while (*aborted == false) {
43 		/* Compute unique value specific to this iteration. */
44 		val = atomic_inc(&count);
45 
46 		/* Allocate memory block and write a unique value to it. */
47 		ptr = malloc(sizeof(int));
48 		zassert_not_null(ptr, "Out of memory");
49 		*ptr = val;
50 
51 		/* Busy wait to increase the likelihood of preemption. */
52 		k_busy_wait(10);
53 
54 		/*
55 		 * Verify that the unique value previously written to the
56 		 * memory block is valid.  This value will become corrupted if
57 		 * the newlib heap is not properly synchronised.
58 		 */
59 		zassert_equal(*ptr, val, "Corrupted memory block");
60 
61 		/* Free memory block. */
62 		free(ptr);
63 	}
64 }
65 
66 /**
67  * @brief Test thread safety of newlib memory management functions
68  *
69  * This test calls the malloc() and free() functions from multiple threads to
70  * verify that no corruption occurs in the newlib memory heap.
71  */
ZTEST(newlib_thread_safety_stress,test_malloc_thread_safety)72 ZTEST(newlib_thread_safety_stress, test_malloc_thread_safety)
73 {
74 	int i;
75 	k_tid_t tid[THREAD_COUNT];
76 	static ZTEST_BMEM bool aborted;
77 
78 	/* Create worker threads. */
79 	for (i = 0; i < ARRAY_SIZE(tid); i++) {
80 		tid[i] = k_thread_create(&tdata[i], tstack[i], STACK_SIZE,
81 					 malloc_thread, &aborted, NULL, NULL,
82 					 K_PRIO_PREEMPT(0), THREAD_OPT,
83 					 K_NO_WAIT);
84 	}
85 
86 	TC_PRINT("Created %d worker threads.\n", THREAD_COUNT);
87 
88 	/* Wait and see if any failures occur. */
89 	TC_PRINT("Waiting %d seconds to see if any failures occur ...\n",
90 		 TEST_INTERVAL);
91 
92 	k_sleep(K_SECONDS(TEST_INTERVAL));
93 
94 	/* Abort all worker threads. */
95 	aborted = true;
96 
97 	for (i = 0; i < ARRAY_SIZE(tid); i++) {
98 		k_thread_join(tid[i], K_FOREVER);
99 	}
100 }
101 ZTEST_SUITE(newlib_thread_safety_stress, NULL, NULL, NULL, NULL, NULL);
102