summaryrefslogtreecommitdiffabout
path: root/lib/process_manager.cc
blob: 48bcb031ea0ef925b80d3373125c825d4c3e6c36 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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;
    }

}