summaryrefslogtreecommitdiffabout
path: root/lib/process_manager.cc
Side-by-side diff
Diffstat (limited to 'lib/process_manager.cc') (more/less context) (ignore whitespace changes)
-rw-r--r--lib/process_manager.cc152
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 @@
+#ifdef USE_PCH
+ #include "pch.h"
+#else
+ #include <sys/types.h>
+ #include <unistd.h>
+ #include <sys/wait.h>
+ #include <signal.h>
+ #include <errno.h>
+ #include <cassert>
+ #include <string>
+ #include <konforka/exception.h>
+ using namespace std;
+ #include "sitecing/process_manager.h"
+#endif
+
+namespace sitecing {
+
+ process_manager::process_manager()
+ : min_children(1), max_children(MAX_SITECING_SCOREBOARD_SLOTS),
+ min_spare_children(0), max_spare_children(-1), finishing(false),
+ die_humbly(false) {
+ }
+ process_manager::~process_manager() {
+ if(die_humbly)
+ return;
+ for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) {
+ scoreboard_slot *sslot = sboard.get_slot(tmp);
+ if((sslot->state!=scoreboard_slot::state_free) && (sslot->pid>0))
+ kill(sslot->pid,SIGTERM);
+ }
+ collect_dead_souls(true);
+ }
+
+ void process_manager::manage() {
+ while(!finishing) {
+ manage_children();
+ // XXX: is it the way it should be?
+ sleep(10);
+ wait_for_children();
+ }
+ collect_dead_souls(true);
+ }
+
+ void process_manager::collect_dead_souls(bool actively) {
+ for(int tries=5;(tries>0) && (sboard.count_slots(scoreboard_slot::state_free)!=MAX_SITECING_SCOREBOARD_SLOTS);tries--) {
+ if(actively) {
+ for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) {
+ scoreboard_slot *sslot = sboard.get_slot(tmp);
+ if((sslot->state!=scoreboard_slot::state_free) && (sslot->pid>0))
+ kill(sslot->pid,SIGTERM);
+ }
+ }
+ wait_for_children(false);
+ // XXX: again.. is it the right way?
+ sleep(1);
+ }
+ }
+
+ void process_manager::wait_for_children(bool hang) {
+ int status;
+ int o = WUNTRACED;
+ if(!hang)
+ o|=WNOHANG;
+ while(sboard.count_slots(scoreboard_slot::state_free)<MAX_SITECING_SCOREBOARD_SLOTS) {
+ pid_t pid = waitpid(-1,&status,o);
+ if(!pid)
+ return;
+ if(pid<0) {
+ if(errno==EINTR)
+ return;
+ throw konforka::exception(CODEPOINT,"failed to waitpid()");
+ }
+ assert(pid);
+ int slot = sboard.get_slot_by_pid(pid);
+ sboard.free_slot(slot);
+ if(hang)
+ return;
+ }
+ }
+
+ void process_manager::manage_children() {
+ if(!spawn_children())
+ kill_children();
+ else
+ sleep(1); // just to get some rest.
+ }
+
+ bool process_manager::spawn_children() {
+ int total_children = 0;
+ int idle_children = 0;
+ for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) {
+ switch(sboard.get_slot(tmp)->state) {
+ case scoreboard_slot::state_free:
+ break;
+ case scoreboard_slot::state_idle:
+ idle_children++;
+ default:
+ total_children++;
+ break;
+ }
+ }
+ int total_lack = 0;
+ if(total_children<min_children)
+ total_lack = min_children-total_children;
+ int idle_lack = 0;
+ if(idle_children<min_spare_children)
+ idle_lack = min_spare_children-idle_children;
+ bool rv = false;
+ for(;(idle_lack>0 || total_lack>0) && (total_children<max_children);idle_lack--,total_lack--,total_children++) {
+ spawn_child();
+ rv = true;
+ }
+ return rv;
+ }
+
+ bool process_manager::kill_children() {
+ int idle_children = 0;
+ for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) {
+ if(sboard.get_slot(tmp)->state==scoreboard_slot::state_idle)
+ idle_children++;
+ }
+ int idle_excess = idle_children-max_spare_children;
+ bool rv = false;
+ for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS && idle_excess>0;tmp++) {
+ scoreboard_slot *sslot = sboard.get_slot(tmp);
+ if((sslot->state==scoreboard_slot::state_idle) && (sslot->pid>0)) {
+ kill(sslot->pid,SIGTERM);
+ idle_excess--;
+ rv = true;
+ }
+ }
+ return rv;
+ }
+
+ void process_manager::spawn_child() {
+ int slot = sboard.allocate_slot();
+ pid_t pid = fork();
+ if(pid<0) {
+ sboard.free_slot(slot);
+ throw konforka::exception(CODEPOINT,"failed to fork()");
+ }
+ if(!pid) {
+ // child
+ sboard.get_slot(slot)->pid = getpid();
+ process(slot);
+ _exit(0);
+ }
+ // parent
+ sboard.get_slot(slot)->pid = pid;
+ }
+
+}