Spawning threads in SystemVerilog with input variables
To help me explain what is happening, I have copied the relevant portion of your code. There is parent thread which is running and which hits this fork .. join_none. When an SV thread encounters a fork, it registers the enclosing begin..end blocks and sequential statements (like the get_output function call here) as threads with the scheduler. But the scheduler does not spawn these threads immediately. Actually it can not -- until the scheduler gets the execution control. At this point the parent thread is executing. The scheduler has to wait till the parent thread yields. The parent thread will yield on encountering any kind of wait statement (or a blocking tlm port read/write etc).

Had you used the fork paired with join or join_any, the parent thread would have yielded immediately. But with join_none, it does not.

So the parent thread continues with execution and only when it hits #1 (if uncommented) it yields, thereby giving a chance to the scheduler to actually spawn the forked thread. And with #1 commented out, index gets incremented before the forked thread gets a chance to get executed. SystemVerilog does provide you with a solution. It involves using the declarative region of the fork construct. See the 2nd code snippet at the bottom.

//spawn a task to perform that and get back to channel listening again
fork
  get_output(data_struct, index);
join_none
$display("Value of index after calling the task is:  %d", index);
//#1;
index++;


With the following code, the declaration ''automatic int _index = index;'' is executed immediately -- along with the registration of the forked thread. For reference see section 9.3.2 of SV 1800-2012 standard. You can omit the enveloping begin and end from the following code, but I think they add clarity to the code.

fork
  automatic int _index = index;
  begin
    get_output(data_struct, _index);
  end
join_none