Evil fork join any bug

Has anyone else stubbed their toe on this?

    fork
      begin : thread1;
        #100;
        // do thread1 stuff
      end : thread1;

      //Process-2
      begin : thread2;
        #200;
        // do thread1 stuff
      end : thread2;
    join_any

    $display($time,"Done with fork join any");

The above code finishes the join_any at time zero. See if one of you smarties spots why.

Spoiler :: It would really be great if there was a warning or something on an empty statement in a fork join any! I felt like I was being punished for being HDL bi-lingual. It’s probably not a mistake you make if you only do verilog.

Here, the System Verilog code uses a fork join_any construct, but it completes at time 0 because the thread1 block contains an empty statement (;) after its label, causing it to finish immediately. The thread2 block, while scheduled to run for 200 time units, doesn’t affect the join_any since it waits for the first thread to complete, which is thread1 at time 0. This issue arises from a subtle syntax error—System Verilog allows an empty statement after a named block.

Hi @Amoch,

In SystemVerilog, the fork-join_any blocks the parent thread until any one of the spawned thread/process completes. As soon as any thread/process completes, the parent thread will continue execution following the fork-block.

In your case, two threads are spawned and having a delay statement. Along with this, you have named the thread by putting a label for begin-end blocks. I tried two variations of your code as follows along with the result and analysis:
1st Case:

fork
  begin: thread1;
    #100;
  end
  begin: thread2;
    #200;
  end
join_any
$display("%0t: Done with fork join any", $time);

Output:

The output as expected, the parent thread continues as soon as "thread1" is completed because it is having less delay than "thread2", and executes $display present after the fork-join_any.

100: Done with fork join any 

==============================
2nd Case:

fork
  begin: thread1;
    #100;
  end : thread1;
  begin: thread2;
    #200;
  end // No label at the end
join_any
$display("%0t: Done with fork join any", $time);

Output:

In this case, the output is not as expected. I think the label used at the end of 1st begin-end block is creating a problem and considering a "null" statement after the semicolon and fork-join_any completes without running actual thread. 

0: Done with fork join any 

Let me know if it is correct.

Thanks,
Naveen Kadian

Yes you guys got it :slight_smile:

The evil bug is more obvious if written like

    fork
      begin
       ; // null statement
        #100;
        // do thread1 stuff
      end
       ; // null statement - Hidden thread 3

      //Process-2
      begin : thread2
      ; //null statement
        #200;
        // do thread1 stuff
      end : thread2
      ; //null statement - Hidden thread 4
    join_any

    $display($time,"Done with fork join any");

System Verilog doesn’t require a ; after begin, end. So those just turn into empty statements, which cause the fork join_any to complete immediately.

A good linter might point out that it is a null statement.

As I stated, this can be a hazard if you are hopping between languages.