Atomicity in the MF - General Discussion - Netduino Forums
   
Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

Atomicity in the MF


  • Please log in to reply
25 replies to this topic

#1 Mario Vernari

Mario Vernari

    Advanced Member

  • Members
  • PipPipPip
  • 1768 posts
  • LocationVenezia, Italia

Posted 17 October 2011 - 07:26 AM

Hello everybody.
I wondering whether the threading model in the Netduino/Micro Framework behaves similarly to the Full one, regarding the atomicity of certain objects.

In other words, when I read/write an Int32, I am guaranteed about the atomicity of the operation, but that does not apply for larger structures, such as Int64, Double, DateTime, etc.
This is surely true for the Full Framework, but does still apply for the simplified threading model of the MF?

For those are avid of examples:

class Program
{
  static Int64 _counter;

  static Main()
  {
    new Thread(Worker).Start();
    new Thread(Worker).Start();
    Thread.Sleep(Timeout.Infinite);
  }

  static Worker()
  {
    _counter = _counter + 1;
  }
}
Will destroy the "_counter" variable or not?

Thank you in advance.
Cheers
Biggest fault of Netduino? It runs by electricity.

#2 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 17 October 2011 - 09:10 AM

Hi Mario, .NET MF is "cooperative-ish-ly multitasked." So IL instructions are executed individually. That said, native code interrupts to handle things like serial buffers and network transfers will still interrupt execution. As far as your example goes...it is possible for the counter value to be read in one managed instruction and then have the CLR switch execution to another thread (mangling your results). But since the multitasking system has a quantum on the order of milliseconds...I'm not sure that would happen in _this_ case. If you're worried about atomicity, I would use monitors (locks) as required. We do that in our cross-platform NETMF/NET/NETCF/Silverlight code. Has anyone else dug into the bowels of the CLR to look at atomicity of INT32s vs INT64s? I'd never investigated this question before... Does that help? Chris

#3 Mario Vernari

Mario Vernari

    Advanced Member

  • Members
  • PipPipPip
  • 1768 posts
  • LocationVenezia, Italia

Posted 17 October 2011 - 09:25 AM

My actual deal is pretty much about the DateTime struct, because I'm writing a RTC driver. The behavior is as expected, and it's better that follows the standard framework guidelines. Even it requires a bit of more code. Anyway, it was much more a knowledge point, rather a strict designing rule. Thank you so much. Cheers
Biggest fault of Netduino? It runs by electricity.

#4 Stefan W.

Stefan W.

    Advanced Member

  • Members
  • PipPipPip
  • 153 posts

Posted 17 October 2011 - 09:41 AM

Has anyone else dug into the bowels of the CLR to look at atomicity of INT32s vs INT64s? I'd never even thought of this question before...


From partition I of the CLI specification:

A conforming CLI shall guarantee that read and write access to properly aligned memory locations no larger than the native word size (the size of type native int) is atomic (...)

Note: There is no guarantee about atomic update (read-modify-write) of memory, except for methods provided for that purpose as part of the class library (see Partition IV). (...)

Note: There is no guaranteed atomic access to 8-byte data when the size of a native int is 32 bits even though some implementations may perform atomic operations when the data is aligned on an 8-byte boundary.


In the case of the code sample from Mario, Interlocked.Increment would be the tool of choice.
I believe that no discovery of fact, however trivial, can be wholly useless to the race, and that no trumpeting of falsehood, however virtuous in intent, can be anything but vicious.
-- H.L. Mencken, "What I Believe"

#5 Mario Vernari

Mario Vernari

    Advanced Member

  • Members
  • PipPipPip
  • 1768 posts
  • LocationVenezia, Italia

Posted 17 October 2011 - 10:02 AM

Because the lack of tools to debug/inspect the realtime of a circuit running the MF (respect an usual workbench based on the full framework), I guess should be paid some careful by playing with threads. The forum is plenty of users that bumped on some issue, maybe trivial, but wherever possible it should be chosen the most reliable way firstly. At this point, it comes to me another question: has anyone tried Netduino/MF with standard Visual Studio? I mean: anyone knows whether the VS thread-view debug tool is working with the MF also? (The life is getting harder day by day...)
Biggest fault of Netduino? It runs by electricity.

