Welcome to our new forum
All users of the legacy CODESYS Forums, please create a new account at account.codesys.com. But make sure to use the same E-Mail address as in the old Forum. Then your posts will be matched. Close

Order of synchronisation object creation

2017-11-11
2017-11-19
  • schnasseldag - 2017-11-11

    Hello all,

    While implementing some thread save FB's for accessing a cascadable Raspberry Pi IO board I came across a general question about the order of creating tasks in contrast to the order of the creation of semaphores being used by these tasks.

    In the embedded world or any other kind of "have a proper run-level boot-up" system I'd first create my synchronisation objects and thereafter the threads using them. At least I would take care for first starting the threads after all synchronisation objects (mutexes, semaphores, queues) are in place and are initialized.

    However, tasks being created in the IDE of CODESYS do seem to be initialized and started by the runtime with no further controlling mechanism from within the (STL) user code being executed. Thus tasks are being created prior to the synchronisation objects which they are supposed to be using. Well, one could think of using some singleton in order to create all these sync objects during the first task cycle. However, not knowing about the thread order to be executed this code would have to rather be located in a globally used object (thus not ideal when trying to encapsulating synchronisation objects as static members of a class e.g. when writing some library code). But even then we encounter a problem. Let's take a look at the code below which creates a semaphore.

    IF hMutex = RTS_INVALID_HANDLE THEN
       hMutex := SysSem.SysSemCreate(ADR(mutexCreateResult));
    END_IF
    // From now on use the mutex.
    

    This code (being executed by several threads) is not 100% thread safe. Why's that? Simply because it relies on the fact the assignment to hMutex itself would be an atomic operation. But strictly speaking this is not ensured! The handle is implemented as a 4Byte value and there is no saying that the assignment is an atomic operation. It is a matter of the compiler how to deal with it.

    One could now think of enclosing the assignment within another variable of type BOOL which suggests that it can be read and written to in an atomic operation - however, this can also fail as for code reordering of the compiler if there is no way of establishing something like a memory barrier.

    So the ultimate question is - how can sync objects be created prior to tasks running? Or otherwise - is there a way in the runtime to inject code during the initialization of the application?

    Regards,

    schnasseldag

     
  • Anonymous - 2017-11-13

    Originally created by: rickj

    After download, variable initialization is accomplished by execution of initialization code, unlike other systems where the compiler performs the operations and places te results in the appropriate memory locations. Consequently variables can be initialized by executing functions and retrkeving their return values.

    You may also want to look into the built-in methods/events FB_init, FB_reinit, FB_exit, etc. If the FB_init mehod is defined, then itis executed prior to the application code.

     
  • schnasseldag - 2017-11-14

    Hi rickj,

    Thanks for your reply. In fact I had a similar thought on initialization and tried initializing my "hMutex" by a call to a "make-sure-Singleton-function" of my own but got a "welcome" by an assertion of the runtime. However, I then checked initialisation of that variable via a call to a system-library function and - that one survived but for other reason giving a compiler hint to an attribute ‘global_init_slot’. I guess that and some other attributes I found might push me into the right direction. Previously I checked the "pseudo-Constructors" FB-Init... for usability but didn't see a way to succeed a singleton implementation this way. In fact the initialization does not seem to follow the C++ "constructor way" and that's why I'm doing a little hard to follow that philosophy. It just feels "uncomfortably" having an FB_Init got called while constants are not being initialized previously. It feels strange to where I'm coming from but the guys from 3S have done a really really great job with their software - just maybe hiding some documentation from being "obvious".
    In fact your idea lead me looking into the Attributes ‘global_init_slot’ and ‘call_after_init’ which may be a solution to my problem. As stated before - I'm trying to avoid I2C bus collisions for a class that can have multiple instantiations all sharing a single mutex resource.

    Take care and many thanks!

    schnasseldag

     
  • schnasseldag - 2017-11-19

    Hi rickj,

    After some reading of the online help and some testing around the initialization process - the runtime initialization seems to follow this order:
    - VAR_STAT
    - FB_Init
    - VAR
    - "call_after_init" marked functions
    - tasks

    So for my purpose of a singleton a VAR_STAT hMutex is just the right thing in order to prevent bus collisions from I2C and SPI access. The assertion coming up previously was my fault as of the this-pointer not being initialized during FB_Init() while accessing members of the class.

    Still I'm wondering where I could read of a "consolidated documentation describing the initialization of the runtime". It's somewhat spread in the help. Looking for "global_init_slot" I do for example not now where it lines in the above list. I'd guess inbetween FB_Init and VAR. However, that wouldn't answer the question about potential overlapping when using ranges below 49990 (GVL) and beyond 50000 (POU)...

    Thanks again and happy hacking...

    schnasseldag

     

Log in to post a comment.