fortrangoingonforty/fgof-devloop / 92707a3

Browse files

Add devloop job supervision

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
92707a3d65945d0825994fec2d3613a7b9a277ec
Parents
3863c5f
Tree
0955343

3 changed files

StatusFile+-
M fpm.toml 1 0
M src/fgof_devloop.f90 267 0
M src/fgof_devloop_types.f90 51 0
fpm.tomlmodified
@@ -20,5 +20,6 @@ implicit-external = false
20
 source-form = "free"
20
 source-form = "free"
21
 
21
 
22
 [dependencies]
22
 [dependencies]
23
+fgof-jobs = { git = "https://github.com/FortranGoingOnForty/fgof-jobs.git", tag = "v0.1.0" }
23
 fgof-process = { git = "https://github.com/FortranGoingOnForty/fgof-process.git", rev = "dd71a77c61985380c7e32f4a719fd8bb247625c7" }
24
 fgof-process = { git = "https://github.com/FortranGoingOnForty/fgof-process.git", rev = "dd71a77c61985380c7e32f4a719fd8bb247625c7" }
24
 fgof-watch = { git = "https://github.com/FortranGoingOnForty/fgof-watch.git", tag = "v0.1.0" }
25
 fgof-watch = { git = "https://github.com/FortranGoingOnForty/fgof-watch.git", tag = "v0.1.0" }
src/fgof_devloop.f90modified
@@ -1,4 +1,30 @@
1
 module fgof_devloop
1
 module fgof_devloop
2
+  use fgof_jobs, only : &
3
+    attach_job, &
4
+    attach_pipeline_members, &
5
+    clear_job_handle, &
6
+    clear_job_spec, &
7
+    configure_job, &
8
+    FGOF_JOBS_SIGNAL_SCOPE_GROUP, &
9
+    FGOF_JOBS_TERMINAL_HANDOFF_FOREGROUND, &
10
+    job_continue_result, &
11
+    job_exit_result, &
12
+    job_handle, &
13
+    job_is_configured, &
14
+    job_is_finished, &
15
+    job_is_running, &
16
+    job_is_stopped, &
17
+    job_needs_cleanup, &
18
+    job_owns_process_group, &
19
+    job_requires_terminal_handoff, &
20
+    job_result, &
21
+    job_signal_result, &
22
+    job_signal_scope, &
23
+    job_spec, &
24
+    job_stop_result, &
25
+    make_job_spec, &
26
+    observe_wait_result, &
27
+    release_job
2
   use fgof_process, only : &
28
   use fgof_process, only : &
3
     FGOF_PROCESS_OK, &
29
     FGOF_PROCESS_OK, &
4
     process_command, &
30
     process_command, &
@@ -22,6 +48,10 @@ module fgof_devloop
22
     FGOF_DEVLOOP_COMMAND_NONE, &
48
     FGOF_DEVLOOP_COMMAND_NONE, &
23
     FGOF_DEVLOOP_COMMAND_RUN, &
49
     FGOF_DEVLOOP_COMMAND_RUN, &
24
     FGOF_DEVLOOP_COMMAND_SMOKE, &
50
     FGOF_DEVLOOP_COMMAND_SMOKE, &
51
+    FGOF_DEVLOOP_JOB_ACTION_NONE, &
52
+    FGOF_DEVLOOP_JOB_ACTION_RESTART, &
53
+    FGOF_DEVLOOP_JOB_ACTION_START, &
54
+    FGOF_DEVLOOP_JOB_ACTION_STOP, &
25
     FGOF_DEVLOOP_TRIGGER_CHANGE, &
55
     FGOF_DEVLOOP_TRIGGER_CHANGE, &
26
     FGOF_DEVLOOP_TRIGGER_MANUAL, &
56
     FGOF_DEVLOOP_TRIGGER_MANUAL, &
27
     FGOF_DEVLOOP_TRIGGER_NONE, &
57
     FGOF_DEVLOOP_TRIGGER_NONE, &
@@ -30,6 +60,9 @@ module fgof_devloop
30
     devloop_command_spec, &
60
     devloop_command_spec, &
31
     devloop_cycle, &
61
     devloop_cycle, &
32
     devloop_decision, &
62
     devloop_decision, &