#6 georgejh

georgejh

    Advanced Member

  • Members
  • PipPipPip
  • 87 posts
  • LocationGlasgow, UK

Posted 17 October 2011 - 07:21 PM

Hi Mario,

Read this series of posts to learn a bit more about multithreading.
I believe in part 2 you will find solution for your problem.

#7 Mario Vernari

Mario Vernari

    Advanced Member

  • Members
  • PipPipPip
  • 1768 posts
  • LocationVenezia, Italia

Posted 18 October 2011 - 03:37 AM

Hi Mario,

Read this series of posts to learn a bit more about multithreading.
I believe in part 2 you will find solution for your problem.

Thank you very much, Georgi.
I read your posts, but my doubt was about the "usefulness" of certain functions.
I could believe that the threading model in the MF, being simplified, could "wait" for some basic functions, instead of breaking anywhere.
Nothing more than this...
Cheers
Biggest fault of Netduino? It runs by electricity.

#8 Corey Kosak

Corey Kosak

    Advanced Member

  • Members
  • PipPipPip
  • 276 posts
  • LocationHoboken, NJ

Posted 19 October 2011 - 12:39 AM

Maybe this is obvious to everyone else, but I felt it might be worthwhile to clarify Mario's question. This might be helpful to other readers.

In this case the question is not really about the usual read-modify-write race between two threads. It is about whether two threads can safely write an object which is wider than 32 bits.

So a simpler example is this one:

void Worker1() {
  _counter = 0x1111111111111111;
}

void Worker2() {
  _counter = 0x2222222222222222;
}

The key question is whether this interleaving of operations is possible:

* Worker1 writes low word of _counter (11111111)
* Worker2 writes both low and high words of _counter (2222222222222222)
* Worker1 writes high word of _counter (11111111)

If such an interleaving is possible, then we are in the worst of both worlds. The result would be 0x1111111122222222, which means that neither Worker1 nor Worker2 won the race; rather, the result was a corrupt combination of Worker1's and Worker2's output.

Now this may happen to work on the current Netduino, which as Chris says probably won't suspend a thread in the middle of a CLR instruction. But who knows, maybe some day there will be a JITter on the MicroFramework, or some other way to compile to native code on the device, and then code like this may well break. One might as well get into the habit of writing code that conforms to the .NET standard (using e.g. lock() or Interlocked.*), as these habits will pay off handsomely over time.

#9 Mario Vernari

Mario Vernari

    Advanced Member

  • Members
  • PipPipPip
  • 1768 posts
  • LocationVenezia, Italia

Posted 19 October 2011 - 03:34 AM

Thank you Corey: absolutely perfect! Also, I missed to use the correct terms: "race condition" and "interleaving". Anyway, I feel that -jitter or not- the execution may break at the current status as well. Since, the break-ability may happen at CLI-level, why does not have to be here? Cheers
Biggest fault of Netduino? It runs by electricity.

#10 georgejh

georgejh

    Advanced Member

  • Members
  • PipPipPip
  • 87 posts
  • LocationGlasgow, UK

Posted 20 October 2011 - 11:51 AM

Hi Mario,

Sorry I misunderstood your question first time. I hope I understand it correctly this time :)

As longer as there is only one CPU/ALU those threads are executed one after (not in parallel as it may appear to you), so there is no danger one thread to overwrite half of the memory bits shared with another thread. When there is multi-core system .NETMF port should take care of this situation. In addition, most of multi-core CPU's have integrated resource management system (resource scheduler) that handles those situations.

Here you can read more details about ARM7, which Netduino is using. In point 1.3 you clearly can see there is a single ALU.

Is that answer your question or I missed the whole point? :P

Cheers

#11 Mario Vernari

Mario Vernari

    Advanced Member

  • Members
  • PipPipPip
  • 1768 posts
  • LocationVenezia, Italia

