Problems with Win Thread

  • For my project I have to communicate via RS 232 with an ARM7. Therefore I start a background thread to handle the communication. In my test file everything works well, shifting this to my project mistakes occure:
    Especially the thread cannot be started, member variables are not initialized anymore, in function calls with parameters they are transferred incorrect. For example a BYTE with 0x90 is given to the called function, but the function gets a BYTE with i.e. 0xF0. Furthermore this value is changed in the called function also in codelines where this value is not used.


    All this looks like running at some point out of memory, but changes in the output base adress or the stack (project settings) did not help.


    Is there anyone having an idea?

  • OK, after days of searching for the mistake here comes the solution (may it's helpfull for someone):
    In the projects setting I used for the output "minimize size". This seems to corrupt the output, so that threads do not work anymore. After resetting this to "default" I choosed "rebuild all" to overwrite all existing "minimize size" files, then the thread works again.

  • Quote from "YoMas"

    In the projects setting I used for the output "minimize size". This seems to corrupt the output, so that threads do not work anymore.


    Sometimes this is a hint for some programming bug, for example uninitialised variables or missing "volatile" on variables. When optimizing for size, the compiler rearranges the code and often uses processor registers instead of variables in RAM. Example:


    Code
    1. static BOOL flag = FALSE;
    2. // then later within thread function:
    3. while (!flag)
    4. Sleep(1000);


    This code waits until the "flag" variable is set by another thread, e.g. the main program, and then continues. For not using all CPU time, it waits for 1 second before checking "flag" again. This code will most probably work with optimization turned off, but it will definitely fail with optimization turned on.


    Why?


    With optimization turned off, the compiler transfers the code line by line to the internal machine code. So it always uses the RAM variable "flag" directly. If another thread modifies the variable, this thread will automatically see it when loading "flag" the next time. The internal step by step machine code will basically look like this:

    Code
    1. 1: Load register X from RAM with value flag
    2. 2: If register X is non-zero, goto 5
    3. 3: Call subroutine Sleep
    4. 4: Goto 1
    5. 5: ...


    But when optimizing the code, the compiler will look at the whole function at once, trying to rearrange things in a better (more optimal) way, avoiding unnecessary work at runtime. And therefore it will recognize that you are never changing the value of variable "flag". Therefore this is a loop invariant and the compiler will move the loading of the variable and the check in front of the loop. If the value is FALSE the first time and as it is not changed within the loop, then it will stay FALSE and it does not make sense to perform this check over and over again. So the resulting code will look like this:

    Code
    1. 1: Load register X from RAM with value flag
    2. 2: If register X is non-zero, goto 5
    3. 3: Call subroutine Sleep
    4. 4: Goto 3
    5. 5: ...


    Please note that lines 1 and 2 are not in the loop anymore. So in fact lines 3 and 4 are an endless loop now.


    The solution is to use "volatile". We have to tell the compiler by hand, that "flag" is a special kind of variable, that can change the value at any time.

    Code
    1. static volatile BOOL flag = FALSE;


    Now the compiler is forced to always use the RAM variable, because the value may change for other reasons than from our own local code. This will result in code semantically more similar to the first version, that will work again. For example:

    Code
    1. 1: Goto 3
    2. 2: Call subroutine Sleep
    3. 3: Load register X from RAM with value flag
    4. 4: If register X is zero, goto 2
    5. 5: ...


    Please note that this is an improved (optimized) version compared to the first one, as the loop now only consists of three instructions (2-4) instead of four as before. This could only be achieved by rearranging the sequence of the commands. And that is exactly what is done during optimization.


    Summary:
    Working with threads is more or less parallel programming. This requires a *very* thorough attention how to synchronize all parallel tasks. Sometimes it requires mutexes, semaphores or critical sections. And of course "volatile" variables. Almost all variables that are accessed from different threads need "volatile".


    Regards,


    H. Keller

    F&S Elektronik Systeme GmbH
    As this is an international forum, please try to post in English.
    Da dies ein internationales Forum ist, bitten wir darum, Beiträge möglichst in Englisch zu verfassen.