kotlin-native/runtime/src/legacymm/cpp/Memory.cpp
void freezeSubgraph(ObjHeader* root) {
  if (root == nullptr) return;
  // First check that passed object graph has no cycles.
  // If there are cycles - run graph condensation on cyclic graphs using Kosoraju-Sharir.
  ContainerHeader* rootContainer = containerFor(root);
  if (isPermanentOrFrozen(rootContainer)) return;

  MEMORY_LOG("Run freeze hooks on subgraph of %p\n", root);

  // Note: Actual freezing can fail, but these hooks won't be undone, and moreover
  // these hooks will run again on a repeated freezing attempt.
  runFreezeHooksRecursive(root);

  MEMORY_LOG("Freeze subgraph of %p\n", root)

  #if USE_GC
    auto state = memoryState;
    // Free cyclic garbage to decrease number of analyzed objects.
    checkIfForceCyclicGcNeeded(state);
  #endif

  // Do DFS cycle detection.
  bool hasCycles = false;
  KRef firstBlocker = root->has_meta_object() && ((root->meta_object()->flags_ & MF_NEVER_FROZEN) != 0) ?
    root : nullptr;
  KStdVector<ContainerHeader*> order;
  depthFirstTraversal(rootContainer, &hasCycles, &firstBlocker, &order);
  if (firstBlocker != nullptr) {
    MEMORY_LOG("See freeze blocker for %p: %p\n", root, firstBlocker)
    ThrowFreezingException(root, firstBlocker);
  }
  ContainerHeaderSet newlyFrozen;
  // Now unmark all marked objects, and freeze them, if no cycles detected.
  if (hasCycles) {
    freezeCyclic(root, order, &newlyFrozen);
  } else {
    freezeAcyclic(rootContainer, &newlyFrozen);
  }
  MEMORY_LOG("Graph of %p is %s with %d elements\n", root, hasCycles ? "cyclic" : "acyclic", newlyFrozen.size())

#if USE_GC
  // Now remove frozen objects from the toFree list.
  // TODO: optimize it by keeping ignored (i.e. freshly frozen) objects in the set,
  // and use it when analyzing toFree during collection.
  for (auto& container : *(state->toFree)) {
    if (!isMarkedAsRemoved(container) && container->frozen()) {
      RuntimeAssert(newlyFrozen.count(container) != 0, "Must be newly frozen");
      container = markAsRemoved(container);
    }
  }
#endif
}
/**
 * Theory of operations.
 *
 * Kotlin/Native supports object graph freezing, allowing to make certain subgraph immutable and thus
 * suitable for safe sharing amongst multiple concurrent executors. This operation recursively operates
 * on all objects reachable from the given object, and marks them as frozen. In frozen state object's
 * fields cannot be modified, and so, lifetime of frozen objects correlates. Practically, it means
 * that lifetimes of all strongly connected components are fully controlled by incoming reference
 * counters, and so if we place all members of strongly connected component to the single container
 * it could be correctly released by just atomic decrement on reference counter, without additional
 * cycle collector run.
 * So during subgraph freezing operation, we perform the following steps:
 *   - run Kosoraju-Sharir algorithm to find strongly connected components
 *   - put all objects in each strongly connected component into an artificial container
 *     (we assume that they all were in single element containers initially), single-object
 *     components remain in the same container
 *   - artificial container sums up outer reference counters of all its objects (i.e.
 *     incoming references from the same strongly connected component are not counted)
 *   - mark all object's headers as frozen
 *
 *  Further reference counting on frozen objects is performed with atomic operations, and so frozen
 * references could be passed across multiple threads.
 */
Theory of operations.
Kotlin/Native supports object graph freezing, allowing to make certain subgraph immutable and thus
suitable for safe sharing amongst multiple concurrent executors. This operation recursively operates
on all objects reachable from the given object, and marks them as frozen. In frozen state object's
fields cannot be modified, and so, lifetime of frozen objects correlates. Practically, it means
that lifetimes of all strongly connected components are fully controlled by incoming reference
counters, and so if we place all members of strongly connected component to the single container
it could be correctly released by just atomic decrement on reference counter, without additional
cycle collector run.