63
+    devloop_job_plan, &
64
+    devloop_job_spec, &
65
+    devloop_job_state, &
33
     devloop_options, &
66
     devloop_options, &
34
     devloop_state, &
67
     devloop_state, &
35
     devloop_supervision_result, &
68
     devloop_supervision_result, &
@@ -41,10 +74,15 @@ module fgof_devloop
41
   character(len=*), parameter :: FGOF_DEVLOOP_BACKEND_MODEL = "model"
74
   character(len=*), parameter :: FGOF_DEVLOOP_BACKEND_MODEL = "model"
42
 
75
 
43
   public :: begin_devloop_cycle
76
   public :: begin_devloop_cycle
77
+  public :: attach_devloop_job
78
+  public :: attach_devloop_pipeline_members
44
   public :: clear_devloop_command_result
79
   public :: clear_devloop_command_result
45
   public :: clear_devloop_command_spec
80
   public :: clear_devloop_command_spec
46
   public :: clear_devloop_cycle
81
   public :: clear_devloop_cycle
47
   public :: clear_devloop_decision
82
   public :: clear_devloop_decision
83
+  public :: clear_devloop_job_plan
84
+  public :: clear_devloop_job_spec
85
+  public :: clear_devloop_job_state
48
   public :: clear_devloop_options
86
   public :: clear_devloop_options
49
   public :: clear_devloop_state
87
   public :: clear_devloop_state
50
   public :: clear_devloop_supervision_result
88
   public :: clear_devloop_supervision_result
@@ -57,9 +95,14 @@ module fgof_devloop
57
   public :: devloop_command_spec
95
   public :: devloop_command_spec
58
   public :: devloop_cycle
96
   public :: devloop_cycle
59
   public :: devloop_decision
97
   public :: devloop_decision
98
+  public :: devloop_job_plan
99
+  public :: devloop_job_restart_plan
100
+  public :: devloop_job_spec
101
+  public :: devloop_job_state
60
   public :: devloop_manual_trigger
102
   public :: devloop_manual_trigger
61
   public :: devloop_options
103
   public :: devloop_options
62
   public :: devloop_run_command
104
   public :: devloop_run_command
105
+  public :: devloop_service_job
63
   public :: devloop_smoke_command
106
   public :: devloop_smoke_command
64
   public :: devloop_start_trigger
107
   public :: devloop_start_trigger
65
   public :: devloop_state
108
   public :: devloop_state
@@ -79,10 +122,23 @@ module fgof_devloop
79
   public :: FGOF_DEVLOOP_COMMAND_NONE
122
   public :: FGOF_DEVLOOP_COMMAND_NONE
80
   public :: FGOF_DEVLOOP_COMMAND_RUN
123
   public :: FGOF_DEVLOOP_COMMAND_RUN
81
   public :: FGOF_DEVLOOP_COMMAND_SMOKE
124
   public :: FGOF_DEVLOOP_COMMAND_SMOKE
125
+  public :: FGOF_DEVLOOP_JOB_ACTION_NONE
126
+  public :: FGOF_DEVLOOP_JOB_ACTION_RESTART
127
+  public :: FGOF_DEVLOOP_JOB_ACTION_START
128
+  public :: FGOF_DEVLOOP_JOB_ACTION_STOP
82
   public :: FGOF_DEVLOOP_TRIGGER_CHANGE
129
   public :: FGOF_DEVLOOP_TRIGGER_CHANGE
83
   public :: FGOF_DEVLOOP_TRIGGER_MANUAL
130
   public :: FGOF_DEVLOOP_TRIGGER_MANUAL
84
   public :: FGOF_DEVLOOP_TRIGGER_NONE
131
   public :: FGOF_DEVLOOP_TRIGGER_NONE
85
   public :: FGOF_DEVLOOP_TRIGGER_START
132
   public :: FGOF_DEVLOOP_TRIGGER_START
133
+  public :: job_continue_result
134
+  public :: job_exit_result
135
+  public :: job_handle
136
+  public :: job_result
137
+  public :: job_signal_result
138
+  public :: job_spec
139
+  public :: job_stop_result
140
+  public :: observe_devloop_job
141
+  public :: release_devloop_job
86
   public :: run_devloop_command
142
   public :: run_devloop_command
