Skip to content

Commit 7ab1a5b

Browse files
authoredFeb 25, 2025··
system: subprocessing interface (#911)
2 parents 76292d3 + 0675b8c commit 7ab1a5b

22 files changed

+2291
-39
lines changed
 

‎CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.14.0)
44
set(CMAKE_USER_MAKE_RULES_OVERRIDE ${CMAKE_CURRENT_SOURCE_DIR}/config/DefaultFlags.cmake)
55

66
project(fortran_stdlib
7-
LANGUAGES Fortran
7+
LANGUAGES Fortran C
88
DESCRIPTION "Community driven and agreed upon de facto standard library for Fortran"
99
)
1010

‎config/fypp_deployment.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def recursive_copy(folder):
105105
for root, _, files in os.walk(folder):
106106
for file in files:
107107
if file not in prune:
108-
if file.endswith(".f90") or file.endswith(".F90") or file.endswith(".dat") or file.endswith(".npy"):
108+
if file.endswith((".f90", ".F90", ".dat", ".npy", ".c")):
109109
shutil.copy2(os.path.join(root, file), base_folder+os.sep+folder+os.sep+file)
110110
recursive_copy('src')
111111
recursive_copy('test')

‎doc/specs/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ This is an index/directory of the specifications (specs) for each new module/fea
3838
- [string\_type](./stdlib_string_type.html) - Basic string support
3939
- [stringlist_type](./stdlib_stringlist_type.html) - 1-Dimensional list of strings
4040
- [strings](./stdlib_strings.html) - String handling and manipulation routines
41+
- [system](./stdlib_system.html) - OS and sub-processing routines
4142
- [version](./stdlib_version.html) - Version information
4243

4344
## Released/Stable Features & Modules

‎doc/specs/stdlib_strings.md

