Question

I've been using C# for a while, and have recently started working on adding parallelism to a side project of mine. So, according to Microsoft, reads and writes to ints and even floats are atomic

I'm sure these atomicity requirements workout just fine on x86 architectures. However, on architectures such as ARM (which may not have hardware floating point support), it seems these guarantees will be hard.

The problem is only made more significant by the fact that an 'int' is always 32-bits. There are many embedded devices that can't atomically perform a 32-bit write.

It seems this is a fundamental mistake in C#. Guaranteeing the atomicity of these data types can't be done portably.

How are these atomicity guarantees intended to be implemented on architectures where there are no FPUs or 32-bit writes?

Was it helpful?

Solution

There are two issues with regard to "portability":

  1. Can an practical implementation of a language be produced for various platforms
  2. Will a program written in a language be expected to run correctly on various platforms without modification

The stronger the guarantees made by a language, the harder it will be to port it to various platforms (some guarantees may make it impossible or impractical to implement the language on some platforms) but the more likely it is that programs written in the language will work without modification on any platform for which support exists.

For example, a lot of networking code relies upon the fact that (on most platforms) an unsigned char is eight bits, and a 32-bit integer is represented by four unsigned chars in ascending or descending sequence. I've used a platform where char was 16 bits, sizeof(int)==1, and sizeof(long)==2. The compiler author could have made the compiler simply use the bottom 8 bits of each address, or could have added a lot of extra code so that writing a 'char' pointer would shift the address right one bit (saving the lsb), read the address, update the high or low half based upon the saved address lsb, and writing it back. Either of those approaches would have allowed the networking code to run without modification, but would have greatly impeded the compiler's usefulness for other purposes.

Some of the guarantees in the CLR mean that it is impractical to implement it in any platform with an atomic operation size smaller than 32 bits. So what? If a microcontroller needs more than a few dozen Kbytes of code space and RAM, the cost differential between 8-bit and 32-bit is pretty small. Since nobody's going to be running any variation of the CLR on a part with 32K of code space and 4K of RAM, who cares whether such a chip could satisfy its guarantees.

BTW, I do think it would be useful to have different levels of features defined in a C spec; a lot of processors, for example, do have 8-bit chars which can be assembled into longer words using unions, and there is a lot of practical code which exploits this. It would be good to define standards for compilers which work with such things. I would also like to see more standards at the low end of the system, making some language enhancements available for 8-bit processors. For example, it would be useful to define overloads for a function which can take a run-time-computed 16-bit integer, an 8-bit variable, or an inline-expanded version with a constant. For often-used functions, there can be a big difference in efficiency among those.

OTHER TIPS

It's not too difficult to guarantee the atomicity with runtime checks. Sure, you won't be as performant as you might be if your platform supported atomic reads and writes, but that's a platform tradeoff.

Bottom line: C# (the core language, not counting some platform-specific APIs) is just as portable as Java.

That's what the CLI is for. I doubt they will certify an implementation if it isn't compliant. So basically, C# is portable to any platform that has one.

The future happened yesterday, C# is in fact ported to a large number of embedded cores. The .NET Micro Framework is the typical deployment scenario. Model numbers I see listed as native targets are AT91, BF537, CortexM3, LPC22XX, LPC24XX, MC9328, PXA271 and SH2.

I don't know the exact implementation details of their core instruction set but I'm fairly sure that these are all 32-bit cores and several of them are ARM cores. Writing threaded code for them requires a minimum guarantee and atomic updates for properly aligned words is one of them. Given the supported list and that 4 byte atomic updates for aligned words is trivial to implement in 32-bit hardware, I trust they all do in fact support it.

Excessively weakening guarantees for the sake of portability defeats the purpose of portability. The stronger the guarantees, the more valuable the portability. The goal is to find the right balance between what the likely target platforms can efficiently support with the guarantees that will be the most useful for development.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top