87
   public :: run_devloop_cycle
143
   public :: run_devloop_cycle
88
   public :: should_start_on_open
144
   public :: should_start_on_open
@@ -198,6 +254,52 @@ contains
198
     supervision%timed_out = .false.
254
     supervision%timed_out = .false.
199
   end function clear_devloop_supervision_result
255
   end function clear_devloop_supervision_result
200
 
256
 
257
+  function clear_devloop_job_spec() result(spec)
258
+    type(devloop_job_spec) :: spec
259
+
260
+    spec%job = clear_job_spec()
261
+    spec%enabled = .false.
262
+    spec%stop_before_restart = .true.
263
+    spec%release_on_handoff = .false.
264
+    spec%label = ""
265
+  end function clear_devloop_job_spec
266
+
267
+  function clear_devloop_job_state() result(job_state)
268
+    type(devloop_job_state) :: job_state
269
+
270
+    job_state%spec = clear_devloop_job_spec()
271
+    job_state%handle = clear_job_handle()
272
+    job_state%pid = 0
273
+    job_state%process_group = 0
274
+    job_state%signal_scope = FGOF_JOBS_SIGNAL_SCOPE_GROUP
275
+    job_state%configured = .false.
276
+    job_state%attached = .false.
277
+    job_state%running = .false.
278
+    job_state%stopped = .false.
279
+    job_state%finished = .false.
280
+    job_state%cleanup_needed = .false.
281
+    job_state%owns_process_group = .false.
282
+    job_state%terminal_handoff_required = .false.
283
+    job_state%released = .false.
284
+    job_state%label = ""
285
+  end function clear_devloop_job_state
286
+
287
+  function clear_devloop_job_plan() result(plan)
288
+    type(devloop_job_plan) :: plan
289
+
290
+    plan%action = FGOF_DEVLOOP_JOB_ACTION_NONE
291
+    plan%pid = 0
292
+    plan%process_group = 0
293
+    plan%signal_scope = FGOF_JOBS_SIGNAL_SCOPE_GROUP
294
+    plan%should_start = .false.
295
+    plan%should_stop = .false.
296
+    plan%should_restart = .false.
297
+    plan%should_release = .false.
298
+    plan%cleanup_needed = .false.
299
+    plan%terminal_handoff_required = .false.
300
+    plan%reason = ""
301
+  end function clear_devloop_job_plan
302
+
201
   function clear_devloop_state() result(state)
303
   function clear_devloop_state() result(state)
202
     type(devloop_state) :: state
304
     type(devloop_state) :: state
203
 
305
 
@@ -301,6 +403,63 @@ contains
301
     spec = make_devloop_command(FGOF_DEVLOOP_COMMAND_SMOKE, command_value, options, label)
403
     spec = make_devloop_command(FGOF_DEVLOOP_COMMAND_SMOKE, command_value, options, label)
302
   end function devloop_smoke_command
404
   end function devloop_smoke_command
303
 
405
 
