Skip to content

Commit d2ee2f2

Browse files
committed
implement callback
1 parent f40a547 commit d2ee2f2

File tree

2 files changed

+106
-21
lines changed

2 files changed

+106
-21
lines changed

src/stdlib_system.F90

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ module stdlib_system
3737
logical :: completed = .false.
3838
integer(TICKS) :: start_time = 0
3939

40-
!> Stdin file name
40+
!> Standard input
4141
character(:), allocatable :: stdin_file
42+
character(:), allocatable :: stdin
4243

4344
!> Standard output
4445
character(:), allocatable :: stdout_file
@@ -49,6 +50,12 @@ module stdlib_system
4950
character(:), allocatable :: stderr_file
5051
character(:), allocatable :: stderr
5152

53+
!> Callback function
54+
procedure(process_callback), nopass, pointer :: oncomplete => null()
55+
56+
!> Optional payload for the callback function
57+
class(*), pointer :: payload => null()
58+
5259
!> Store time at the last update
5360
integer(TICKS) :: last_update = 0
5461

@@ -90,9 +97,9 @@ module stdlib_system
9097
!! Processes can be executed via a single command string or a list of arguments, with options to collect
9198
!! standard output and error streams, or to provide a standard input stream via a `character` string.
9299
!!
93-
!! @note The implementation depends on system-level process management capabilities.
100+
!! @note The implementation depends on system-level process management capabilitiesa
94101
!!
95-
module function run_async_cmd(cmd, stdin, want_stdout, want_stderr) result(process)
102+
module function run_async_cmd(cmd, stdin, want_stdout, want_stderr, callback, payload) result(process)
96103
!> The command line string to execute.
97104
character(*), intent(in) :: cmd
98105
!> Optional input sent to the process via standard input (stdin).
@@ -101,11 +108,16 @@ module function run_async_cmd(cmd, stdin, want_stdout, want_stderr) result(proce
101108
logical, optional, intent(in) :: want_stdout
102109
!> Whether to collect standard error output.
103110
logical, optional, intent(in) :: want_stderr
111+
!> Optional callback function to be called on process completion
112+
procedure(process_callback), optional :: callback
113+
!> Optional payload to pass to the callback on completion
114+
class(*), optional, intent(inout), target :: payload
104115
!> The output process handler.
105116
type(process_type) :: process
117+
106118
end function run_async_cmd
107119

108-
module function run_async_args(args, stdin, want_stdout, want_stderr) result(process)
120+
module function run_async_args(args, stdin, want_stdout, want_stderr, callback, payload) result(process)
109121
!> List of arguments for the process to execute.
110122
character(*), intent(in) :: args(:)
111123
!> Optional input sent to the process via standard input (stdin).
@@ -114,6 +126,10 @@ module function run_async_args(args, stdin, want_stdout, want_stderr) result(pro
114126
logical, optional, intent(in) :: want_stdout
115127
!> Whether to collect standard error output.
116128
logical, optional, intent(in) :: want_stderr
129+
!> Optional callback function to be called on process completion
130+
procedure(process_callback), optional :: callback
131+
!> Optional payload to pass to the callback on completion
132+
class(*), optional, intent(inout), target :: payload
117133
!> The output process handler.
118134
type(process_type) :: process
119135
end function run_async_args
@@ -137,7 +153,7 @@ end function run_async_args
137153
!!
138154
!! @note The implementation depends on system-level process management capabilities.
139155
!!
140-
module function run_sync_cmd(cmd, stdin, want_stdout, want_stderr) result(process)
156+
module function run_sync_cmd(cmd, stdin, want_stdout, want_stderr, callback, payload) result(process)
141157
!> The command line string to execute.
142158
character(*), intent(in) :: cmd
143159
!> Optional input sent to the process via standard input (stdin).
@@ -146,11 +162,15 @@ module function run_sync_cmd(cmd, stdin, want_stdout, want_stderr) result(proces
146162
logical, optional, intent(in) :: want_stdout
147163
!> Whether to collect standard error output.
148164
logical, optional, intent(in) :: want_stderr
165+
!> Optional callback function to be called on process completion
166+
procedure(process_callback), optional :: callback
167+
!> Optional payload to pass to the callback on completion
168+
class(*), optional, intent(inout), target :: payload
149169
!> The output process handler.
150170
type(process_type) :: process
151171
end function run_sync_cmd
152172

153-
module function run_sync_args(args, stdin, want_stdout, want_stderr) result(process)
173+
module function run_sync_args(args, stdin, want_stdout, want_stderr, callback, payload) result(process)
154174
!> List of arguments for the process to execute.
155175
character(*), intent(in) :: args(:)
156176
!> Optional input sent to the process via standard input (stdin).
@@ -159,6 +179,10 @@ module function run_sync_args(args, stdin, want_stdout, want_stderr) result(proc
159179
logical, optional, intent(in) :: want_stdout
160180
!> Whether to collect standard error output.
161181
logical, optional, intent(in) :: want_stderr
182+
!> Optional callback function to be called on process completion
183+
procedure(process_callback), optional :: callback
184+
!> Optional payload to pass to the callback on completion
185+
class(*), optional, intent(inout), target :: payload
162186
!> The output process handler.
163187
type(process_type) :: process
164188
end function run_sync_args
@@ -342,6 +366,21 @@ module subroutine sleep(millisec)
342366
end subroutine sleep
343367
end interface sleep
344368

369+
abstract interface
370+
subroutine process_callback(pid,exit_state,stdin,stdout,stderr,payload)
371+
import process_ID
372+
implicit none
373+
!> Process ID
374+
integer(process_ID), intent(in) :: pid
375+
!> Process return state
376+
integer, intent(in) :: exit_state
377+
!> Process input/output: presence of these arguments depends on how process was created
378+
character(len=*), optional, intent(in) :: stdin,stdout,stderr
379+
!> Optional payload passed by the user on process creation
380+
class(*), optional, intent(inout) :: payload
381+
end subroutine process_callback
382+
end interface
383+
345384
interface
346385

347386
!! version: experimental

src/stdlib_system_subprocess.F90

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
submodule (stdlib_system) stdlib_system_subprocess
22
use iso_c_binding
33
use iso_fortran_env, only: int64, real64
4-
use stdlib_system
54
use stdlib_strings, only: to_c_string, join
65
use stdlib_linalg_state, only: linalg_state_type, LINALG_ERROR, linalg_error_handling
76
implicit none(type, external)
@@ -85,7 +84,7 @@ module subroutine sleep(millisec)
8584

8685
end subroutine sleep
8786

88-
module function run_async_cmd(cmd, stdin, want_stdout, want_stderr) result(process)
87+
module function run_async_cmd(cmd, stdin, want_stdout, want_stderr, callback, payload) result(process)
8988
!> The command line string to execute.
9089
character(*), intent(in) :: cmd
9190
!> Optional input sent to the process via standard input (stdin).
@@ -94,14 +93,18 @@ module function run_async_cmd(cmd, stdin, want_stdout, want_stderr) result(proce
9493
logical, optional, intent(in) :: want_stdout
9594
!> Whether to collect standard error output.
9695
logical, optional, intent(in) :: want_stderr
96+
!> Optional callback function to be called on process completion
97+
procedure(process_callback), optional :: callback
98+
!> Optional payload to pass to the callback on completion
99+
class(*), optional, intent(inout), target :: payload
97100
!> The output process handler.
98101
type(process_type) :: process
99102

100-
process = process_open([cmd],.false.,stdin,want_stdout,want_stderr)
103+
process = process_open([cmd],.false.,stdin,want_stdout,want_stderr,callback,payload)
101104

102105
end function run_async_cmd
103106

104-
module function run_async_args(args, stdin, want_stdout, want_stderr) result(process)
107+
module function run_async_args(args, stdin, want_stdout, want_stderr, callback, payload) result(process)
105108
!> List of arguments for the process to execute.
106109
character(*), intent(in) :: args(:)
107110
!> Optional input sent to the process via standard input (stdin).
@@ -110,14 +113,18 @@ module function run_async_args(args, stdin, want_stdout, want_stderr) result(pro
110113
logical, optional, intent(in) :: want_stdout
111114
!> Whether to collect standard error output.
112115
logical, optional, intent(in) :: want_stderr
116+
!> Optional callback function to be called on process completion
117+
procedure(process_callback), optional :: callback
118+
!> Optional payload to pass to the callback on completion
119+
class(*), optional, intent(inout), target :: payload
113120
!> The output process handler.
114121
type(process_type) :: process
115122

116-
process = process_open(args,.false.,stdin,want_stdout,want_stderr)
123+
process = process_open(args,.false.,stdin,want_stdout,want_stderr,callback,payload)
117124

118125
end function run_async_args
119126

120-
module function run_sync_cmd(cmd, stdin, want_stdout, want_stderr) result(process)
127+
module function run_sync_cmd(cmd, stdin, want_stdout, want_stderr, callback, payload) result(process)
121128
!> The command line string to execute.
122129
character(*), intent(in) :: cmd
123130
!> Optional input sent to the process via standard input (stdin).
@@ -126,14 +133,18 @@ module function run_sync_cmd(cmd, stdin, want_stdout, want_stderr) result(proces
126133
logical, optional, intent(in) :: want_stdout
127134
!> Whether to collect standard error output.
128135
logical, optional, intent(in) :: want_stderr
136+
!> Optional callback function to be called on process completion
137+
procedure(process_callback), optional :: callback
138+
!> Optional payload to pass to the callback on completion
139+
class(*), optional, intent(inout), target :: payload
129140
!> The output process handler.
130141
type(process_type) :: process
131142

132-
process = process_open([cmd],.true.,stdin,want_stdout,want_stderr)
143+
process = process_open([cmd],.true.,stdin,want_stdout,want_stderr,callback,payload)
133144

134145
end function run_sync_cmd
135146

136-
module function run_sync_args(args, stdin, want_stdout, want_stderr) result(process)
147+
module function run_sync_args(args, stdin, want_stdout, want_stderr, callback, payload) result(process)
137148
!> List of arguments for the process to execute.
138149
character(*), intent(in) :: args(:)
139150
!> Optional input sent to the process via standard input (stdin).
@@ -142,15 +153,19 @@ module function run_sync_args(args, stdin, want_stdout, want_stderr) result(proc
142153
logical, optional, intent(in) :: want_stdout
143154
!> Whether to collect standard error output.
144155
logical, optional, intent(in) :: want_stderr
156+
!> Optional callback function to be called on process completion
157+
procedure(process_callback), optional :: callback
158+
!> Optional payload to pass to the callback on completion
159+
class(*), optional, intent(inout), target :: payload
145160
!> The output process handler.
146161
type(process_type) :: process
147162

148-
process = process_open(args,.true.,stdin,want_stdout,want_stderr)
163+
process = process_open(args,.true.,stdin,want_stdout,want_stderr,callback,payload)
149164

150165
end function run_sync_args
151166

152167
!> Internal function: open a new process from a command line
153-
function process_open_cmd(cmd,wait,stdin,want_stdout,want_stderr) result(process)
168+
function process_open_cmd(cmd,wait,stdin,want_stdout,want_stderr,callback,payload) result(process)
154169
!> The command and arguments
155170
character(*), intent(in) :: cmd
156171
!> Optional character input to be sent to the process via pipe
@@ -159,15 +174,19 @@ function process_open_cmd(cmd,wait,stdin,want_stdout,want_stderr) result(process
159174
logical, intent(in) :: wait
160175
!> Require collecting output
161176
logical, optional, intent(in) :: want_stdout, want_stderr
177+
!> Optional callback function to be called on process completion
178+
procedure(process_callback), optional :: callback
179+
!> Optional payload to pass to the callback on completion
180+
class(*), optional, intent(inout), target :: payload
162181
!> The output process handler
163182
type(process_type) :: process
164183

165-
process = process_open([cmd],wait,stdin,want_stdout,want_stderr)
184+
process = process_open([cmd],wait,stdin,want_stdout,want_stderr,callback,payload)
166185

167186
end function process_open_cmd
168187

169188
!> Internal function: open a new process from arguments
170-
function process_open(args,wait,stdin,want_stdout,want_stderr) result(process)
189+
function process_open(args,wait,stdin,want_stdout,want_stderr,callback,payload) result(process)
171190
!> The command and arguments
172191
character(*), intent(in) :: args(:)
173192
!> Optional character input to be sent to the process via pipe
@@ -176,6 +195,10 @@ function process_open(args,wait,stdin,want_stdout,want_stderr) result(process)
176195
logical, intent(in) :: wait
177196
!> Require collecting output
178197
logical, optional, intent(in) :: want_stdout, want_stderr
198+
!> Optional callback function to be called on process completion
199+
procedure(process_callback), optional :: callback
200+
!> Optional payload to pass to the callback on completion
201+
class(*), optional, intent(inout), target :: payload
179202
!> The output process handler
180203
type(process_type) :: process
181204

@@ -197,6 +220,19 @@ function process_open(args,wait,stdin,want_stdout,want_stderr) result(process)
197220
if (collect_stdout) process%stdout_file = scratch_name('out')
198221
if (collect_stderr) process%stderr_file = scratch_name('err')
199222

223+
! Attach callback function and payload
224+
if (present(callback)) then
225+
process%oncomplete => callback
226+
else
227+
nullify(process%oncomplete)
228+
end if
229+
230+
if (present(payload)) then
231+
process%payload => payload
232+
else
233+
nullify(process%payload)
234+
end if
235+
200236
! Save the process's generation time
201237
call system_clock(process%start_time,count_rate,count_max)
202238
process%last_update = process%start_time
@@ -452,23 +488,33 @@ subroutine save_completed_state(process,delete_files)
452488
! Clean up process state using waitpid
453489
if (process%id/=FORKED_PROCESS) call process_query_status(process%id, C_TRUE, running, exit_code)
454490

455-
! Process is over: load stdout/stderr if requested
491+
! Process is over: load stderr if requested
456492
if (allocated(process%stderr_file)) then
457493
process%stderr = getfile(process%stderr_file,delete=delete_files)
458494
deallocate(process%stderr_file)
459495
endif
460496

497+
! Process is over: load stdout if requested
461498
if (allocated(process%stdout_file)) then
462499
process%stdout = getfile(process%stdout_file,delete=delete_files)
463500
deallocate(process%stdout_file)
464501
endif
465502

503+
! Process is over: delete stdin file if it was provided
466504
if (allocated(process%stdin_file)) then
467-
open(newunit=delete,file=process%stdin_file,access='stream',action='write')
468-
close(delete,status='delete')
505+
process%stdin = getfile(process%stdin_file,delete=delete_files)
469506
deallocate(process%stdin_file)
470507
end if
471508

509+
! Process is over: invoke callback if requested
510+
if (associated(process%oncomplete)) &
511+
call process%oncomplete(process%id, &
512+
process%exit_code, &
513+
process%stderr, &
514+
process%stdout, &
515+
process%stderr, &
516+
process%payload)
517+
472518
end subroutine save_completed_state
473519

474520
!> Live check if a process is running

0 commit comments

Comments
 (0)