2626#include < stdlib.h>
2727#include < stdio.h>
2828#include < algorithm>
29+ #include " mbed_stats.h"
2930
3031#if !NVSTORE_ENABLED
3132#error [NOT_SUPPORTED] NVSTORE needs to be enabled for this test
@@ -42,26 +43,24 @@ static const int thr_test_num_secs = 5;
4243static const int thr_test_max_data_size = 32 ;
4344static const int thr_test_num_threads = 3 ;
4445
45- #ifdef TARGET_NRF52
46- static const int thr_test_stack_size = 1024 ;
47- #else
48- static const int thr_test_stack_size = 768 ;
49- #endif
46+ static const int thr_test_min_stack_size = 768 ;
47+ static const int thr_test_max_stack_size = 1024 ;
48+
5049
5150typedef struct {
5251 uint8_t *buffs[max_test_keys][thr_test_num_buffs];
5352 uint16_t sizes[max_test_keys][thr_test_num_buffs];
54- int inds[max_test_keys];
5553 uint16_t max_keys;
56- uint16_t last_key;
57- int last_ind;
54+ bool stop_threads;
5855} thread_test_data_t ;
5956
6057static thread_test_data_t *thr_test_data;
6158
6259static const int race_test_num_threads = 4 ;
6360static const int race_test_key = 1 ;
6461static const int race_test_data_size = 128 ;
62+ static const int race_test_min_stack_size = 768 ;
63+ static const int race_test_max_stack_size = 1024 ;
6564
6665static void gen_random (uint8_t *s, int len)
6766{
@@ -378,6 +377,41 @@ static void nvstore_basic_functionality_test()
378377 delete[] nvstore_testing_buf_get;
379378}
380379
380+ // This function calculates the stack size that needs to be allocated per thread in
381+ // the multi-thread tests. Given minimal and maximal stack sizes, and based on the heap
382+ // stats (automatically enabled in CI), the function checks whether each thread has at least
383+ // the minimal stack size, otherwise it reduces the number of threads (something that may happen
384+ // on low memory boards).
385+ static void calc_thread_stack_size (int &num_threads, uint32_t min_size, uint32_t max_size,
386+ uint32_t &stack_size)
387+ {
388+ mbed_stats_heap_t heap_stats;
389+ mbed_stats_heap_get (&heap_stats);
390+
391+ // reserved size (along with all other fields in heap stats) will be zero if
392+ // app is compiled without heap stats (typically local builds)
393+ if (!heap_stats.reserved_size ) {
394+ stack_size = max_size;
395+ printf (" Heap stats disabled in this build, so test may fail due to insufficient heap size\n " );
396+ printf (" If this happens, please build the test with heap stats enabled (-DMBED_HEAP_STATS_ENABLED=1)\n " );
397+ return ;
398+ }
399+
400+ NVStore &nvstore = NVStore::get_instance ();
401+ int page_size = nvstore.size () / nvstore.get_max_possible_keys ();
402+ // Check if we can allocate enough stack size (per thread) for the current number of threads
403+ while (num_threads) {
404+ stack_size = (heap_stats.reserved_size - heap_stats.current_size ) / num_threads - sizeof (rtos::Thread) - page_size;
405+
406+ stack_size = std::min (stack_size, max_size);
407+ if (stack_size >= min_size) {
408+ return ;
409+ }
410+
411+ // Got here - stack not sufficient per thread. Reduce number of threads
412+ num_threads--;
413+ }
414+ }
381415
382416static void thread_test_check_key (uint16_t key)
383417{
@@ -386,7 +420,7 @@ static void thread_test_check_key(uint16_t key)
386420 uint16_t actual_len_bytes;
387421 NVStore &nvstore = NVStore::get_instance ();
388422
389- ret = nvstore.get (key, basic_func_max_data_size , get_buff, actual_len_bytes);
423+ ret = nvstore.get (key, thr_test_max_data_size , get_buff, actual_len_bytes);
390424 TEST_ASSERT_EQUAL (NVSTORE_SUCCESS, ret);
391425 TEST_ASSERT_NOT_EQUAL (0 , actual_len_bytes);
392426
@@ -400,13 +434,6 @@ static void thread_test_check_key(uint16_t key)
400434 }
401435 }
402436
403- if (key == thr_test_data->last_key ) {
404- if ((thr_test_data->sizes [key][thr_test_data->last_ind ] == actual_len_bytes) &&
405- (!memcmp (thr_test_data->buffs [key][thr_test_data->last_ind ], get_buff, actual_len_bytes))) {
406- return ;
407- }
408- }
409-
410437 // Got here - always assert
411438 TEST_ASSERT (0 );
412439
@@ -420,17 +447,13 @@ static void thread_test_worker()
420447 uint16_t key;
421448 NVStore &nvstore = NVStore::get_instance ();
422449
423- for (;; ) {
450+ while (!thr_test_data-> stop_threads ) {
424451 key = rand () % thr_test_data->max_keys ;
425452 is_set = rand () % 10 ;
426-
427453 if (is_set) {
428454 buf_num = rand () % thr_test_num_buffs;
429- thr_test_data->last_key = key;
430- thr_test_data->last_ind = buf_num;
431455 ret = nvstore.set (key, thr_test_data->sizes [key][buf_num], thr_test_data->buffs [key][buf_num]);
432456 TEST_ASSERT_EQUAL (NVSTORE_SUCCESS, ret);
433- thr_test_data->inds [key] = buf_num;
434457 } else {
435458 thread_test_check_key (key);
436459 }
@@ -445,6 +468,7 @@ static void nvstore_multi_thread_test()
445468#ifdef MBED_CONF_RTOS_PRESENT
446469 int i;
447470 int num_threads = thr_test_num_threads;
471+ uint32_t stack_size;
448472 uint16_t size;
449473 uint16_t key;
450474 int ret;
@@ -458,43 +482,51 @@ static void nvstore_multi_thread_test()
458482
459483 thr_test_data = new thread_test_data_t ;
460484 thr_test_data->max_keys = max_test_keys / 2 ;
485+ thr_test_data->stop_threads = false ;
461486 for (key = 0 ; key < thr_test_data->max_keys ; key++) {
462487 for (i = 0 ; i < thr_test_num_buffs; i++) {
463488 size = 1 + rand () % thr_test_max_data_size;
464489 thr_test_data->sizes [key][i] = size;
465490 thr_test_data->buffs [key][i] = new uint8_t [size + 1 ];
466- thr_test_data->inds [key] = 0 ;
467491 gen_random (thr_test_data->buffs [key][i], size);
468492 }
469493 ret = nvstore.set (key, thr_test_data->sizes [key][0 ], thr_test_data->buffs [key][0 ]);
470494 TEST_ASSERT_EQUAL (NVSTORE_SUCCESS, ret);
471495 }
472496
497+ calc_thread_stack_size (num_threads, thr_test_min_stack_size, thr_test_max_stack_size, stack_size);
498+ if (!num_threads) {
499+ printf (" Not enough heap space to run test. Test skipped\n " );
500+ goto end;
501+ }
502+
473503 for (i = 0 ; i < num_threads; i++) {
474- threads[i] = new rtos::Thread ((osPriority_t)((int )osPriorityBelowNormal- num_threads+ i), thr_test_stack_size );
504+ threads[i] = new rtos::Thread ((osPriority_t)((int )osPriorityBelowNormal - num_threads + i), stack_size );
475505 threads[i]->start (callback (thread_test_worker));
476506 }
477507
478508 wait_ms (thr_test_num_secs * 1000 );
509+ thr_test_data->stop_threads = true ;
510+
511+ wait_ms (1000 );
479512
480513 for (i = 0 ; i < num_threads; i++) {
481- threads[i]->terminate ();
482514 delete threads[i];
483515 }
484516
485517 delete[] threads;
486518
487- wait_ms (1000 );
488-
489- nvstore.deinit ();
519+ ret = nvstore.deinit ();
520+ TEST_ASSERT_EQUAL (NVSTORE_SUCCESS, ret);
490521
491- nvstore.init ();
522+ ret = nvstore.init ();
523+ TEST_ASSERT_EQUAL (NVSTORE_SUCCESS, ret);
492524
493525 for (key = 0 ; key < thr_test_data->max_keys ; key++) {
494526 thread_test_check_key (key);
495- TEST_ASSERT_EQUAL (NVSTORE_SUCCESS, ret);
496527 }
497528
529+ end:
498530 for (key = 0 ; key < thr_test_data->max_keys ; key++) {
499531 for (i = 0 ; i < thr_test_num_buffs; i++) {
500532 delete[] thr_test_data->buffs [key][i];
@@ -503,9 +535,12 @@ static void nvstore_multi_thread_test()
503535
504536 delete thr_test_data;
505537
538+ nvstore.reset ();
539+
506540#endif
507541}
508542
543+
509544static void race_test_worker (void *buf)
510545{
511546 int ret;
@@ -519,18 +554,20 @@ static void nvstore_race_test()
519554{
520555#ifdef MBED_CONF_RTOS_PRESENT
521556 int i;
557+ uint32_t stack_size;
522558 uint16_t initial_buf_size;
523559 int ret;
524560 rtos::Thread *threads[race_test_num_threads];
525561 uint8_t *get_buff, *buffs[race_test_num_threads];
562+ int num_threads = race_test_num_threads;
526563 uint16_t actual_len_bytes;
527564
528565 NVStore &nvstore = NVStore::get_instance ();
529566
530567 ret = nvstore.reset ();
531568 TEST_ASSERT_EQUAL (NVSTORE_SUCCESS, ret);
532569
533- initial_buf_size = std::min ((nvstore.size () - race_test_data_size) / 2 , (size_t ) 256 );
570+ initial_buf_size = std::min ((nvstore.size () - race_test_data_size) / 2 , (size_t ) race_test_data_size );
534571 uint8_t *initial_buf = new uint8_t [initial_buf_size];
535572 int num_sets = (nvstore.size () - race_test_data_size) / initial_buf_size;
536573 for (i = 0 ; i < num_sets; i++) {
@@ -539,33 +576,42 @@ static void nvstore_race_test()
539576 }
540577 delete[] initial_buf;
541578
542- for (i = 0 ; i < race_test_num_threads; i++) {
579+ get_buff = new uint8_t [race_test_data_size];
580+
581+ calc_thread_stack_size (num_threads, race_test_min_stack_size, race_test_max_stack_size, stack_size);
582+ if (!num_threads) {
583+ printf (" Not enough heap space to run test. Test skipped\n " );
584+ goto end;
585+ }
586+
587+ for (i = 0 ; i < num_threads; i++) {
543588 buffs[i] = new uint8_t [race_test_data_size];
544589 gen_random (buffs[i], race_test_data_size);
545590 }
546591
547- for (i = 0 ; i < race_test_num_threads ; i++) {
548- threads[i] = new rtos::Thread ((osPriority_t)((int )osPriorityBelowNormal - race_test_num_threads + i), thr_test_stack_size );
592+ for (i = 0 ; i < num_threads ; i++) {
593+ threads[i] = new rtos::Thread ((osPriority_t)((int )osPriorityBelowNormal - num_threads + i), stack_size );
549594 threads[i]->start (callback (race_test_worker, (void *) buffs[i]));
550595 threads[i]->join ();
551596 }
552597
553- get_buff = new uint8_t [race_test_data_size];
554598 ret = nvstore.get (race_test_key, race_test_data_size, get_buff, actual_len_bytes);
555599 TEST_ASSERT_EQUAL (NVSTORE_SUCCESS, ret);
556600 TEST_ASSERT_EQUAL (race_test_data_size, actual_len_bytes);
557601
558- for (i = 0 ; i < race_test_num_threads ; i++) {
602+ for (i = 0 ; i < num_threads ; i++) {
559603 if (!memcmp (buffs[i], get_buff, actual_len_bytes)) {
560604 break ;
561605 }
562606 }
563- TEST_ASSERT_NOT_EQUAL (race_test_num_threads , i);
607+ TEST_ASSERT_NOT_EQUAL (num_threads , i);
564608
565- for (i = 0 ; i < race_test_num_threads ; i++) {
609+ for (i = 0 ; i < num_threads ; i++) {
566610 delete threads[i];
567611 delete[] buffs[i];
568612 }
613+
614+ end:
569615 delete[] get_buff;
570616#endif
571617}
0 commit comments