406
+  function devloop_service_job(command, argv, label, background, new_process_group, signal_scope, &
407
+                               terminal_handoff, resume_sends_sigcont, stop_before_restart, &
408
+                               release_on_handoff) result(spec)
409
+    character(len=*), intent(in) :: command
410
+    character(len=*), intent(in), optional :: argv(:)
411
+    character(len=*), intent(in), optional :: label
412
+    logical, intent(in), optional :: background
413
+    logical, intent(in), optional :: new_process_group
414
+    integer, intent(in), optional :: signal_scope
415
+    integer, intent(in), optional :: terminal_handoff
416
+    logical, intent(in), optional :: resume_sends_sigcont
417
+    logical, intent(in), optional :: stop_before_restart
418
+    logical, intent(in), optional :: release_on_handoff
419
+    type(devloop_job_spec) :: spec
420
+    logical :: actual_background
421
+    logical :: actual_new_process_group
422
+    integer :: actual_signal_scope
423
+    integer :: actual_terminal_handoff
424
+    logical :: actual_resume_sends_sigcont
425
+
426
+    spec = clear_devloop_job_spec()
427
+
428
+    actual_background = .true.
429
+    if (present(background)) actual_background = background
430
+    actual_new_process_group = .true.
431
+    if (present(new_process_group)) actual_new_process_group = new_process_group
432
+    actual_signal_scope = FGOF_JOBS_SIGNAL_SCOPE_GROUP
433
+    if (present(signal_scope)) actual_signal_scope = signal_scope
434
+    actual_terminal_handoff = FGOF_JOBS_TERMINAL_HANDOFF_FOREGROUND
435
+    if (present(terminal_handoff)) actual_terminal_handoff = terminal_handoff
436
+    actual_resume_sends_sigcont = .true.
437
+    if (present(resume_sends_sigcont)) actual_resume_sends_sigcont = resume_sends_sigcont
438
+
439
+    if (present(argv)) then
440
+      spec%job = make_job_spec(command, argv, background=actual_background, &
441
+                               new_process_group=actual_new_process_group, &
442
+                               signal_scope=actual_signal_scope, &
443
+                               terminal_handoff=actual_terminal_handoff, &
444
+                               resume_sends_sigcont=actual_resume_sends_sigcont)
445
+    else
446
+      spec%job = make_job_spec(command, background=actual_background, &
447
+                               new_process_group=actual_new_process_group, &
448
+                               signal_scope=actual_signal_scope, &
449
+                               terminal_handoff=actual_terminal_handoff, &
450
+                               resume_sends_sigcont=actual_resume_sends_sigcont)
451
+    end if
452
+
453
+    spec%enabled = allocated(spec%job%command)
454
+    if (present(stop_before_restart)) spec%stop_before_restart = stop_before_restart
455
+    if (present(release_on_handoff)) spec%release_on_handoff = release_on_handoff
456
+    if (present(label)) then
457
+      spec%label = label
458
+    else if (allocated(spec%job%command)) then
459
+      spec%label = spec%job%command
460
+    end if
461
+  end function devloop_service_job
462
+
304
   function devloop_summarize_watch_events(events) result(summary)
463
   function devloop_summarize_watch_events(events) result(summary)
305
     type(watch_event), intent(in) :: events(:)
464
     type(watch_event), intent(in) :: events(:)
306
     type(devloop_watch_summary) :: summary
465
     type(devloop_watch_summary) :: summary
@@ -456,6 +615,93 @@ contains
456
     supervision%decision = finish_devloop_cycle(state, supervision%succeeded, supervision%last_exit_code)
615
     supervision%decision = finish_devloop_cycle(state, supervision%succeeded, supervision%last_exit_code)
457
   end function run_devloop_cycle
616
   end function run_devloop_cycle
458
 
617
 
