Object Deallocation in SystemVerilog

Hi @dave_59,

In below code, why object has deallocated when assigned with special value null when threads spawned by fork-join_none are still running?

Could you please tell if the following statements are true as per SystemVerilog:

  1. SystemVerilog can’t garbage collects an object that is still referenced by a handle.
  2. An object can’t be deallocated that is used by a spawned thread and threads are still running. - Applicable in my case.
  3. Setting a handle to special value null doesn’t guarantee immediate deallocation because the garbage collection process will still check for any active references before an object deallocation.

Code:

program automatic top;
  class ObjectDeallocation;
     int a;
     function new();
        a = 10;
     endfunction
  endclass

  task insert_wait(ObjectDeallocation obj);
     fork
       #10 $display("%0t: THREAD1 Completed", $time);
       #20 $display("%0t: THREAD2 Completed", $time);
     join_none
     $display("%0t: TASK Completed", $time);
  endtask

  initial begin
    ObjectDeallocation obj;
    obj = new();
    fork
       insert_wait(obj);
       #1 insert_wait(obj);
    join_none
    obj = null;
    $display("%0t: After object deallocation", $time);
  end
endprogram

Expected Output: I am expecting either Output_1 or Output_2
Output_1:

0: TASK DONE (From 1st thread spwaned by fork-join_none in begin-end)
1: TASK DONE (From 2nd thread spwaned by fork-join_none in begin-end)
10: THREAD1 Completed (from 1st thread ...)
11: THREAD1 Completed (from 2nd thread ...)
20: THREAD2 Completed (from 1st thread ...)
21: THREAD2 Completed (from 2nd thread ...)
(once all threads are completed/terminated, object deallocation happens now)
21: After object deallocation

Output_2:

Simulation should give an error when it encounters object deallocation statement i.e. obj = null;  because threads are still running and code is trying to deallocate an object forcefully while still active references for an object are present.

Actual Output:

0: After object deallocation

Thanks,
Naveen Kadian

The reason your code does not work as expected is an example of why I strongly recommend against using program blocks. Simulation ends when the thread started by the initial construct in a program block terminates. It does not wait for any forked threads to terminate. Use a module instead. In your example the simulation terminates before any forked thread starts.

An object instance becomes available for deallocation when no active variable holds a handle to that instance. Static variables are always active, while variables with automatic lifetimes are only active during the life of the process they are contained in.

I use the term “available for deallocation” because SystemVerilog doesn’t specify precisely when an object instance gets deallocated. Once no active variable holds a handle to the instance, there’s no way to determine its exact deallocation time.

In your example, you construct an object and store its handle in the static variable obj. Your fork/join_none construct schedules 2 processes, but they do not start executing before assigning obj = null;. So a null value gets passed to the insert_wait obj task argument. If you were to add a #5 to that assignment, then the object instance handle gets copied to the obj argument. The handle remains in that argument until the tasks complete.

I don’t think Output_2 is possible. In fact, it doesn’t make any sense. The garbage collector is designed to deallocate the object only if there is no handle pointing to it.

If there is no handle pointing to the object, then there will never be. How can a handle suddenly point to it when there have been no handles already? The only situation the number of handles turning from 0 to 1 is when the object gets created using new().

Thanks @dave_59. Now its very clear why the output was that way with program block. By using module, code is giving output as expected.

Thanks,
Naveen Kadian