micro-os-plus / micro-os-plus-iii

The portable part of µOS++ IIIe (an xpm/npm package)
http://micro-os-plus.github.io
MIT License
115 stars 17 forks source link

calling os routines before scheduler is started #64

Open LnnrtS opened 3 years ago

LnnrtS commented 3 years ago

What is the expected behavior when calling os routines before the scheduler is started? From my experience, everything related to memory allocation is working correctly by design, some is not working (sysclock.sleep_for()) and some might just be working by coincidence (this_thread, interaction with mutex). Can you shed some light on this?

This has practical relevance because there are multiples ways how application code is able to call os routines before the scheduler is started. Some of them are hooks like os_startup_initialize_hardware() but also constructors of objects with static storage duration. In the first case it might be obvious to the user that the code is running under special conditions and it's acceptable it has to comply to certain conventions (not calling certain functions); but not in second case.

My concrete suggestions would be

Looking forward to hearing your thoughts on this

ilg-ul commented 3 years ago

What is the expected behavior when calling os routines before the scheduler is started?

Some will run, some will not ;-)

detect calls to os routines that happen before the scheduler has started

I think that this is an useful suggestion, and some asserts should be added to detect calls that should not happen before the scheduler started

call os_run_init_array() from the main thread

I did not check, but the C/C++ standard might require for the static constructors to be called before entering main().

Anyway, having constructors with a complex functional logic (either system calls or touching the hardware) is not a good idea, and should be avoided.

ilg-ul commented 3 years ago

BTW, if I remember right, there might be some calls that switch to a limited functionality when running without the scheduler.

And, if there are not, there should be. In the new version of the project template it is possible to create projects without the scheduler, but there are many calls, even timer.sleep() calls, that do not depend on the scheduler, and can run very well.

The conclusion is that the entire system will need a thorough check and functions that can run without a scheduler, should run, and functions that should not be called without a scheduler should throw an assert.

LnnrtS commented 3 years ago

but the C/C++ standard might require for the static constructors to be called before entering main()

Why not have main also run from a thread?
If os_main was just named main (and main renamed to something else) that seems like already solved.

Anyway, having constructors with a complex functional logic (either system calls or touching the hardware) is not a good idea, and should be avoided.

I wouldn't say that. For example std::unique_lock performs a system call in its constructor.

ilg-ul commented 3 years ago

For example std::unique_lock performs a system call in its constructor

What call?

LnnrtS commented 3 years ago

It locks the mutex you pass it which in turn interacts with the os.
(I would say that is a system call but maybe we are talking about different things then)

ilg-ul commented 3 years ago

Does it work when called from a static constructor on Linux?

ilg-ul commented 3 years ago

And related: on Linux, is main() running from a thread?

LnnrtS commented 3 years ago

Honestly I don't know that.

But my understanding is that everything in userspace is 'running from a thread'. When you call a c++ program, some thread context is created, execution starts from the entry point of the executable which directly calls into libc and libc++ which in turn calls init functions and finally calls main. At every point you should be able make system calls because its not the system that gets bootstrapped but the c++ environment. In our case its more complicated because system and c++ bootstrapping are mixed together.

ilg-ul commented 3 years ago

If on Linux the static constructors also run from a thread, then we should investigate this route too.

ilg-ul commented 3 years ago

I did some reading and indeed, initialising static code in multithreaded environments seem a tough subject in C++.

There is also a good article on singletons:

Moving the static constructors initialisations on the main thread probably is not that difficult, but protecting static initialisations in a function which may be called from two threads needs some attention.

We probably have to revisit some parts of the system.

LnnrtS commented 3 years ago

That's an interesting paper on an aspect I haven't thought about much before.

But I would say its is outdated. C++11 added general thread awareness to language, so as stated in the reference initialization of static local statics is guaranteed to be thread safe - Unless you don't disable it in the compiler (-fno-threadsafe-statics in gcc)

ilg-ul commented 3 years ago

static local statics is guaranteed to be thread safe

Well it is guaranteed as long as the runtime implements correctly the guard functions, which in µOS++ is not done, and, if I remember right, my projects disable this feature, to avoid other issues.

Definitely something to improve.

ilg-ul commented 3 years ago

For reference, here is one of the glibc startup files: