Global Array Definition - Race Condition?

Probably bad coding. But this has always worked (and still does) for my Edge devices (SDK 3.3). I just added a ForeRunner 955 and now it fails for that device (SDK 4.1).

I define default values for UserSettings in a global array (and overwrite that value with User-defined values if they change the value). So for example, in one source file I define: UserSettings[12,"Test",false].

In another source file, I need to define a Global Array of size X, where X is UserSettings[0]. So I do this:

Iterations = new[UserSettings[0]+1];

================

There are several issues with this. So I'll fix this sloppy code. But just for curiosity, for my EDGE devices, it runs fine. The runtime resolves the UserSettings array definition first and then can apply the "12" to the size of the Iterations array when it is defined.

But when I run it (in the simulator) for the ForeRunner, I get this. This suggests the Iteration array definition occurred before the UserSettings array was initialized. So besides poor coding (it does compile fine)... is this just an ambigious race condition - the UserSettings array may or may not be defined before Iterations uses the value?

Runtime Error on the ForeRunner device:

Error: Unhandled Exception
Exception: UnexpectedTypeException: Expected Object/Array/Dictionary/ByteArray, given null
Stack:
- <init>() at C:\Users\DaveBRILLHART\GARMIN_CIQ\ConnectIQ\MyProjects\HR_Stress_Run\source\CalcFunctions.mc:65 0x100001c0
-
Native code0xffffffff

  • Could you please clarify where all of this code is happening? Is it all at the global scope? It seems to be that way based on your post.

    i.e. I understand that at the global scope, you have...

    ...in one file ("File A"):

    var UserSettings = [12, "Test", false];

    ...in another file ("File B"):

    var Iterations = new [UserSettings[0] + 1];

    I mean yeah, your code depends on the order of initialization here. When you write a variable declaration at the global scope like this...

    var x = "foo";

    ...two things are happening: you're declaring that the variable exists, and you're specifying its initial value (which is supposed to be applied before any other code runs). Obviously when you have globals in different files, the compiler has to choose *some* order to initialize them, and you can't control what it is, given that they are in different files. If they were in the same file, you might reasonably expect variables to be initialized in order of appearance, but since they're in different files, it seems to me that the order in unspecified or ambiguous as you said. So when it works for you, it's only by coincidence that the compiler chose the order that doesn't make your code crash. 

    Your app might not be optimally structured if it has global variables just floating around in different files, yet they depend on each other. In an ideal world, you'd have no global variables or functions (I break this rule all the time to save memory), but if you must, seems like global variables in different files should not have dependencies on each other -- even if your code doesn't crash, it's hard to maintain and reason about code which is written that way ("spaghetti code"). Conceptually, anything that's used to store some kind of state should go in its respective class (e.g. app class, view class, or a class related/belonging to one of the above.)

    So I would suggest one of a few approaches:

    - restructure your code so there's no global variables or at least none that are dependent on each other across files. For example, UserSettings could become an actual class instead of a free-floating global. You could create and initialize an instance of UserSettings in your app base and pass it down to your initial view

    - if you must have global variables in separate files, then write a single global initializer function which is called at AppBase.initialize() or onStart(). But the fact that you would have a single centralized function to initialize variables that are spread out across your program again implies that they shouldn't be spread out like that. e.g.

    File A:

    var UserSettings = null;

    File B:

    var Iterations = null;

    File C:

    function initializeGlobals() {
      UserSettings = [12, "Test", false]
      Iterations = new [UserSettings[0] + 1];
    }

    Note that File C has to know about stuff in File A and File B, which isn't optimal. Same argument if you put initializeGlobals() in File A or File B. Either way, AppBase would also have to know to call initializeGlobals(), which also isn't optimal.

  • Having said all that, it would be nice to see a compiler error message such as "The value of UserSettings may be accessed before it's initialized" or something like that.