-rw-r--r-- | lib/process_manager.cc | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/lib/process_manager.cc b/lib/process_manager.cc new file mode 100644 index 0000000..48bcb03 --- a/dev/null +++ b/lib/process_manager.cc | |||
@@ -0,0 +1,152 @@ | |||
1 | #ifdef USE_PCH | ||
2 | #include "pch.h" | ||
3 | #else | ||
4 | #include <sys/types.h> | ||
5 | #include <unistd.h> | ||
6 | #include <sys/wait.h> | ||
7 | #include <signal.h> | ||
8 | #include <errno.h> | ||
9 | #include <cassert> | ||
10 | #include <string> | ||
11 | #include <konforka/exception.h> | ||
12 | using namespace std; | ||
13 | #include "sitecing/process_manager.h" | ||
14 | #endif | ||
15 | |||
16 | namespace sitecing { | ||
17 | |||
18 | process_manager::process_manager() | ||
19 | : min_children(1), max_children(MAX_SITECING_SCOREBOARD_SLOTS), | ||
20 | min_spare_children(0), max_spare_children(-1), finishing(false), | ||
21 | die_humbly(false) { | ||
22 | } | ||
23 | process_manager::~process_manager() { | ||
24 | if(die_humbly) | ||
25 | return; | ||
26 | for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) { | ||
27 | scoreboard_slot *sslot = sboard.get_slot(tmp); | ||
28 | if((sslot->state!=scoreboard_slot::state_free) && (sslot->pid>0)) | ||
29 | kill(sslot->pid,SIGTERM); | ||
30 | } | ||
31 | collect_dead_souls(true); | ||
32 | } | ||
33 | |||
34 | void process_manager::manage() { | ||
35 | while(!finishing) { | ||
36 | manage_children(); | ||
37 | // XXX: is it the way it should be? | ||
38 | sleep(10); | ||
39 | wait_for_children(); | ||
40 | } | ||
41 | collect_dead_souls(true); | ||
42 | } | ||
43 | |||
44 | void process_manager::collect_dead_souls(bool actively) { | ||
45 | for(int tries=5;(tries>0) && (sboard.count_slots(scoreboard_slot::state_free)!=MAX_SITECING_SCOREBOARD_SLOTS);tries--) { | ||
46 | if(actively) { | ||
47 | for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) { | ||
48 | scoreboard_slot *sslot = sboard.get_slot(tmp); | ||
49 | if((sslot->state!=scoreboard_slot::state_free) && (sslot->pid>0)) | ||
50 | kill(sslot->pid,SIGTERM); | ||
51 | } | ||
52 | } | ||
53 | wait_for_children(false); | ||
54 | // XXX: again.. is it the right way? | ||
55 | sleep(1); | ||
56 | } | ||
57 | } | ||
58 | |||
59 | void process_manager::wait_for_children(bool hang) { | ||
60 | int status; | ||
61 | int o = WUNTRACED; | ||
62 | if(!hang) | ||
63 | o|=WNOHANG; | ||
64 | while(sboard.count_slots(scoreboard_slot::state_free)<MAX_SITECING_SCOREBOARD_SLOTS) { | ||
65 | pid_t pid = waitpid(-1,&status,o); | ||
66 | if(!pid) | ||
67 | return; | ||
68 | if(pid<0) { | ||
69 | if(errno==EINTR) | ||
70 | return; | ||
71 | throw konforka::exception(CODEPOINT,"failed to waitpid()"); | ||
72 | } | ||
73 | assert(pid); | ||
74 | int slot = sboard.get_slot_by_pid(pid); | ||
75 | sboard.free_slot(slot); | ||
76 | if(hang) | ||
77 | return; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | void process_manager::manage_children() { | ||
82 | if(!spawn_children()) | ||
83 | kill_children(); | ||
84 | else | ||
85 | sleep(1); // just to get some rest. | ||
86 | } | ||
87 | |||
88 | bool process_manager::spawn_children() { | ||
89 | int total_children = 0; | ||
90 | int idle_children = 0; | ||
91 | for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) { | ||
92 | switch(sboard.get_slot(tmp)->state) { | ||
93 | case scoreboard_slot::state_free: | ||
94 | break; | ||
95 | case scoreboard_slot::state_idle: | ||
96 | idle_children++; | ||
97 | default: | ||
98 | total_children++; | ||
99 | break; | ||
100 | } | ||
101 | } | ||
102 | int total_lack = 0; | ||
103 | if(total_children<min_children) | ||
104 | total_lack = min_children-total_children; | ||
105 | int idle_lack = 0; | ||
106 | if(idle_children<min_spare_children) | ||
107 | idle_lack = min_spare_children-idle_children; | ||
108 | bool rv = false; | ||
109 | for(;(idle_lack>0 || total_lack>0) && (total_children<max_children);idle_lack--,total_lack--,total_children++) { | ||
110 | spawn_child(); | ||
111 | rv = true; | ||
112 | } | ||
113 | return rv; | ||
114 | } | ||
115 | |||
116 | bool process_manager::kill_children() { | ||
117 | int idle_children = 0; | ||
118 | for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) { | ||
119 | if(sboard.get_slot(tmp)->state==scoreboard_slot::state_idle) | ||
120 | idle_children++; | ||
121 | } | ||
122 | int idle_excess = idle_children-max_spare_children; | ||
123 | bool rv = false; | ||
124 | for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS && idle_excess>0;tmp++) { | ||
125 | scoreboard_slot *sslot = sboard.get_slot(tmp); | ||
126 | if((sslot->state==scoreboard_slot::state_idle) && (sslot->pid>0)) { | ||
127 | kill(sslot->pid,SIGTERM); | ||
128 | idle_excess--; | ||
129 | rv = true; | ||
130 | } | ||
131 | } | ||
132 | return rv; | ||
133 | } | ||
134 | |||
135 | void process_manager::spawn_child() { | ||
136 | int slot = sboard.allocate_slot(); | ||
137 | pid_t pid = fork(); | ||
138 | if(pid<0) { | ||
139 | sboard.free_slot(slot); | ||
140 | throw konforka::exception(CODEPOINT,"failed to fork()"); | ||
141 | } | ||
142 | if(!pid) { | ||
143 | // child | ||
144 | sboard.get_slot(slot)->pid = getpid(); | ||
145 | process(slot); | ||
146 | _exit(0); | ||
147 | } | ||
148 | // parent | ||
149 | sboard.get_slot(slot)->pid = pid; | ||
150 | } | ||
151 | |||
152 | } | ||