Proper variable access for function return

I couldn’t seem to find a straight answer by searching around, but is calling a function that returns a value from a variable in another task, thread safe? I understand that if the data can be copied atomically, then it should be protected enough. But would a mutex be needed for the example below? Or say if the size of the data is bigger like some multi-variable struct, I assume a queue is the safer route to go?

/*Task A is in charge of gathering some sensor value, and a function exists to give other tasks access to that variable*/
uint32_t rawSensor = 0;
uint32_t Sensor_A_Value(void)
{
  return rawSensor;
}

void TaskA(void) {
  rawSensor = ReadSensor();//Gather data
}


/*Task B is charge of periodically looking at sensors and doing something with their value*/
uint32_t sensorSample = 0;
void TaskB(void){
  //Check other sensors
  sensorSample = Sesnor_A_Value();
}

Thanks in advance,

Sensor_A_Value() is not defined in TaskA() and is callable by all tasks, so this is really a question about sharing global (or file scope via an accesser function as in your example).

Access to the variable is atomic if the width of the variable equals (or in most if not all cases larger) than the natural word size of the processor. For example, a 32-bit access on a 32-bit processor is atomic, whereas accessing a 32-bit variable on a 16-bit processor is not as it needs to be done in two steps.

If accesses are atomic, and you only have one writer (by which I mean task or interrupt that writes to the shared variable) you can have any number of readers (by which I mean tasks or interrupts that read the shared variable) without needing to have any mutual exclusion.

Any time there is an access that is not atomic you need to have some form of mutual exclusion on the access (more on this below).

Any time you have multiple writers to a variable you need to have some form of mutual exclusion (as one write may load the value into memory, then another task may change the value before the first task writes it back).

Regarding mutual exclusion mechanisms. In this case a semaphore is way too heavy, all you need is a critical section. So your access function would look like this:

uint32_t Sensor_A_Value(void)
{
    taskENTER_CRITICAL(); /* Enter critical section to protect access. */
    return rawSensor;  /* Access is safe here. */
    taskEXIT_CRITICAL(); /* Must exit critical section again. */
}