+1
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,7 @@ The result is of the same type as the elements of `strings` (`type(string_type)`
498498
```
499499

500500
<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -->
501+
501502
### `to_string`
502503

503504
#### Description

‎doc/specs/stdlib_system.md

+337
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
---
2+
title: system
3+
---
4+
5+
# System and sub-processing module
6+
7+
The `stdlib_system` module provides interface for interacting with external processes, enabling the execution
8+
and monitoring of system commands or applications directly from Fortran.
9+
10+
[TOC]
11+
12+
## `run` - Execute an external process synchronously
13+
14+
### Status
15+
16+
Experimental
17+
18+
### Description
19+
20+
The `run` interface allows execution of external processes using a single command string or a list of arguments.
21+
Processes run synchronously, meaning execution is blocked until the process finishes.
22+
Optional arguments enable the collection of standard output and error streams, as well as sending input via standard input.
23+
Additionally, a callback function can be specified to execute upon process completion, optionally receiving a user-defined payload.
24+
25+
### Syntax
26+
27+
`process = ` [[stdlib_subprocess(module):run(interface)]] `(args [, stdin] [, want_stdout] [, want_stderr] [, callback] [, payload])`
28+
29+
### Arguments
30+
31+
`args`: Shall be a `character(*)` string (for command-line execution) or a `character(*), dimension(:)` array (for argument-based execution). It specifies the command and arguments to execute. This is an `intent(in)` argument.
32+
33+
`stdin` (optional): Shall be a `character(*)` value containing input to send to the process via standard input (pipe). This is an `intent(in)` argument.
34+
35+
`want_stdout` (optional): Shall be a `logical` flag. If `.true.`, the standard output of the process will be captured; if `.false.` (default), it will be lost. This is an `intent(in)` argument.
36+
37+
`want_stderr` (optional): Shall be a `logical` flag. If `.true.`, the standard error output of the process will be captured. If `.false.` (default), it will be lost. This is an `intent(in)` argument.
38+
39+
`callback` (optional): Shall be a procedure conforming to the `process_callback` interface. If present, this function will be called upon process completion with the process ID, exit state, and optionally collected standard input, output, and error streams. This is an `intent(in)` argument.
40+
41+
`payload` (optional): Shall be a generic (`class(*)`) scalar that will be passed to the callback function upon process completion. It allows users to associate custom data with the process execution. This is an `intent(inout), target` argument.
42+
43+
### Return Value
44+
45+
Returns an object of type `process_type` that contains information about the state of the created process.
46+
47+
### Example
48+
49+
```fortran
50+
! Example usage with command line or list of arguments
51+
type(process_type) :: p
52+
53+
! Run a simple command line synchronously
54+
p = run("echo 'Hello, world!'", want_stdout=.true.)
55+
```
56+
57+
## `runasync` - Execute an external process asynchronously
58+
59+
### Status
60+
61+
Experimental
62+
63+
### Description
64+
65+
The `runasync` interface allows execution of external processes using a single command string or a list of arguments.
66+
Processes are run asynchronously (non-blocking), meaning execution does not wait for the process to finish.
67+
Optional arguments enable the collection of standard output and error streams, as well as sending input via standard input.
68+
Additionally, a callback function can be specified to execute upon process completion, optionally receiving a user-defined payload.
69+
70+
### Syntax
71+
72+
`process = ` [[stdlib_subprocess(module):runasync(interface)]] `(args [, stdin] [, want_stdout] [, want_stderr] [, callback] [, payload])`
73+
74+
### Arguments
75+
76+
`args`: Shall be a `character(*)` string (for command-line execution) or a `character(*), dimension(:)` array (for argument-based execution). It specifies the command and arguments to execute. This is an `intent(in)` argument.
77+
78+
`stdin` (optional): Shall be a `character(*)` value containing input to send to the process via standard input (pipe). This is an `intent(in)` argument.
79+
80+
`want_stdout` (optional): Shall be a `logical` flag. If `.true.`, the standard output of the process will be captured; if `.false.` (default), it will be lost. This is an `intent(in)` argument.
81+
82+
`want_stderr` (optional): Shall be a `logical` flag. If `.true.`, the standard error output of the process will be captured. Default: `.false.`. This is an `intent(in)` argument.
83+
84+
`callback` (optional): Shall be a procedure conforming to the `process_callback` interface. If present, this function will be called upon process completion with the process ID, exit state, and optionally collected standard input, output, and error streams. This is an `intent(in)` argument.
85+
86+
`payload` (optional): Shall be a generic (`class(*)`) scalar that will be passed to the callback function upon process completion. It allows users to associate custom data with the process execution. This is an `intent(inout), target` argument.
87+
88+
### Return Value
89+
90+
Returns an object of type `process_type` that contains information about the state of the created process.
91+
92+
### Example
93+
94+
```fortran
95+
{!example/system/example_process_1.f90!}
96+
```
97+
98+
## `is_running` - Check if a process is still running
99+
100+
### Status
101+
102+
Experimental
103+
104+
### Description
105+
106+
The `is_running` interface provides a method to check if an external process is still running.
107+
This is useful for monitoring the status of asynchronous processes created with the `run` interface.
108+
109+
### Syntax
110+
111+
`status = ` [[stdlib_subprocess(module):is_running(interface)]] `(process)`
112+
113+
### Arguments
114+
115+
`process`: Shall be a `type(process_type)` object representing the external process to check. This is an `intent(inout)` argument.
116+
117+
118+
### Return Value
119+
120+
Returns a `logical` value: `.true.` if the process is still running, or `.false.` if the process has terminated.
121+
After a call to `is_running`, the `type(process_type)` structure is also updated to the latest process state.
122+
123+
### Example
124+
125+
```fortran
126+
{!example/system/example_process_2.f90!}
127+
```
128+
129+
## `is_completed` - Check if a process has completed execution
130+
131+
### Status
132+
133+
Experimental
134+
135+
### Description
136+
137+
The `is_completed` interface provides a method to check if an external process has finished execution.
138+
This is useful for determining whether asynchronous processes created with the `run` interface have terminated.
139+
140+
### Syntax
141+
142+
`status = ` [[stdlib_subprocess(module):is_completed(interface)]] `(process)`
143+
144+
### Arguments
145+
146+
`process`: Shall be a `type(process_type)` object representing the external process to check. This is an `intent(inout)` argument.
147+
148+
### Return Value
149+
150+
Returns a `logical` value:
151+
- `.true.` if the process has completed.
152+
- `.false.` if the process is still running.
153+
154+
After a call to `is_completed`, the `type(process_type)` structure is updated to reflect the latest process state.
155+
156+
### Example
157+
158+
```fortran
159+
{!example/system/example_process_1.f90!}
160+
```
161+
162+
## `elapsed` - Return process lifetime in seconds
163+
164+
### Status
165+
166+
Experimental
167+
168+
### Description
169+
170+
The `elapsed` interface provides a method to calculate the total time that has elapsed since a process was started.
171+
This is useful for tracking the duration of an external process or for performance monitoring purposes.
172+
173+
The result is a real value representing the elapsed time in seconds, measured from the time the process was created.
174+
175+
### Syntax
176+
177+
`delta_t = ` [[stdlib_subprocess(module):elapsed(subroutine)]] `(process)`
178+
179+
### Arguments
180+
181+
`process`: Shall be a `type(process_type)` object representing the external process. It is an `intent(in)` argument.
182+
183+
### Return Value
184+
185+
Returns a `real(real64)` value that represents the elapsed time (in seconds) since the process was started.
186+
If the process is still running, the value returned is the time elapsed until the call to this function.
187+
Otherwise, the total process duration from creation until completion is returned.
188+
189+
### Example
190+
191+
```fortran
192+
{!example/system/example_process_3.f90!}
193+
```
194+
195+
## `wait` - Wait until a running process is completed
196+
197+
### Status
198+
199+
Experimental
200+
201+
### Description
202+
203+
The `wait` interface provides a method to block the calling program until the specified process completes.
204+
If the process is running asynchronously, this subroutine will pause the workflow until the given process finishes.
205+
Additionally, an optional maximum wait time can be provided. If the process does not finish within the specified time,
206+
the subroutine will return without waiting further.
207+
208+
On return from this routine, the process state is accordingly updated.
209+
This is useful when you want to wait for a background task to complete, but want to avoid indefinite blocking
210+
in case of process hang or delay.
211+
212+
213+
### Syntax
214+
215+
`call ` [[stdlib_subprocess(module):wait(subroutine)]] `(process [, max_wait_time])`
216+
217+
### Arguments
218+
219+
`process`: Shall be a `type(process_type)` object representing the external process to monitor.
220+
This is an `intent(inout)` argument, and its state is updated upon completion.
221+
222+
`max_wait_time` (optional): Shall be a `real` value specifying the maximum wait time in seconds.
223+
If not provided, the subroutine will wait indefinitely until the process completes.
224+
225+
### Example
226+
227+
```fortran
228+
{!example/system/example_process_2.f90!}
229+
```
230+
231+
## `update` - Update the internal state of a process
232+
233+
### Status
234+
235+
Experimental
236+
237+
### Description
238+
239+
The `update` interface allows the internal state of a process object to be updated by querying the system.
240+
After the process completes, the standard output and standard error are retrieved, if they were requested, and loaded into the `process%stdout` and `process%stderr` string variables, respectively.
241+
242+
This is especially useful for monitoring asynchronous processes and retrieving their output after they have finished.
243+
244+
### Syntax
245+
246+
`call ` [[stdlib_subprocess(module):update(subroutine)]] `(process)`
247+
248+
### Arguments
249+
250+
`process`: Shall be a `type(process_type)` object representing the external process whose state needs to be updated.
251+
This is an `intent(inout)` argument, and its internal state is updated on completion.
252+
253+
### Example
254+
255+
```fortran
256+
{!example/system/example_process_5.f90!}
257+
```
258+
259+
## `kill` - Terminate a running process
260+
261+
### Status
262+
263+
Experimental
264+
265+
### Description
266+
267+
The `kill` interface is used to terminate a running external process. It attempts to stop the process and returns a boolean flag indicating whether the operation was successful.
268+
This interface is useful when a process needs to be forcefully stopped, for example, if it becomes unresponsive or if its execution is no longer required.
269+
270+
### Syntax
271+
272+
`call ` [[stdlib_subprocess(module):kill(subroutine)]] `(process, success)`
273+
274+
### Arguments
275+
276+
`process`: Shall be a `type(process_type)` object representing the external process to be terminated.
277+
This is an `intent(inout)` argument, and on return is updated with the terminated process state.
278+
279+
`success`: Shall be a `logical` variable. It is set to `.true.` if the process was successfully killed, or `.false.` otherwise.
280+
281+
### Example
282+
283+
```fortran
284+
{!example/system/example_process_4.f90!}
285+
```
286+
287+
## `sleep` - Pause execution for a specified time in milliseconds
288+
289+
### Status
290+
291+
Experimental
292+
293+
### Description
294+
295+
The `sleep` interface pauses the execution of a program for a specified duration, given in milliseconds.
296+
This routine acts as a cross-platform wrapper, abstracting the underlying platform-specific sleep implementations.
297+
It ensures that the requested sleep duration is honored on both Windows and Unix-like systems.
298+
299+
### Syntax
300+
301+
`call ` [[stdlib_system(module):sleep(subroutine)]] `(millisec)`
302+
303+
### Arguments
304+
305+
`millisec`: Shall be an `integer` representing the number of milliseconds to sleep. This is an `intent(in)` argument.
306+
307+
### Example
308+
309+
```fortran
310+
{!example/system/example_sleep.f90!}
311+
```
312+
313+
## `is_windows` - Check if the system is running on Windows
314+
315+
### Status
316+
317+
Experimental
318+
319+
### Description
320+
321+
The `is_windows` interface provides a quick, compile-time check to determine if the current system is Windows.
322+
It leverages a C function that checks for the presence of the `_WIN32` macro, which is defined in C compilers when targeting Windows.
323+
This function is highly efficient and works during the compilation phase, avoiding the need for runtime checks.
324+
325+
### Syntax
326+
327+
`result = ` [[stdlib_system(module):is_windows(function)]] `()`
328+
329+
### Return Value
330+
331+
Returns a `logical` flag: `.true.` if the system is Windows, or `.false.` otherwise.
332+
333+
### Example
334+
335+
```fortran
336+
{!example/system/example_process_1.f90!}
337+
```

‎example/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@ add_subdirectory(stats_distribution_uniform)
3030
add_subdirectory(stringlist_type)
3131
add_subdirectory(strings)
3232
add_subdirectory(string_type)
33+
add_subdirectory(system)
3334
add_subdirectory(version)

‎example/system/CMakeLists.txt

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
ADD_EXAMPLE(process_1)
2+
ADD_EXAMPLE(process_2)
3+
ADD_EXAMPLE(process_3)
4+
ADD_EXAMPLE(process_4)
5+
ADD_EXAMPLE(process_5)
6+
ADD_EXAMPLE(process_6)
7+
ADD_EXAMPLE(process_7)
8+
ADD_EXAMPLE(sleep)

0 commit comments

Comments
 (0)
Please sign in to comment.