Why does rand() or random() return 0 in threads but is OK in main?

I have an STM32G071 running FreeRTOS CMSIS v2 compiled with STM32CubeIDE. In one of the threads I want to generate a pseudo-random number. (I don’t need anything sophisticated and the STM32G071 doesn’t have an RNG anyway; pseudo-random is absolutely fine for my purposes).

If I call rand() or random() from anywhere in main() before I call osKernelStart(), all is well but if I call rand() or random() from anywhere within a thread they return 0.

Update:

I can call rand() or random() in a thread if I call srand(), srandom(), rand() or random() from main before I start the kernel.

As far as I can tell, it doesn’t matter which function so long as something calls one of those functions before attempting to call rand() or random() from a thread.

Why would I have to do that?

Are you sure you’re really calling the same functions? It sort of sounds like the wrong rand/random functions are being linked in. I’m not familiar with STM32CubeIDE, but I’ve seen plenty of badly behaved IDEs that will grab unexpected functions from libraries all over the place.

Calling it first in main may create the prototype and then the linker doesn’t grab some far off rand() function and glue it in.

The easiest way to confirm is to compile with no optimization, debug and single step into the rand() function.

@Pilot It’s hard to be categorical because you can’t step into them even in the disassembler to the address but given the same call works in one context and not another, I think it’s likely they are the same.

I’m just assuming there is something that rand() or random() depend on that can no longer be accessed once the kernel is running (e.g. the pseudo-random generator doesn’t bother initialising something until it’s called the first time).

@Pilot - actually they are the same, I made a copy of the function pointers (which were identical) and called them via that instead.

Where are the random functions coming from? I assume your C library, and if so, which C library?

@rtel Yes, the C library linked in as standard with gcc. I haven’t linked to anything else.

…and which C library is it? For example, is it newlib?

@rtel I’m ashamed to say I don’t know. How do I find out? (I can’t find any mention of newlib in the build settings or the build console output).

Check the Cube docs or your cross compiler install folder. Supposed it’s a GNU/GCC toolchain there should be a readme file and various license disclaimers. Usually embedded GCC toolchains come with newlib, which is a embedded variant of the standard GNU libc.
Also you should check the map file (might be a build option to generate it along with the elf target) where the rand etc. functions come from.

CubeIDE does use newlib. There are a couple of problems with the code and projects generated by CubeIDE when using FreeRTOS. See here: http://www.nadler.com/embedded/newlibAndFreeRTOS.html.

Does your generated project have a file called sysmem.c in it? If so, delete it. That version of _sbrk() that isn’t compatible with FreeRTOS. It causes malloc() (called by newlib code) to fail once the scheduler starts. If you delete sysmem.c, the newlib build included with CubeIDE actually provides a working _sbrk() for you.

@hs2 It appears to be libc_nano in the map file. Does that make sense?

Should be ok. Tried it myself also using newlib nano and for me rand() is working when called from a task… strange.

@hs2 Yes, very strange.

Oh wait - it looks as though deleting sysmem.c might be fixing it. I found that sometimes, Cube was recreating it but I’m not sure what triggers that. It’s not every time I save the .ioc file.

@jefftenney Thank you. Deleting sysmem.c seems to make rand() and random() work in tasks/threads although sometimes CubeIDE recreates the file. I’m not sure what causes this - I tried using the CubeIDE editor to change bits of the .ioc file but it didn’t cause sysmem.c to reappear…

I just ran a quick test with a new test project, and CubeIDE didn’t regenerate sysmem.c for me. I wonder what makes it regenerate that file.

Anyway maybe you could exclude the file from the build then. Right click the file, choose Resource Configurations, Exclude from build, and then exclude it from all build configs.

If that works and if you continue with the library version of _sbrk() (not the sysmem.c version), beware that it does not check for the “out-of-memory” condition. That’s usually OK in CubeIDE generated projects because there is generally enough heap memory to satisfy newlib. However, a more robust solution is available at the link I posted before, along with other useful information about using FreeRTOS with newlib.