wait fork lets the current process wait until every process it has forked is over — the opposite of disable fork, which terminates them instead. This is useful when a process needs to spawn multiple threads, do some other work in the meantime, and only later pause to make sure all of that spawned work has actually finished.

7 min read | Intermediate Level

What You'll Learn

  • What wait fork does, and how it differs from disable fork
  • How wait fork behaves after a fork...join_any leaves some threads still running
  • Why wait fork waits for every immediate child process of the current process, not just the most recent fork block
  • When to reach for wait fork instead of disable fork

A Basic wait fork Example

This reuses the same three-thread setup from the disable fork article — three threads are kicked off in parallel with fork...join_any, and the main process resumes as soon as any one of them finishes. This time, instead of disabling the remaining threads, the main process waits for all of them to finish with wait fork:


module tb_top;
  
	initial begin
		// Fork off 3 sub-threads in parallel and the currently executing main thread
		// will finish when any of the 3 sub-threads have finished.
		fork
  
			// Thread1 : Will finish first at time 40ns
			#40 $display ("[%0t ns] Show #40 $display statement", $time);  
  
			// Thread2 : Will finish at time 70ns
			begin
				#20 $display ("[%0t ns] Show #20 $display statement", $time);
				#50 $display ("[%0t ns] Show #50 $display statement", $time);
			end
  
			// Thread3 : Will finish at time 60ns
			#60 $display ("[%0t ns] TIMEOUT", $time);
		join_any
  
		// Display as soon as the fork is done
      	$display ("[%0t ns] Fork join is done, wait fork to end", $time);  
  
      	// Wait until all forked processes are over and display
      	wait fork;
      	$display ("[%0t ns] Fork join is over", $time);
   end
endmodule

Output

Output
ncsim> run
[20 ns] Show #20 $display statement
[40 ns] Show #40 $display statement
[40 ns] Fork join is done, wait fork to end
[60 ns] TIMEOUT
[70 ns] Show #50 $display statement
[70 ns] Fork join is over
ncsim: *W,RNQUIE: Simulation is complete.

The main process resumes at 40ns, same as before, right after Thread1 finishes. But this time, instead of moving straight on, wait fork blocks until the two remaining threads — Thread3 (60ns) and Thread2 (70ns) — both finish. "Fork join is over" only prints at 70ns, once the slowest of the two remaining threads completes.

Quick Check

Q: If Thread2's total delay above were shortened so it finished at 55ns instead of 70ns, at what time would "Fork join is over" print?

Show Answer

60ns. wait fork waits for every remaining thread, so it resolves once the last one finishes — with Thread2 now finishing at 55ns and Thread3 still at 60ns, Thread3 becomes the slower of the two.

Does wait fork Wait for Every Process?

To see the full scope of wait fork, this example forks two more processes after the first join_any, before calling wait fork:


module tb_top;
  
	initial begin
		// Fork off 3 sub-threads in parallel and the currently executing main thread
		// will finish when any of the 3 sub-threads have finished.
		fork
  
			// Thread1 : Will finish first at time 40ns
			#40 $display ("[%0t ns] Show #40 $display statement", $time);  
  
			// Thread2 : Will finish at time 70ns
			begin
				#20 $display ("[%0t ns] Show #20 $display statement", $time);

				#50 $display ("[%0t ns] Show #50 $display statement", $time);
			end
  
			// Thread3 : Will finish at time 60ns
			#60 $display ("[%0t ns] TIMEOUT", $time);
		join_any

      	// Display as soon as the fork is done
      	$display ("[%0t ns] Fork join is done, wait fork to end", $time);
  
      	// Fork two more processes
      	fork
          #10 $display ("[%0t ns] Wait for 10", $time);
          #20 $display ("[%0t ns] Wait for 20", $time);
        join_any
  
      	// Wait until ALL forked processes are over
      	wait fork;
        $display ("[%0t ns] Fork join is over", $time);
   end
endmodule

Output

Output
ncsim> run
[20 ns] Show #20 $display statement
[40 ns] Show #40 $display statement
[40 ns] Fork join is done, wait fork to end
[50 ns] Wait for 10
[60 ns] TIMEOUT
[60 ns] Wait for 20
[70 ns] Show #50 $display statement
[70 ns] Fork join is over
ncsim: *W,RNQUIE: Simulation is complete.

At 40ns, a second fork...join_any spawns two more threads (finishing 10ns and 20ns later, so at 50ns and 60ns respectively), and resumes as soon as the faster one finishes — "Wait for 10" at 50ns. At that point, wait fork is called. Notice it doesn't just wait for the second fork's leftover thread ("Wait for 20," at 60ns) — it also waits for Thread2 and Thread3, still pending from the first fork block, all the way back at the top of the process. "Fork join is over" only prints at 70ns, once every one of those remaining threads — from both fork blocks — has finished.

wait fork covers every immediate child process of the current process, regardless of which fork statement spawned them — not just the most recent one. That's narrower than disable fork, though: disable fork reaches an entire descendant tree (children, their children, and so on), while wait fork only waits on the current process's direct children, not on anything those children themselves go on to fork.

Where Is wait fork Used?

wait fork is the right tool when you need results from every spawned thread rather than discarding whatever hasn't finished — for example, running several independent checkers in parallel and needing every one of them to actually complete and report before deciding whether a test passed, instead of cutting any of them off early with disable fork.

Common Beginner Mistakes

Mistake #1: Confusing wait fork with disable fork

One waits for spawned processes to finish; the other terminates them. Why? Using disable fork when you actually needed the results of a still-running thread throws that work away, while using wait fork when you meant to abandon a stuck thread can stall the process indefinitely instead.

Mistake #2: Assuming wait fork only covers the most recent fork block

As the second example shows, wait fork waits for every immediate child process of the current process — including threads left over from an earlier, unrelated fork statement. Why? This can make wait fork take much longer than expected if older background threads from earlier in the same process are still running.

Mistake #3: Not accounting for how long wait fork might actually block

Code right after wait fork doesn't execute until every remaining immediate child process finishes, however long that takes. Why? If one of those background threads is a long-running or even indefinite task, wait fork will block for just as long — it's not a quick synchronization checkpoint unless you already know every spawned thread finishes promptly.

Practice Exercise

Challenge: Predict the Output

Using the basic wait fork example from earlier, suppose Thread3's delay were changed from #60 to #80. At what time would "Fork join is over" print, and which thread would be the last one wait fork is waiting on?

Hint

The main process still resumes at 40ns when Thread1 finishes first. From there, work out each remaining thread's own finish time and take the latest one.

Solution

80ns, waiting on Thread3. With Thread3 now finishing at 80ns instead of 60ns, it becomes the slowest of the two remaining threads (Thread2 still finishes at 70ns), so wait fork now resolves at 80ns instead of 70ns.

Summary

In this tutorial, you learned:

  • wait fork blocks the current process until every process it has forked has finished, instead of terminating them like disable fork does
  • After a fork...join_any leaves threads running, wait fork resolves once the slowest of the remaining threads finishes
  • wait fork's scope covers every immediate child process of the current process, not just the most recently forked block — though unlike disable fork, it doesn't reach any further than that process's direct children
  • Use wait fork when you need the results or side effects of every spawned thread; use disable fork when you need to abandon whatever's still running
  • wait fork can block for as long as its slowest remaining thread takes, including an indefinitely long one

Continuing to Interprocess Communication

With threads and processes covered, continue with SystemVerilog Interprocess Communication to start the next chapter — covering events, semaphores, and mailboxes for coordinating concurrent processes safely.

Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5