Posted 20 October 2011 - 12:20 PM

Hi Mario,

Sorry I misunderstood your question first time. I hope I understand it correctly this time :)

As longer as there is only one CPU/ALU those threads are executed one after (not in parallel as it may appear to you), so there is no danger one thread to overwrite half of the memory bits shared with another thread. When there is multi-core system .NETMF port should take care of this situation. In addition, most of multi-core CPU's have integrated resource management system (resource scheduler) that handles those situations.

Here you can read more details about ARM7, which Netduino is using. In point 1.3 you clearly can see there is a single ALU.

Is that answer your question or I missed the whole point? :P

Cheers

ARE YOU SURE ABOUT IT?
Multicores and threads are friends hanging on together, but not twins!
Biggest fault of Netduino? It runs by electricity.

#12 georgejh

georgejh

    Advanced Member

  • Members
  • PipPipPip
  • 87 posts
  • LocationGlasgow, UK

Posted 20 October 2011 - 12:38 PM

ARE YOU SURE ABOUT IT?
Multicores and threads are friends hanging on together, but not twins!


Yes, I am sure :) Additionally you can have a look here.
"..Many families of microcontrollers and embedded processors have multiple register banks to allow quick context switching for interrupts. Such schemes can be considered a type of block multithreading among the user program thread and the interrupt threads...."

Porting .NETMF is also good read.

#13 Cuno

Cuno

    Advanced Member

  • Members
  • PipPipPip
  • 144 posts
  • LocationZürich / Switzerland

Posted 20 October 2011 - 04:07 PM

ARE YOU SURE ABOUT IT?

No.

There are two questions here: what behavior is required by the C# language specification on the one hand, and what behavior is actually implemented by NETMF on the other hand.

In the C# language specification, section 5.5 Atomicity of variable references, the following is said:

Reads and writes of other types, including long, ulong, double, and decimal, as well as user-defined types, are not guaranteed to be atomic.

So according to the language specification, Corey's "worst of both worlds" scenario may indeed happen.

I don't know whether the NETMF interpreter implements stronger guarantees, but as Corey says it wouldn't be a good idea to rely on them anyway. If a JITter for NETMF materializes at some point in the future, it may well behave very differently.

There are no magic processor feature that solve these problems. The so-called "memory models" of C#, Java, Intel processors, and ARM processors are all subtly different. On the more complex processors, you have things like caches and out-of-order execution of instructions, all in order to improve performance. In general, the weaker guarantees a memory model provides, the faster it can be implemented in hardware - and the more likely you are to inadvertantly do something that may break on another processor or on another .NET implementation.

I found more about memory models than I ever wanted to learn by googling "Joe Duffy CLR memory model"...

#14 georgejh

georgejh

    Advanced Member

  • Members
  • PipPipPip
  • 87 posts
  • LocationGlasgow, UK

Posted 20 October 2011 - 04:30 PM

No.

There are two questions here: what behavior is required by the C# language specification on the one hand, and what behavior is actually implemented by NETMF on the other hand.

In the C# language specification, section 5.5 Atomicity of variable references, the following is said:


So according to the language specification, Corey's "worst of both worlds" scenario may indeed happen.

I don't know whether the NETMF interpreter implements stronger guarantees, but as Corey says it wouldn't be a good idea to rely on them anyway. If a JITter for NETMF materializes at some point in the future, it may well behave very differently.

There are no magic processor feature that solve these problems. The so-called "memory models" of C#, Java, Intel processors, and ARM processors are all subtly different. On the more complex processors, you have things like caches and out-of-order execution of instructions, all in order to improve performance. In general, the weaker guarantees a memory model provides, the faster it can be implemented in hardware - and the more likely you are to inadvertantly do something that may break on another processor or on another .NET implementation.

I found more about memory models than I ever wanted to learn by googling "Joe Duffy CLR memory model"...


