Sane C++ Libraries
C++ Platform Abstraction Libraries
SC::ProcessFork Struct Reference

Forks current process exiting child at end of process A fork duplicates a parent process execution state, os handles and private memory. More...

#include <Process.h>

Public Types

enum  Side {
  ForkParent ,
  ForkChild
}
 
enum  State {
  Suspended ,
  Immediate
}
 

Public Member Functions

 ProcessFork (const ProcessFork &)=delete
 
ProcessForkoperator= (const ProcessFork &)=delete
 
Side getSide () const
 Obtain process parent / fork side. More...
 
Result fork (State state)
 Forks current process (use ForkProcess::getType to know the side) More...
 
Result resumeChildFork ()
 Sends 1 byte on parentToFork to resume State::Paused child fork. More...
 
Result waitForChild ()
 Waits for child fork to finish execution. More...
 
int32_t getExitStatus () const
 Gets the return code from the exited child fork. More...
 
FileDescriptorgetWritePipe ()
 Gets the descriptor to "write" something to the other side. More...
 
FileDescriptorgetReadPipe ()
 Gets the descriptor to "read" something from the other side. More...
 

Detailed Description

Forks current process exiting child at end of process A fork duplicates a parent process execution state, os handles and private memory.

Its semantics are quite different from platform to platform but on its most common denominator it can be used to carry on "background" operations on snapshots of current program memory. One relevant use case is serializing to disk or network a live, complex and large data structure. Without the fork the program should either:

  1. Duplicate all the data, to snapshot it in a given instant, and keep it around for Async IO
  2. Block program execution and write the live data-structure until all IO is finished

Fork avoids memory duplication because it will be shared through Copy On Write (COW) mechanisms. COW ensures that un-modified duplicated memory pages will not occupy additional Physical RAM.

A pair of pipes makes it easy to do some coordination between parent and forked process.

Warning
There are really MANY caveats when forking that one should be aware of:
  1. Many API will just not work as expected on the forked process, especially on Windows
  2. Limit API calls in forked process to console IO, network and file I/O (avoid GUI / Graphics)
  3. All threads other than the current one will be suspended in child process (beware of deadlocks)
  4. Create Sockets and FileDescriptors with Inheritable flags if you need them in fork process
  5. Process deadlocks under Windows ARM64 / x86 emulation (use Process::IsWindowsEmulatedProcess)

Example: Fork current process modifying memory in forked process leaving parent's one unmodified.

// Cross-platform lightweight clone of current process, sharing memory
// but keeping any modification after clone "private" (Copy-On-Write).
// Achieved using "fork" on Posix and "RtlCloneUserProcess" on Windows.
StringView sharedTag = "INITIAL";
StringView parentTag = "PARENT";
StringView saveFile = "ForkSaveFile.txt";
// The string will be duplicated using Copy-On-Write (COW)
String shared = sharedTag;
// CLONE current process, starting child fork in Suspended state
// Forked process will be terminated by ProcessFork destructor
ProcessFork fork;
// After fork program must check if it's on fork or parent side
switch (fork.getSide())
{
report.console.printLine("FORKED process");
report.console.print("FORKED Shared={0}\n", shared);
// Write the "shared" memory snapshot to the file system
FileSystem fs;
SC_TEST_EXPECT(fs.init(report.applicationRootDirectory));
SC_TEST_EXPECT(fs.writeString(saveFile, shared.view()));
// Send (as a signal) modified string contents back to Parent
SC_TEST_EXPECT(fork.getWritePipe().write({shared.view().toCharSpan()}));
}
break;
report.console.printLine("PARENT process");
// Check initial state to be "INITIAL" and modify shared = "PARENT"
report.console.print("PARENT Shared={0}\n", shared);
SC_TEST_EXPECT(shared == sharedTag and "PARENT");
shared = parentTag;
// Resume suspended fork verifying that on its side shared == "INITIAL"
SC_TEST_EXPECT(fork.resumeChildFork());
char string[255] = {0};
Span<char> received;
SC_TEST_EXPECT(fork.getReadPipe().read(string, received));
StringView stringFromFork(received, true, StringEncoding::Ascii);
report.console.print("PARENT received={0}\n", stringFromFork);
SC_TEST_EXPECT(stringFromFork == sharedTag);
// Check creation of "save file" by fork and verify its content too
FileSystem fs;
SC_TEST_EXPECT(fs.init(report.applicationRootDirectory));
String savedData;
SC_TEST_EXPECT(fs.read(saveFile, savedData, StringEncoding::Ascii));
SC_TEST_EXPECT(savedData == sharedTag);
SC_TEST_EXPECT(fs.removeFile(saveFile));
// Optionally wait for child process to exit and check its status
SC_TEST_EXPECT(fork.waitForChild());
SC_TEST_EXPECT(fork.getExitStatus() == 0);
}
break;
}
@ Ascii
Encoding is ASCII.
#define SC_TEST_EXPECT(e)
Records a test expectation (eventually aborting or breaking o n failed test)
Definition: Testing.h:116
@ Suspended
Start the forked process suspended (resume it with ProcessFork::resumeChildFork)
Definition: Process.h:412
@ ForkParent
Parent side of the fork.
Definition: Process.h:403
@ ForkChild
Child side of the fork.
Definition: Process.h:404
Result fork(State state)
Forks current process (use ForkProcess::getType to know the side)

Member Enumeration Documentation

◆ Side

Enumerator
ForkParent 

Parent side of the fork.

ForkChild 

Child side of the fork.

◆ State

Enumerator
Suspended 

Start the forked process suspended (resume it with ProcessFork::resumeChildFork)

Immediate 

Start the forked process immediately.

Member Function Documentation

◆ fork()

Result SC::ProcessFork::fork ( State  state)

Forks current process (use ForkProcess::getType to know the side)

◆ getExitStatus()

int32_t SC::ProcessFork::getExitStatus ( ) const
inline

Gets the return code from the exited child fork.

◆ getReadPipe()

FileDescriptor & SC::ProcessFork::getReadPipe ( )

Gets the descriptor to "read" something from the other side.

◆ getSide()

Side SC::ProcessFork::getSide ( ) const
inline

Obtain process parent / fork side.

◆ getWritePipe()

FileDescriptor & SC::ProcessFork::getWritePipe ( )

Gets the descriptor to "write" something to the other side.

◆ resumeChildFork()

Result SC::ProcessFork::resumeChildFork ( )

Sends 1 byte on parentToFork to resume State::Paused child fork.

◆ waitForChild()

Result SC::ProcessFork::waitForChild ( )

Waits for child fork to finish execution.


The documentation for this struct was generated from the following file: