-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 @@ +#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; + } + +} |