Here we are talking about .NETMF and not for C# language specification (small but very significant difference).
"Due to the constraints under which it operates, the .NET Micro Framework does have some limitations beyond those imposed by its slimmed-down libraries. For example, the platform does not support symmetric multi-processing, multi-dimensional arrays, machine-dependent types, or unsafe instructions. The CLR is an interpreter rather than a just-in-time compiler, and uses a simpler mark-and-sweep garbage collector rather than a generational approach. Interoperation between managed and native code currently has a number of limitations. The .NET Micro Framework does not support any .NET languages other than C# at this time."

In single CPU environment with block type multithreading model, to switch from one thread to another, first thread has to finish and return result (atomic operation), which will be stored into the threads context , before second thread get's restored with its context and given an execution time.

But whatever, if you choose to believe it is not safe then it is up to you :)

#15 liqdfire

liqdfire

    Advanced Member

  • Members
  • PipPipPip
  • 78 posts

Posted 20 October 2011 - 04:39 PM

I have not looked, does anyone know if the System.Threading.Interlocked class is supported in the MF?

#16 Stefan W.

Stefan W.

    Advanced Member

  • Members
  • PipPipPip
  • 153 posts

Posted 20 October 2011 - 04:41 PM

There is nothing in your text (that is also outdated) that says anything about additional guarantees, georgejh.
I believe that no discovery of fact, however trivial, can be wholly useless to the race, and that no trumpeting of falsehood, however virtuous in intent, can be anything but vicious.
-- H.L. Mencken, "What I Believe"

#17 georgejh

georgejh

    Advanced Member

  • Members
  • PipPipPip
  • 87 posts
  • LocationGlasgow, UK

Posted 20 October 2011 - 04:43 PM

I have not looked, does anyone know if the System.Threading.Interlocked class is supported in the MF?


Yes It is

#18 Stefan W.

Stefan W.

    Advanced Member

  • Members
  • PipPipPip
  • 153 posts

Posted 20 October 2011 - 04:44 PM

I have not looked, does anyone know if the System.Threading.Interlocked class is supported in the MF?


It is, see
http://msdn.microsof...y/ee434385.aspx
and
http://blogs.msdn.co...ronization.aspx
I believe that no discovery of fact, however trivial, can be wholly useless to the race, and that no trumpeting of falsehood, however virtuous in intent, can be anything but vicious.
-- H.L. Mencken, "What I Believe"

#19 Corey Kosak

Corey Kosak

    Advanced Member

  • Members
  • PipPipPip
  • 276 posts
  • LocationHoboken, NJ

Posted 20 October 2011 - 04:46 PM

But whatever, if you choose to believe it is not safe then it is up to you :)


If there is a disagreement here, it may be about what each of us regards as "safe". I read the above as a description of the NETMF's current behavior, and not a promise that it will always behave so.

#20 georgejh

georgejh

    Advanced Member

  • Members
  • PipPipPip
  • 87 posts
  • LocationGlasgow, UK

Posted 20 October 2011 - 05:01 PM

There is nothing in your text (that is also outdated) that says anything about additional guarantees, georgejh.


Go here and read "Introducing the .NET Micro Framework" and "Porting the .NET Micro Framework". Those must be quite still valid if they are on the main .NETMF site. Don't they?

Let's look on some of them:
....
The HAL kernel supports two execution modes: single-threaded application and interrupt service routine (ISR). There is no scheduler per se, as there is only a single application thread. All user applications (the shell, network protocols, alerts, games, etc.) execute within the CLR, which has its own multithreading facilities. The single application thread also reduces locking overhead. However, it means that the application running on the HAL (that is, the CLR) must use cooperative multitasking, explicitly yielding execution periodically at idle time so that continuations can be processed



Basic Features
These features are common to all compliant .NET CLRs.

....
· Time-sliced thread management
Threading
The .NET Micro Framework CLR provides multi-threading support even when running on an underlying platform or OS that does not. While the execution engine is not a true multi-threaded kernel, it is a close simulation, offering time-sliced context switching using a 20ms quantum. Threads are prioritized and, due to instruction-level interpretation, interruptible.


Synchronization
Accesses to shared resources between multiple threads of execution are synchronized to avoid corrupting state.

etc...




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.