5 years, 2 months ago.

Can the OS interrupt my thread during 64-bit operations and break the content?

In my project I read the serial number from an IC which is stored in three 16-bit registers. To get the serial number I read the data from itsregisters and store it into dataFirst, dataMiddle and dataLast, each one is a uint16_t. But I wonder if the OS can interrupt the operation while id is assigned.

As far as I know in uC programms without OS I need to make sure that this kind of operations doesn't break when an interrupt occoures. But how is it with mbed as OS? And not only hardware interrupts but also when the scheduler interrupts the task? For this project I use a 32-bit Cortex M4 controller. Do you think I need something like a semaphore or mutex?

	uint64_t id = static_cast<uint64_t>(dataFirst) <<32
	   | static_cast<uint64_t>(dataMiddle)<<16
	   | static_cast<uint64_t>(dataLast);

Thanks

1 Answer

5 years, 2 months ago.

As far as I can tell, your code can read these registers without worry - subject to a few qualifiers. The OS itself would context switch on an event or OS call; it would [generally] not context switch between instructions in a sequence.

That said, there are risks to consider.

  • If reading these registers involves some library (e.g. to interact with SPI or I2C ports), then it may have the possibility to context switch embedded within it.
  • If you have the possibility of an enabled interrupt to your sequence, and that interrupting process interacts with the same registers or peripheral, you are certainly at risk.

To present these points, here is some c-like code I didn't compile:

direct access

uint16_t * p = (uint16_t *)0x8000012;  // assume the first of the registers starts at this address.
uint16_t dataFirst = *p++;
uint16_t dataMiddle = *p++;
uint16_t dataLast = *p;
uint64_t id = ((uint64_t)dataFirst << 32) | ((uint64_t)dataMiddle << 16) | ((uint64_t)dataLast);

In the direct access example, your device is directly memory mapped. Assuming the serial number is a readable register, even an interrupt in the middle should not affect the resultant value.

lib-based access

uint64_t id = GetSerialNumber();

In the lib-based access, unless you created the library, you don't know what all might happen. If your device were on a SPI port, it is conceivable that the library could be preempted between reading any of the values, and then if that other thread used that same SPI port, you would have the high chance of corruption. This is where a sema/mutex would be used to protect access to the shared resource (the SPI port in this example).

guarded

mutex SPI_Guard;

// one thread
SPI_Guard.lock()
uint64_t id = GetSerialNumber();
SPI_Guard.unlock();

// some other thread ...
SPI_Guard.lock();
OtherAccessToSameSpi();
SPI_Guard.unlock();

In the guarded example, each service that wants to access a peripheral on the shared port must first lock it, and then unlock it. If it is already locked by the other thread, then this other accessor is blocked by the OS until the SPI_Guard is available.

See this Mutex Example