Advice about accessing data (but not modifying) from another thread.

dibosco wrote on Tuesday, November 10, 2015:

I’m trying to understand what is good practice in terms of accessing data created/modified in another thread.

It seems that a queue is obviously the safest way of passing data from one thread to another, but what about when one thread merely needs to know about the status of somehting in another thread?

Is it acceptable to have an accessor function that reads a static variable in the module the other thread is contained in, to read and return, say, a flag?

For example:

I have a thread that parses an XML file and puts together which of the sixteen DALI scenarios are valid.

I have another thread that receives UDP packets in, with one of the fields in the UDP packet a request to switch to a new DALI scenario. If the DALI scenario is not valid, according to the XML thread, the scenario should not be requested over the DALI bus.

In this case, I have created a static variable in the XML parsing module that is a bit map, each bit set or cleared to define whether a scenario is valid. Is it acceptable for the UDP thread just to request that bit map and find out whether a particular scenario is valid? Or must I send that bit map to the UDP thread via a queue? If so. must, or should, that bit map be sent to the UDP thread or is it acceptable just to have a static variable accessible by the whole of the module that contains the UDP thread?

I have in the past (in other projects) had a queue of just length of one 32-bit variable - which is 32 flags - and just written to and read/peeked from/at that queue from lots of threads so all threads can at any time just look at the flags or read them out, modify them and restore them. In the end this is more of a faff. However, it may be that it is the only really thread-safe way of doing things?

Or is there a third way I have not thought of?

Having a separate queue for all different types of data for different threads to access like this soon really eats into precious RAM.

Many thanks :slight_smile:

heinbali01 wrote on Tuesday, November 10, 2015:

Hi DiBosco,

In most cases, I would share that kind of information without any protection, as long as it is clear that there is only one task allowed to change the data.

I’m sure you know about this potential optimisation problem:

	extern BaseType_t isReady;
	/* Here it is not clear for the compiler that 'isReady'
	may be changed by another task.
    The test may be performed only once and the loop
    might never end. */
	while( isReady == pdFALSE )
		// Do something

	/* This construction forces the compiler to fetch
	'isReady' in every loop. */
	while( *( ( volatile BaseType_t * )&isReady ) == pdFALSE )
		// Do something

But normally, all variables that you test will be fetched when required. The loop here above would be an exception.

If your data may also be changed by an interrupt, you’ll need a critical section.

    // change the data
    isReady = isReady + 1;

The interrupt which changes the data does not need a critical section, because the interrupt normally won’t get interrupted by an interrupt touching the same data.

If your data may only be changed by another task (not by an interrupt), you may suspend the scheduler:

    // change the data
    isReady = isReady + 1;

If changing the data is an atomic action (i.a. a single assembler instruction), no protection is needed.

    /* 'isready' is accessed by a single instruction. */
    isReady = 0;

But as said, in your application the common data is managed by a single task, so it doesn’t need any protection.


richard_damon wrote on Tuesday, November 10, 2015:

Hein, the answer to the optimization problem is to mark the variable ‘volatile’, which is designed to prevent this type of optimization and require the compiler to access the variable fresh each time it is used (Some C Purest will point out that volatile might not be quite enough, as it might not force cache coherency, but this is normally only a problem on multi-processor systems, for which FreeRTOS really isn’t designed).

As to accessing the data, as long as you can make the accesses act ‘atomic’ then there is no problem. If only one thing writes to the data, and it is a simple small variable, this is normally not a big problem. If not, then you will need some sort of protection to make the actions act atomic. This can be critical sections, stoping the scheduler, or using a mutex/semaphore depending on the need.

dibosco wrote on Wednesday, November 11, 2015:

Thanks folks, that’s most helpful :slight_smile: