views:

479

answers:

5

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?

+6  A: 

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.

Hank Gay
...... Exactly!
Randolpho
That's disappointing, as this requirement means the CLI isn't very portable.
Jake
@Jake Portable CLI's are not a reasonable expectation. JVM's and HALs aren't portable. The whole point of these technologies is that they enable the other bits to be portable.
Conrad Frix
@Jake: nor is C.
Randolpho
@Randolpho - show's how much you know about C.
Jake
@Jake: if you really think C is as portable as you seem to wish C# was, you don't know nearly as much about C as you think you do.
Randolpho
@Randolpho - What part of C isn't portable? I'm well versed with the standard, and am genuinely interested.
Jake
But then again C doesn't promise that much right? It might have changed with C99 but IIRC C/C++ promises for instance: 1==sizeof(char)<=sizeof(short)<=sizeof(int)<=sizeof(long).So a C compiler where sizeof(char) == sizeof (int) would be standard conforming according to that definition. In addition IIRC char can be 7 or 9 bits as long as sizeof(char) == 1. I bet alot of programs out there aren't able to cope with a 7bit char.Once again my knowledge might be out of sync since I have not looked into the details of C99.
FuleSnabel
@Jake: @FuleSnabel has provided an excellent example of my point. C is portable -- to a point. You can write very portable C -- usually. But there are frequently little implementation details that crop up.
Randolpho
@FuleSnabel: C99 guarantees that a char is at least 8 bits.
Jake
@Randolpho: It's not a good point. Flexible type sizes are why C is so portable. My original question is an example of the C# standard being unreasonably strict. As a result, some architectures will never be able to support C#.
Jake
@Jake: I think you may have a different definition of "portable" than I do. If I have to jump through hoops and deal with edge cases to write code that runs the same on two different platforms, the language is not portable.
Randolpho
+12  A: 

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.

Randolpho
Can you explain what you mean by: "It's not too difficult to guarantee the atomicity with runtime checks"?
Jake
If the only way one ever wrote to a 32-bit data type were via library routine, that routine could make sure it never started a 32-bit write while another was in progress. I suspect that's what will happen with things like Interlocked.Increment on 64-bit values, since there is an explicit warning that 64-bit Interlocked operations are only guaranteed atomic with regard to other 64-bit Interlocked operations.
supercat
@Jake: it's called a [semaphore](http://en.wikipedia.org/wiki/Semaphore_%28programming%29).
Randolpho
Easy - generate native code that surrounds all accesses to those types with appropriate locking. It'll probably be slow, but the guarantees can be met.
Andrew Medico
@Andrew - I suppose that makes sense. You could lock around every single read / write of an int. That seems like it'd cause a huge overhead though. I guess that'd make it portable though. But then I'd have to say: C# is fundamently inefficient on certain architectures. And, it could be made efficient by removing this requirement that these types behave atomically.
Jake
@Jake: The whole point of higher-level languages like C#, Java, and Python are that we are willing to trade off "efficiency" for "rapid development", "portability" (to an extent), and "maintainability" (although it's quite possible to write unmaintainable code in any language). Is C# faster than raw C? Probably not. Is raw C faster than platform assembly? Probably not. Does it matter? Not really.
Randolpho
@Randolpho - I like C#. I want C# to be very portable. It seems they could have dropped the 'ints / floats' are atomic requirement and made it significantly moreso. What's wrong with using Interlocked* methods when atomicity is needed?
Jake
@Jake: and what's wrong with *not* using them when it's guaranteed by the framework? IMO, the guarantee solves far more problems than dropping it would.
Randolpho
@Randolpho: How does on go about not using the feature that reads and writes to an int are atomic?
Jake
@Jake: my point is that we shouldn't be forced to use Interlocked* methods when the framework can take care of that for us -- that's the whole point of a framework like .NET or Java. You might as well be arguing that we should go back to unmanaged memory. We as developers know the pros and cons of managed memory and choose the pros because they outweigh the cons.
Randolpho
+7  A: 

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.

Hans Passant
I've looked up each of those architectures. Not only are they all 32-bit, they also have dedicated FPU's. This still doesn't answer the question of architectures that are neither of those.
Jake
@Jake: I wouldn't hold my breath waiting for C# to be ported to them. There's little point, executing IL does require a bit of horse power.
Hans Passant
Personally, I'm still holding out for a CLI implementation that can run on my old NES.
Dan Bryant
@Dan: your old NES getting ported to CLI is however likely :)
Hans Passant
@Dan: lol, who woulda thought all those old NES games would be so portable?
Jake
@Jake, I don't think the NES emulator writers would agree. The early cartridge-based console games tended to operate at very low levels, often relying on odd quirks in the behavior of the NES CPU and chipsets. Because they were cartridges, they could also include their own custom electronics, which sometimes included custom co-processors for special effects not supported by the main CPU. Back in that era, portability was not really a concern; developers were relying on the minute peculiarities of each platform, trying to push the limits of performance.
Dan Bryant
+2  A: 

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.

Dan Bryant
+2  A: 
supercat
+1 Very well said.
Randolpho