Further reference counting on frozen objects is performed with atomic operations, and so frozen
references could be passed across multiple threads.
Theory of operations . Kotlin / Native supports object graph freezing allowing to make certain subgraph immutable and thus suitable for safe sharing amongst multiple concurrent executors . This operation recursively operates on all objects reachable from the given object and marks them as frozen . In frozen state object ' s fields cannot be modified and so lifetime of frozen objects correlates . Practically it means that lifetimes of all strongly connected components are fully controlled by incoming reference counters and so if we place all members of strongly connected component to the single container it could be correctly released by just atomic decrement on reference counter without additional cycle collector run . Further reference counting on frozen objects is performed with atomic operations and so frozen references could be passed across multiple threads .
void freezeSubgraph(ObjHeader* root) {
  if (root == nullptr) return;
  ContainerHeader* rootContainer = containerFor(root);
  if (isPermanentOrFrozen(rootContainer)) return;
  MEMORY_LOG("Run freeze hooks on subgraph of %p\n", root);
  runFreezeHooksRecursive(root);
  MEMORY_LOG("Freeze subgraph of %p\n", root)
  #if USE_GC
    auto state = memoryState;
    checkIfForceCyclicGcNeeded(state);
  #endif
  bool hasCycles = false;
  KRef firstBlocker = root->has_meta_object() && ((root->meta_object()->flags_ & MF_NEVER_FROZEN) != 0) ?
    root : nullptr;
  KStdVector<ContainerHeader*> order;
  depthFirstTraversal(rootContainer, &hasCycles, &firstBlocker, &order);
  if (firstBlocker != nullptr) {
    MEMORY_LOG("See freeze blocker for %p: %p\n", root, firstBlocker)
    ThrowFreezingException(root, firstBlocker);
  }
  ContainerHeaderSet newlyFrozen;
  if (hasCycles) {
    freezeCyclic(root, order, &newlyFrozen);
  } else {
    freezeAcyclic(rootContainer, &newlyFrozen);
  }
  MEMORY_LOG("Graph of %p is %s with %d elements\n", root, hasCycles ? "cyclic" : "acyclic", newlyFrozen.size())
#if USE_GC
  for (auto& container : *(state->toFree)) {
    if (!isMarkedAsRemoved(container) && container->frozen()) {
      RuntimeAssert(newlyFrozen.count(container) != 0, "Must be newly frozen");
      container = markAsRemoved(container);
    }
  }
#endif
}
void freezeSubgraph ( ObjHeader * root ) { if ( root == nullptr ) return ; ContainerHeader * rootContainer = containerFor ( root ) ; if ( isPermanentOrFrozen ( rootContainer ) ) return ; MEMORY_LOG ( " \n " , root ) ; runFreezeHooksRecursive ( root ) ; MEMORY_LOG ( " \n " , root ) #if USE_GC auto state = memoryState ; checkIfForceCyclicGcNeeded ( state ) ; #endif bool hasCycles = false ; KRef firstBlocker = root -> has_meta_object ( ) && ( ( root -> meta_object ( ) -> flags_ & MF_NEVER_FROZEN ) != 0 ) ? root : nullptr ; KStdVector < ContainerHeader * > order ; depthFirstTraversal ( rootContainer , & hasCycles , & firstBlocker , & order ) ; if ( firstBlocker != nullptr ) { MEMORY_LOG ( " \n " , root , firstBlocker ) ThrowFreezingException ( root , firstBlocker ) ; } ContainerHeaderSet newlyFrozen ; if ( hasCycles ) { freezeCyclic ( root , order , & newlyFrozen ) ; } else { freezeAcyclic ( rootContainer , & newlyFrozen ) ; } MEMORY_LOG ( " \n " , root , hasCycles ? " " : " " , newlyFrozen . size ( ) ) #if USE_GC for ( auto & container : * ( state -> toFree ) ) { if ( ! isMarkedAsRemoved ( container ) && container -> frozen ( ) ) { RuntimeAssert ( newlyFrozen . count ( container ) != 0 , " " ) ; container = markAsRemoved ( container ) ; } } #endif }
Theory of operations.
Theory of operations .
// First check that passed object graph has no cycles. // If there are cycles - run graph condensation on cyclic graphs using Kosoraju-Sharir. // Note: Actual freezing can fail, but these hooks won't be undone, and moreover // these hooks will run again on a repeated freezing attempt. // Free cyclic garbage to decrease number of analyzed objects. // Do DFS cycle detection. // Now unmark all marked objects, and freeze them, if no cycles detected. // Now remove frozen objects from the toFree list. // TODO: optimize it by keeping ignored (i.e. freshly frozen) objects in the set, // and use it when analyzing toFree during collection.