September 23, 2021

On Thursday, 23 September 2021 at 13:30:42 UTC, eugene wrote:

>

So, in C it is MY (potentially wrong) code.
In D, it is NOT MY code, it is GC.

Actually in both cases it is MY+the compiler's code. A very similar example from C-land (without my digging up the exact details) is something like

for (int i = 0; i >= 0; i++) {
  // exit loop on signed integer overflow
}

where gcc 2.95 would do what "MY code" said, but later gcc versions would 'optimize' into an infinite loop (followed by dead code that can now be removed):

for (;;) {
  // never exit loop
}

Because in math, positive numbers never +1 into negative numbers. And in C this is undefined behavior which is (modern understanding:) complete license for the compiler to do anything at all. And on the specific architecture we are specifically compiling for there is specific behavior--but who cares about that, this is optimization! And if you complained about it, well you were a sloppy coder actually, for wanting the target architecture's actual behavior with your actual code as you actually wrote it. (If you feel like defending C's honor here, please, I've heard it already. Everybody thinks very highly of the nasal demons joke.)

There are other cases where very security-minded software had defensive code that an optimizer decided would never be needed, that then exposed a software vulnerability, or there are 'unnecessary' writes that are intended to remove a password from memory:

https://duckduckgo.com/?q=dead+code+elimination+security+vulnerability

September 23, 2021

On Thursday, 23 September 2021 at 14:00:30 UTC, eugene wrote:

>

For the moment I am personally quite happy

void main(string[] args) {

    import core.memory : GC;

    auto Main = new Main();
    GC.addRoot(cast(void*)Main);
    Main.run();

    auto stopper = new Stopper();
    GC.addRoot(cast(void*)stopper);
    stopper.run();

Fine, works!

September 23, 2021

On Thursday, 23 September 2021 at 14:23:40 UTC, eugene wrote:

>

On Thursday, 23 September 2021 at 14:00:30 UTC, eugene wrote:

>

For the moment I am personally quite happy

void main(string[] args) {

    import core.memory : GC;

    auto Main = new Main();
    GC.addRoot(cast(void*)Main);
    Main.run();

    auto stopper = new Stopper();
    GC.addRoot(cast(void*)stopper);
    stopper.run();

Fine, works!

Nice. I thought of GC.addRoot several times but I was distracted by the general solution of using object lifetimes with it, so that a struct's destructor would call GC.removeRoot. For your case just pinning these and forgetting about them is the easiest way to do it.

September 23, 2021

On Monday, 13 September 2021 at 17:18:30 UTC, eugene wrote:

>

And the most strange thing is this - if using gdc with -Os flag, the program behaves
exactly as when compiled with fresh dmd - destructors for sg0 and sg1 are called soon after program start.

Now I guess, gdc optimization by size imply DSE.

September 23, 2021

On Thursday, 23 September 2021 at 14:31:34 UTC, jfondren wrote:

>

Nice. I thought of GC.addRoot several times but I was distracted by the general solution of using object lifetimes with it, so that a struct's destructor would call GC.removeRoot. For your case just pinning these and forgetting about them is the easiest way to do it.

Yes, these two must live until the end of main().
Moreover, in real (C) programs I (usually) do
not create state machines on the fly,
instead I keep them in pools, like RX/TX machines pools
in echo-server and in echo-client.

September 23, 2021

On 9/23/21 10:55 AM, eugene wrote:

>

On Thursday, 23 September 2021 at 14:31:34 UTC, jfondren wrote:

>

Nice. I thought of GC.addRoot several times but I was distracted by the general solution of using object lifetimes with it, so that a struct's destructor would call GC.removeRoot. For your case just pinning these and forgetting about them is the easiest way to do it.

Yes, these two must live until the end of main().
Moreover, in real (C) programs I (usually) do
not create state machines on the fly,
instead I keep them in pools, like RX/TX machines pools
in echo-server and in echo-client.

Technically, they should live past the end of main, because it's still possible to receive signals then.

But the chances of someone hitting ctrl-c in that window are quite small.

-Steve

September 23, 2021

On 9/23/21 9:18 AM, eugene wrote:

>

On Thursday, 23 September 2021 at 12:53:14 UTC, Steven Schveighoffer wrote:

> >

We need to add a better way to do that (similar to C# KeepAlive).

Do you mean some function attribute?..

C# KeepAlive (and Go KeepAlive) are a mechanism to do exactly what you suggested -- use the object later.

However, they are recognized by the compiler as an intrinsic which generates no code or side effects, but is not subject to elimination by the optimizer.

See more details:

https://docs.microsoft.com/en-us/dotnet/api/system.gc.keepalive?view=net-5.0#remarks

-Steve

September 23, 2021

On Thursday, 23 September 2021 at 15:53:37 UTC, Steven Schveighoffer wrote:

>

Technically, they should live past the end of main, because it's still possible to receive signals then.

No, as soon as an application get SIGTERM/SIGINT,
event queue is stopped and we do not need no
more notifications from OS (POLLIN/POLLOUT I mean).

Stopping event queue in this case is just
closing file descriptor obtained from epoll_create().
After this getting POLLIN from any fd (including signal fd) is
just impossible.

September 23, 2021

On Thursday, 23 September 2021 at 15:56:16 UTC, Steven Schveighoffer wrote:

>

See more details:

https://docs.microsoft.com/en-us/dotnet/api/system.gc.keepalive?view=net-5.0#remarks

"
This method references the obj parameter, making that object ineligible for garbage collection from the start of the routine to the point, in execution order, where this method is called. Code this method at the end, not the beginning, of the range of instructions where obj must be available.
"

Code this method at the end...

:)
it is the same as proposed by jfondren simple
writeln(stopper.sg0.number) in the end of main, right?

September 23, 2021

On 9/23/21 12:58 PM, eugene wrote:

>

On Thursday, 23 September 2021 at 15:56:16 UTC, Steven Schveighoffer wrote:

>

See more details:

https://docs.microsoft.com/en-us/dotnet/api/system.gc.keepalive?view=net-5.0#remarks

"
This method references the obj parameter, making that object ineligible for garbage collection from the start of the routine to the point, in execution order, where this method is called. Code this method at the end, not the beginning, of the range of instructions where obj must be available.
"

Code this method at the end...

:)
it is the same as proposed by jfondren simple
writeln(stopper.sg0.number) in the end of main, right?

Same effect, but writeln actually executes code to write data to the console, whereas KeepAlive doesn't do anything.

Essentially, you get the side effect of keeping the object as live, without paying the penalty of inserting frivolous code.

All my efforts to achieve the same via a library were thwarted by at least LDC (whose optimizer is very good).

The only possible solution I can think of is to generate an opaque function that LDC cannot see into, in order to force it to avoid inlining, and have that function do nothing.

However, there's always Link-Time-Optmization...

-Steve