---
tags: forced-execution
---
# Cutting processes
Processes can be merged/discarded in the following positions:
- After the program reaches the end of the outer-most conditional
- End each process for after completing a certain scope of execution
## Current approach [Latest - 06/21/2021]
In order to avoid the processes to increase exponentially and avoid the dataflow being mitigated entirely, a hybrid approach is used which scales linearly with the number of conditionals in the program.
**Rule:** For each conditional, only one process will continue through a branch. The process will be selected as per FCFS (first come first serve) basis.
To avoid race-condition for the above rule, all forks wait **indifinitely** for their child process to terminate. Effectively only one process is executed at a time.
Example:
```python=
a = 5
if a== 2: # fork() called here
a = 3
print("a-")
# Status: P1 and P2, both are alive with values a=5 and a=3
if a > 6: # fork () called here for P1 and P2 => P3 and P4
a = 6
print("b") # kill fork for P3 here since it will reach later than P4
elif a < 5: # fork () called here for P1 and P2 => P5 and P6
a =7
print("c") # kill fork for P5 here since it will reach later than P6
else:
print("d")
a=8
print(a) # P1, P2, P4, and P6 will reach here only
```
Following the rule stated above, a forked process will not proceed further in it's scope (will not reach it's post dominator) if another process has already crossed it. In this manner, all blocks will have dataflow atleast once while keeping the increase in processes *linear* with the increase in conditionals.
> Note that this does not take into consideration forks related to loops or try/catch blocks.
## Previous approaches [06/14/2021]
### Cut outer-most conditional
>TLDR; Does not work for the program having the entire code in the outer-most conditional branch
This is where all forks will merge or all child processes will end *except* the main process.
Example:
```python=
#hash: 8cb5c1756e2c688a389b59b26a17a678f8062074
def architecture(executable=sys.executable, bits='', linkage=''):
if not bits:
import struct
try:
size = struct.calcsize('P')
except struct.error:
size = struct.calcsize('l')
bits = str(size * 8) + 'bit'
# CUT HERE
if executable:
output = _syscmd_file(executable, '')
else:
output = ''
# CUT HERE
if not output and executable == sys.executable:
if sys.platform in _default_architecture:
b, l = _default_architecture[sys.platform]
if b:
bits = b
if l:
linkage = l
return (
bits, linkage)
# CUT HERE
fileout = _architecture_split(output)[1:]
# code removed here
return (
bits, linkage)
```
In the example above we cut at line 11, 16, and 28. At each position all except the main process is cut down.
This however fails for example as follows:
```python=
def uname():
global _uname_cache
no_os_uname = 0
if _uname_cache is not None:
return _uname_cache
else:
processor = ''
try:
system, node, release, version, machine = os.uname()
except AttributeError:
no_os_uname = 1
if no_os_uname or not [_f for _f in (system, node, release, version, machine) if _f]:
if no_os_uname:
system = sys.platform
release = ''
version = ''
node = _node()
machine = ''
use_syscmd_ver = 1
# Code removed here
if not processor:
processor = _syscmd_uname('-p', '')
if system == 'unknown':
system = ''
if node == 'unknown':
node = ''
if release == 'unknown':
release = ''
if version == 'unknown':
version = ''
if machine == 'unknown':
machine = ''
if processor == 'unknown':
processor = ''
if system == 'Microsoft' and release == 'Windows':
system = 'Windows'
release = 'Vista'
_uname_cache = (system, node, release, version, machine, processor)
return _uname_cache
# CUT HERE
```
For the above example, the cutting makes no change in mitigating the exponential increase in processes since it would cut when the entire function ends.
### Cut after each scope
>TLDR; Should lose dataflow by not full executing processes through branches
Here each forked child process will execute for until the scope of the conditional's block and end as soon as the end of the scope is reached.
Example:
```python=
#hash: 8cb5c1756e2c688a389b59b26a17a678f8062074
def architecture(executable=sys.executable, bits='', linkage=''):
if not bits:
import struct
try:
size = struct.calcsize('P')
except struct.error:
size = struct.calcsize('l')
bits = str(size * 8) + 'bit'
# CUT HERE
if executable:
output = _syscmd_file(executable, '')
# CUT HERE
else:
output = ''
# CUT HERE
if not output and executable == sys.executable:
if sys.platform in _default_architecture:
b, l = _default_architecture[sys.platform]
if b:
bits = b
# CUT HERE
if l:
linkage = l
# CUT HERE
return (
bits, linkage)
# CUT HERE
fileout = _architecture_split(output)[1:]
# code removed here
return (
bits, linkage)
```
In contrast from the previous case, here this approach will cut all processes after the end of execution in the scope of the conditional. Theretically speaking, each process is forked to execute the scope of a conditional and end itself. All information will be stored but the data-flow would be blocked through the newly covered branches. The main process executes as is.
This mitigates the exponential increase of processes.