Fork join_none with $display and #0 $display

Hi Everyone,
I am trying to understand the below code w.r.t to $display. I know below code will result in 3 3 3 as output. My question is if I remove #0(before the $display) nothing gets printed. Why is this so? It should be related to systemverilog scheduler regions, but I am not clear why this is happening exactly.

program no_auto;
  initial begin 
    for(int j =0;j<3; j++)
      fork
        $write(j);
      join_none
    #0 $display;
    
  end
endprogram



Instead of using #0 display, we can use wait fork as well, right ?

The processes created by a fork/join_none start when the parent process suspends or terminates. Your problem is you used a program instead of a module. Simulation finishes immediately when the initial process of a program terminates.

We strongly recommend against using the program block in SystemVerilog.

I understood without #0 the simulation ends immediately. However, how adding a zero time delay helps in holding the simulation?

The initial block in the program starts in the re-active region. #0 suspends the parent process (the initial block process) into the re-inactive event region. Then the forked process start and terminate in the re-active region.

When the re-active region is empty, the simulation can move to the re-inactive region. This schedules the parent process to resume in the re-active region. Then the $display executes and the process terminates, ending the simulation.

If you do this in a module, you can get rid of my “re-” prefixes and every works the same in the active and inactive regions. But you won’t need the #0 because the simulation ends when all regions are emptied, or an explicit call to $finish.