618
+  function attach_devloop_job(spec, pid, process_group, owns_process, owns_process_group) result(job_state)
619
+    type(devloop_job_spec), intent(in) :: spec
620
+    integer, intent(in) :: pid
621
+    integer, intent(in), optional :: process_group
622
+    logical, intent(in), optional :: owns_process
623
+    logical, intent(in), optional :: owns_process_group
624
+    type(devloop_job_state) :: job_state
625
+    integer :: actual_process_group
626
+    logical :: actual_owns_process
627
+    logical :: actual_owns_process_group
628
+
629
+    job_state = clear_devloop_job_state()
630
+    if (.not. spec%enabled) return
631
+
632
+    job_state%spec = spec
633
+    call configure_job(job_state%handle, spec%job)
634
+
635
+    actual_process_group = pid
636
+    if (present(process_group)) actual_process_group = process_group
637
+    actual_owns_process = .true.
638
+    if (present(owns_process)) actual_owns_process = owns_process
639
+    actual_owns_process_group = actual_owns_process .and. spec%job%new_process_group .and. &
640
+                                actual_process_group == pid
641
+    if (present(owns_process_group)) actual_owns_process_group = owns_process_group
642
+
643
+    call attach_job(job_state%handle, pid, process_group=actual_process_group, &
644
+                    owns_process=actual_owns_process, owns_process_group=actual_owns_process_group)
645
+    call refresh_devloop_job_state(job_state)
646
+  end function attach_devloop_job
647
+
648
+  subroutine attach_devloop_pipeline_members(job_state, pids)
649
+    type(devloop_job_state), intent(inout) :: job_state
650
+    integer, intent(in) :: pids(:)
651
+
652
+    call attach_pipeline_members(job_state%handle, pids)
653
+    call refresh_devloop_job_state(job_state)
654
+  end subroutine attach_devloop_pipeline_members
655
+
656
+  subroutine observe_devloop_job(job_state, result_value)
657
+    type(devloop_job_state), intent(inout) :: job_state
658
+    type(job_result), intent(in) :: result_value
659
+
660
+    call observe_wait_result(job_state%handle, result_value)
661
+    call refresh_devloop_job_state(job_state)
662
+  end subroutine observe_devloop_job
663
+
664
+  subroutine release_devloop_job(job_state)
665
+    type(devloop_job_state), intent(inout) :: job_state
666
+
667
+    call release_job(job_state%handle)
668
+    job_state%released = .true.
669
+    call refresh_devloop_job_state(job_state)
670
+  end subroutine release_devloop_job
671
+
672
+  function devloop_job_restart_plan(job_state, trigger) result(plan)
673
+    type(devloop_job_state), intent(in) :: job_state
674
+    type(devloop_trigger), intent(in) :: trigger
675
+    type(devloop_job_plan) :: plan
676
+
677
+    plan = clear_devloop_job_plan()
678
+    plan%pid = job_state%pid
679
+    plan%process_group = job_state%process_group
680
+    plan%signal_scope = job_state%signal_scope
681
+    plan%cleanup_needed = job_state%cleanup_needed
682
+    plan%terminal_handoff_required = job_state%terminal_handoff_required
683
+
684
+    if (.not. job_state%spec%enabled) return
685
+    if (.not. job_state%configured) return
686
+    if (trigger%kind == FGOF_DEVLOOP_TRIGGER_NONE) return
687
+
688
+    if (job_state%terminal_handoff_required .and. job_state%spec%release_on_handoff) then
689
+      plan%should_release = .true.
690
+    end if
691
+
692
+    if (job_state%running .or. job_state%stopped .or. job_state%cleanup_needed) then
693
+      plan%action = FGOF_DEVLOOP_JOB_ACTION_RESTART
694
+      plan%should_restart = .true.
695
+      plan%should_start = .true.
696
+      plan%should_stop = job_state%spec%stop_before_restart
697
+      plan%reason = "restart existing job"
698
+    else
699
+      plan%action = FGOF_DEVLOOP_JOB_ACTION_START
700
+      plan%should_start = .true.
701
+      plan%reason = "start job"
702
+    end if
703
+  end function devloop_job_restart_plan
704
+
459
   function begin_devloop_cycle(state, trigger) result(cycle)
705
   function begin_devloop_cycle(state, trigger) result(cycle)
460
     type(devloop_state), intent(inout) :: state
706
     type(devloop_state), intent(inout) :: state
461
     type(devloop_trigger), intent(in) :: trigger
707
     type(devloop_trigger), intent(in) :: trigger
@@ -596,6 +842,27 @@ contains
596
     supervision%timed_out = command_result%timed_out
842
     supervision%timed_out = command_result%timed_out
597
   end subroutine record_supervised_command
843
   end subroutine record_supervised_command
598
 
844
 
845
+  subroutine refresh_devloop_job_state(job_state)
846
+    type(devloop_job_state), intent(inout) :: job_state
847
+    logical :: was_released
848
+
849
+    was_released = job_state%released
850
+    job_state%configured = job_is_configured(job_state%handle)
851
+    job_state%attached = job_state%handle%pid > 0
852
+    job_state%pid = job_state%handle%pid
853
+    job_state%process_group = job_state%handle%process_group
854
+    job_state%signal_scope = job_signal_scope(job_state%handle)
855
+    job_state%running = job_is_running(job_state%handle)
856
+    job_state%stopped = job_is_stopped(job_state%handle)
857
+    job_state%finished = job_is_finished(job_state%handle)
858
+    job_state%cleanup_needed = job_needs_cleanup(job_state%handle)
859
+    job_state%owns_process_group = job_owns_process_group(job_state%handle)
860
+    job_state%terminal_handoff_required = job_requires_terminal_handoff(job_state%handle)
861
+    job_state%released = was_released
862
+    job_state%label = ""
863
+    if (allocated(job_state%spec%label)) job_state%label = job_state%spec%label
864
+  end subroutine refresh_devloop_job_state
865
+
599
   subroutine normalize_options(options)
866
   subroutine normalize_options(options)
600
     type(devloop_options), intent(inout) :: options
867
     type(devloop_options), intent(inout) :: options
601
 
868
 
src/fgof_devloop_types.f90modified
@@ -1,4 +1,8 @@
1
 module fgof_devloop_types
1
 module fgof_devloop_types
2
+  use fgof_jobs_types, only : &
3
+    job_handle, &
4
+    job_result, &
5
+    job_spec
2
   use fgof_process_types, only : &
6
   use fgof_process_types, only : &
3
     FGOF_PROCESS_OK, &
7
     FGOF_PROCESS_OK, &
4
     process_command, &
8
     process_command, &
@@ -19,7 +23,14 @@ module fgof_devloop_types
19
   integer, parameter, public :: FGOF_DEVLOOP_COMMAND_BUILD = 1
23
   integer, parameter, public :: FGOF_DEVLOOP_COMMAND_BUILD = 1
20
   integer, parameter, public :: FGOF_DEVLOOP_COMMAND_RUN = 2
24
   integer, parameter, public :: FGOF_DEVLOOP_COMMAND_RUN = 2
21
   integer, parameter, public :: FGOF_DEVLOOP_COMMAND_SMOKE = 3
25
   integer, parameter, public :: FGOF_DEVLOOP_COMMAND_SMOKE = 3
26
+  integer, parameter, public :: FGOF_DEVLOOP_JOB_ACTION_NONE = 0
27
+  integer, parameter, public :: FGOF_DEVLOOP_JOB_ACTION_START = 1
28
+  integer, parameter, public :: FGOF_DEVLOOP_JOB_ACTION_STOP = 2
29
+  integer, parameter, public :: FGOF_DEVLOOP_JOB_ACTION_RESTART = 3
22
 
30
 
31
+  public :: job_handle
32
+  public :: job_result
33
+  public :: job_spec
23
   public :: process_command
34
   public :: process_command
24
   public :: process_options
35
   public :: process_options
25
   public :: process_result
36
   public :: process_result
@@ -116,6 +127,46 @@ module fgof_devloop_types
116
     logical :: timed_out = .false.
127
     logical :: timed_out = .false.
117
   end type devloop_supervision_result
128
   end type devloop_supervision_result
118
 
129
 
130
+  type, public :: devloop_job_spec
131
+    type(job_spec) :: job
132
+    logical :: enabled = .false.
133
+    logical :: stop_before_restart = .true.
134
+    logical :: release_on_handoff = .false.
135
+    character(len=:), allocatable :: label
136
+  end type devloop_job_spec
137
+
138
+  type, public :: devloop_job_state
139
+    type(devloop_job_spec) :: spec
140
+    type(job_handle) :: handle
141
+    integer :: pid = 0
142
+    integer :: process_group = 0
143
+    integer :: signal_scope = 0
144
+    logical :: configured = .false.
145
+    logical :: attached = .false.
146
+    logical :: running = .false.
147
+    logical :: stopped = .false.
148
+    logical :: finished = .false.
149
+    logical :: cleanup_needed = .false.
150
+    logical :: owns_process_group = .false.
151
+    logical :: terminal_handoff_required = .false.
152
+    logical :: released = .false.
153
+    character(len=:), allocatable :: label
154
+  end type devloop_job_state
155
+
156
+  type, public :: devloop_job_plan
157
+    integer :: action = FGOF_DEVLOOP_JOB_ACTION_NONE
158
+    integer :: pid = 0
159
+    integer :: process_group = 0
160
+    integer :: signal_scope = 0
161
+    logical :: should_start = .false.
162
+    logical :: should_stop = .false.
163
+    logical :: should_restart = .false.
164
+    logical :: should_release = .false.
165
+    logical :: cleanup_needed = .false.
166
+    logical :: terminal_handoff_required = .false.
167
+    character(len=:), allocatable :: reason
168
+  end type devloop_job_plan
169
+
119
   type, public :: devloop_state
170
   type, public :: devloop_state
120
     type(devloop_options) :: options
171
     type(devloop_options) :: options
121
     type(devloop_cycle) :: last_cycle
172
     type(devloop_cycle) :: last_cycle