passenger 5.0.9 → 5.0.10
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of passenger might be problematic. Click here for more details.
- checksums.yaml +8 -8
- checksums.yaml.gz.asc +7 -7
- data.tar.gz.asc +7 -7
- data/CHANGELOG +15 -0
- data/CONTRIBUTORS +6 -0
- data/README.md +1 -1
- data/bin/passenger-install-apache2-module +24 -11
- data/bin/passenger-status +29 -14
- data/build/agents.rb +12 -10
- data/build/cxx_tests.rb +30 -30
- data/doc/Design and Architecture.html +1 -10
- data/doc/Design and Architecture.txt +1 -6
- data/doc/Users guide Apache.html +1 -19
- data/doc/Users guide Apache.txt +1 -1
- data/doc/Users guide Nginx.html +2 -20
- data/doc/Users guide Nginx.txt +2 -2
- data/doc/users_guide_snippets/tips.txt +0 -9
- data/ext/common/ApplicationPool2/ApiKey.h +158 -0
- data/ext/common/ApplicationPool2/BasicGroupInfo.h +81 -0
- data/ext/common/ApplicationPool2/BasicProcessInfo.h +106 -0
- data/ext/common/ApplicationPool2/Common.h +5 -44
- data/ext/common/ApplicationPool2/Context.h +94 -0
- data/ext/common/ApplicationPool2/Group.h +130 -1205
- data/ext/common/ApplicationPool2/Group/InitializationAndShutdown.cpp +190 -0
- data/ext/common/ApplicationPool2/Group/InternalUtils.cpp +329 -0
- data/ext/common/ApplicationPool2/Group/LifetimeAndBasics.cpp +103 -0
- data/ext/common/ApplicationPool2/{Pool/Debug.h → Group/Miscellaneous.cpp} +40 -38
- data/ext/common/ApplicationPool2/Group/OutOfBandWork.cpp +323 -0
- data/ext/common/ApplicationPool2/Group/ProcessListManagement.cpp +606 -0
- data/ext/common/ApplicationPool2/Group/SessionManagement.cpp +337 -0
- data/ext/common/ApplicationPool2/Group/SpawningAndRestarting.cpp +478 -0
- data/ext/common/ApplicationPool2/Group/StateInspection.cpp +197 -0
- data/ext/common/ApplicationPool2/Group/Verification.cpp +159 -0
- data/ext/common/ApplicationPool2/Implementation.cpp +19 -1401
- data/ext/common/ApplicationPool2/Options.h +5 -5
- data/ext/common/ApplicationPool2/Pool.h +260 -815
- data/ext/common/ApplicationPool2/Pool/{AnalyticsCollection.h → AnalyticsCollection.cpp} +55 -56
- data/ext/common/ApplicationPool2/Pool/{GarbageCollection.h → GarbageCollection.cpp} +49 -49
- data/ext/common/ApplicationPool2/Pool/GeneralUtils.cpp +241 -0
- data/ext/common/ApplicationPool2/Pool/GroupUtils.cpp +276 -0
- data/ext/common/ApplicationPool2/Pool/InitializationAndShutdown.cpp +145 -0
- data/ext/common/ApplicationPool2/Pool/Miscellaneous.cpp +244 -0
- data/ext/common/ApplicationPool2/Pool/ProcessUtils.cpp +330 -0
- data/ext/common/ApplicationPool2/Pool/StateInspection.cpp +299 -0
- data/ext/common/ApplicationPool2/Process.h +399 -205
- data/ext/common/ApplicationPool2/Session.h +70 -28
- data/ext/common/ApplicationPool2/Socket.h +1 -0
- data/ext/common/Constants.h +11 -3
- data/ext/common/Exceptions.h +1 -1
- data/ext/common/Logging.cpp +9 -4
- data/ext/common/Logging.h +6 -0
- data/ext/common/ServerKit/HttpServer.h +225 -215
- data/ext/common/ServerKit/Server.h +57 -57
- data/ext/common/SpawningKit/BackgroundIOCapturer.h +160 -0
- data/ext/common/SpawningKit/Config.h +107 -0
- data/ext/common/{ApplicationPool2 → SpawningKit}/DirectSpawner.h +17 -16
- data/ext/common/{ApplicationPool2 → SpawningKit}/DummySpawner.h +33 -33
- data/ext/common/{ApplicationPool2/SpawnerFactory.h → SpawningKit/Factory.h} +17 -17
- data/ext/common/{ApplicationPool2/ComponentInfo.h → SpawningKit/Options.h} +8 -21
- data/ext/common/SpawningKit/PipeWatcher.h +148 -0
- data/ext/common/{ApplicationPool2/PipeWatcher.h → SpawningKit/Result.h} +15 -33
- data/ext/common/{ApplicationPool2 → SpawningKit}/SmartSpawner.h +52 -57
- data/ext/common/{ApplicationPool2 → SpawningKit}/Spawner.h +83 -371
- data/ext/common/SpawningKit/UserSwitchingRules.h +265 -0
- data/ext/common/Utils/BufferedIO.h +24 -0
- data/ext/common/{ApplicationPool2/SpawnObject.h → Utils/ClassUtils.h} +24 -51
- data/ext/common/Utils/IOUtils.cpp +70 -0
- data/ext/common/Utils/IOUtils.h +19 -0
- data/ext/common/Utils/JsonUtils.h +113 -0
- data/ext/common/Utils/StrIntUtils.h +29 -0
- data/ext/common/Utils/json.h +1 -1
- data/ext/common/agents/ApiServerUtils.h +941 -0
- data/ext/common/agents/HelperAgent/{AdminServer.h → ApiServer.h} +163 -365
- data/ext/common/agents/HelperAgent/Main.cpp +86 -88
- data/ext/common/agents/HelperAgent/OptionParser.h +9 -10
- data/ext/common/agents/HelperAgent/RequestHandler/BufferBody.cpp +3 -0
- data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +2 -0
- data/ext/common/agents/HelperAgent/RequestHandler/Hooks.cpp +1 -1
- data/ext/common/agents/HelperAgent/RequestHandler/SendRequest.cpp +2 -2
- data/ext/common/agents/LoggingAgent/ApiServer.h +279 -0
- data/ext/common/agents/LoggingAgent/Main.cpp +41 -51
- data/ext/common/agents/LoggingAgent/OptionParser.h +11 -11
- data/ext/common/agents/Watchdog/ApiServer.h +311 -0
- data/ext/common/agents/Watchdog/Main.cpp +91 -65
- data/helper-scripts/prespawn +2 -0
- data/lib/phusion_passenger.rb +1 -1
- data/lib/phusion_passenger/admin_tools/instance.rb +1 -1
- data/lib/phusion_passenger/common_library.rb +27 -14
- data/lib/phusion_passenger/config/{admin_command_command.rb → api_call_command.rb} +19 -16
- data/lib/phusion_passenger/config/detach_process_command.rb +6 -3
- data/lib/phusion_passenger/config/main.rb +3 -5
- data/lib/phusion_passenger/config/reopen_logs_command.rb +29 -7
- data/lib/phusion_passenger/config/restart_app_command.rb +13 -4
- data/lib/phusion_passenger/config/utils.rb +15 -8
- data/lib/phusion_passenger/constants.rb +6 -2
- data/lib/phusion_passenger/platform_info/apache.rb +4 -0
- data/lib/phusion_passenger/platform_info/apache_detector.rb +18 -3
- data/resources/templates/apache2/mpm_unknown.txt.erb +20 -0
- metadata +42 -21
- metadata.gz.asc +7 -7
- data/ext/common/ApplicationPool2/Pool/GeneralUtils.h +0 -127
- data/ext/common/ApplicationPool2/Pool/Inspection.h +0 -219
- data/ext/common/ApplicationPool2/Pool/ProcessUtils.h +0 -85
- data/ext/common/ApplicationPool2/SuperGroup.h +0 -706
- data/ext/common/agents/LoggingAgent/AdminServer.h +0 -435
- data/ext/common/agents/Watchdog/AdminServer.h +0 -432
@@ -0,0 +1,197 @@
|
|
1
|
+
/*
|
2
|
+
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
+
* Copyright (c) 2011-2015 Phusion
|
4
|
+
*
|
5
|
+
* "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
|
6
|
+
*
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
12
|
+
* furnished to do so, subject to the following conditions:
|
13
|
+
*
|
14
|
+
* The above copyright notice and this permission notice shall be included in
|
15
|
+
* all copies or substantial portions of the Software.
|
16
|
+
*
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
* THE SOFTWARE.
|
24
|
+
*/
|
25
|
+
#include <ApplicationPool2/Group.h>
|
26
|
+
|
27
|
+
/*************************************************************************
|
28
|
+
*
|
29
|
+
* Session management functions for ApplicationPool2::Group
|
30
|
+
*
|
31
|
+
*************************************************************************/
|
32
|
+
|
33
|
+
namespace Passenger {
|
34
|
+
namespace ApplicationPool2 {
|
35
|
+
|
36
|
+
using namespace std;
|
37
|
+
using namespace boost;
|
38
|
+
|
39
|
+
|
40
|
+
/****************************
|
41
|
+
*
|
42
|
+
* Public methods
|
43
|
+
*
|
44
|
+
****************************/
|
45
|
+
|
46
|
+
|
47
|
+
unsigned int
|
48
|
+
Group::getProcessCount() const {
|
49
|
+
return enabledCount + disablingCount + disabledCount;
|
50
|
+
}
|
51
|
+
|
52
|
+
/**
|
53
|
+
* Returns whether the lower bound of the group-specific process limits
|
54
|
+
* have been satisfied. Note that even if the result is false, the pool limits
|
55
|
+
* may not allow spawning, so you should check `pool->atFullCapacity()` too.
|
56
|
+
*/
|
57
|
+
bool
|
58
|
+
Group::processLowerLimitsSatisfied() const {
|
59
|
+
return capacityUsed() >= options.minProcesses;
|
60
|
+
}
|
61
|
+
|
62
|
+
/**
|
63
|
+
* Returns whether the upper bound of the group-specific process limits have
|
64
|
+
* been reached, or surpassed. Does not check whether pool limits have been
|
65
|
+
* reached. Use `pool->atFullCapacity()` to check for that.
|
66
|
+
*/
|
67
|
+
bool
|
68
|
+
Group::processUpperLimitsReached() const {
|
69
|
+
return options.maxProcesses != 0 && capacityUsed() >= options.maxProcesses;
|
70
|
+
}
|
71
|
+
|
72
|
+
/**
|
73
|
+
* Returns whether all enabled processes are totally busy. If so, another
|
74
|
+
* process should be spawned, if allowed by the process limits.
|
75
|
+
* Returns false if there are no enabled processes.
|
76
|
+
*/
|
77
|
+
bool
|
78
|
+
Group::allEnabledProcessesAreTotallyBusy() const {
|
79
|
+
return nEnabledProcessesTotallyBusy == enabledCount;
|
80
|
+
}
|
81
|
+
|
82
|
+
/**
|
83
|
+
* Returns the number of processes in this group that should be part of the
|
84
|
+
* ApplicationPool process limits calculations.
|
85
|
+
*/
|
86
|
+
unsigned int
|
87
|
+
Group::capacityUsed() const {
|
88
|
+
return enabledCount + disablingCount + disabledCount + processesBeingSpawned;
|
89
|
+
}
|
90
|
+
|
91
|
+
/**
|
92
|
+
* Checks whether this group is waiting for capacity on the pool to
|
93
|
+
* become available before it can continue processing requests.
|
94
|
+
*/
|
95
|
+
bool
|
96
|
+
Group::isWaitingForCapacity() const {
|
97
|
+
return enabledProcesses.empty()
|
98
|
+
&& processesBeingSpawned == 0
|
99
|
+
&& !m_restarting
|
100
|
+
&& !getWaitlist.empty();
|
101
|
+
}
|
102
|
+
|
103
|
+
bool
|
104
|
+
Group::garbageCollectable(unsigned long long now) const {
|
105
|
+
/* if (now == 0) {
|
106
|
+
now = SystemTime::getUsec();
|
107
|
+
}
|
108
|
+
return busyness() == 0
|
109
|
+
&& getWaitlist.empty()
|
110
|
+
&& disabledProcesses.empty()
|
111
|
+
&& options.getMaxPreloaderIdleTime() != 0
|
112
|
+
&& now - spawner->lastUsed() >
|
113
|
+
(unsigned long long) options.getMaxPreloaderIdleTime() * 1000000; */
|
114
|
+
return false;
|
115
|
+
}
|
116
|
+
|
117
|
+
void
|
118
|
+
Group::inspectXml(std::ostream &stream, bool includeSecrets) const {
|
119
|
+
ProcessList::const_iterator it;
|
120
|
+
|
121
|
+
stream << "<name>" << escapeForXml(info.name) << "</name>";
|
122
|
+
stream << "<component_name>" << escapeForXml(info.name) << "</component_name>";
|
123
|
+
stream << "<app_root>" << escapeForXml(options.appRoot) << "</app_root>";
|
124
|
+
stream << "<app_type>" << escapeForXml(options.appType) << "</app_type>";
|
125
|
+
stream << "<environment>" << escapeForXml(options.environment) << "</environment>";
|
126
|
+
stream << "<uuid>" << toString(uuid) << "</uuid>";
|
127
|
+
stream << "<enabled_process_count>" << enabledCount << "</enabled_process_count>";
|
128
|
+
stream << "<disabling_process_count>" << disablingCount << "</disabling_process_count>";
|
129
|
+
stream << "<disabled_process_count>" << disabledCount << "</disabled_process_count>";
|
130
|
+
stream << "<capacity_used>" << capacityUsed() << "</capacity_used>";
|
131
|
+
stream << "<get_wait_list_size>" << getWaitlist.size() << "</get_wait_list_size>";
|
132
|
+
stream << "<disable_wait_list_size>" << disableWaitlist.size() << "</disable_wait_list_size>";
|
133
|
+
stream << "<processes_being_spawned>" << processesBeingSpawned << "</processes_being_spawned>";
|
134
|
+
if (m_spawning) {
|
135
|
+
stream << "<spawning/>";
|
136
|
+
}
|
137
|
+
if (restarting()) {
|
138
|
+
stream << "<restarting/>";
|
139
|
+
}
|
140
|
+
if (includeSecrets) {
|
141
|
+
stream << "<secret>" << escapeForXml(getApiKey().toStaticString()) << "</secret>";
|
142
|
+
stream << "<api_key>" << escapeForXml(getApiKey().toStaticString()) << "</api_key>";
|
143
|
+
}
|
144
|
+
LifeStatus lifeStatus = (LifeStatus) this->lifeStatus.load(boost::memory_order_relaxed);
|
145
|
+
switch (lifeStatus) {
|
146
|
+
case ALIVE:
|
147
|
+
stream << "<life_status>ALIVE</life_status>";
|
148
|
+
break;
|
149
|
+
case SHUTTING_DOWN:
|
150
|
+
stream << "<life_status>SHUTTING_DOWN</life_status>";
|
151
|
+
break;
|
152
|
+
case SHUT_DOWN:
|
153
|
+
stream << "<life_status>SHUT_DOWN</life_status>";
|
154
|
+
break;
|
155
|
+
default:
|
156
|
+
P_BUG("Unknown 'lifeStatus' state " << lifeStatus);
|
157
|
+
}
|
158
|
+
|
159
|
+
SpawningKit::UserSwitchingInfo usInfo(SpawningKit::prepareUserSwitching(options));
|
160
|
+
stream << "<user>" << escapeForXml(usInfo.username) << "</user>";
|
161
|
+
stream << "<uid>" << usInfo.uid << "</uid>";
|
162
|
+
stream << "<group>" << escapeForXml(usInfo.groupname) << "</group>";
|
163
|
+
stream << "<gid>" << usInfo.gid << "</gid>";
|
164
|
+
|
165
|
+
stream << "<options>";
|
166
|
+
options.toXml(stream, getResourceLocator());
|
167
|
+
stream << "</options>";
|
168
|
+
|
169
|
+
stream << "<processes>";
|
170
|
+
|
171
|
+
for (it = enabledProcesses.begin(); it != enabledProcesses.end(); it++) {
|
172
|
+
stream << "<process>";
|
173
|
+
(*it)->inspectXml(stream, includeSecrets);
|
174
|
+
stream << "</process>";
|
175
|
+
}
|
176
|
+
for (it = disablingProcesses.begin(); it != disablingProcesses.end(); it++) {
|
177
|
+
stream << "<process>";
|
178
|
+
(*it)->inspectXml(stream, includeSecrets);
|
179
|
+
stream << "</process>";
|
180
|
+
}
|
181
|
+
for (it = disabledProcesses.begin(); it != disabledProcesses.end(); it++) {
|
182
|
+
stream << "<process>";
|
183
|
+
(*it)->inspectXml(stream, includeSecrets);
|
184
|
+
stream << "</process>";
|
185
|
+
}
|
186
|
+
for (it = detachedProcesses.begin(); it != detachedProcesses.end(); it++) {
|
187
|
+
stream << "<process>";
|
188
|
+
(*it)->inspectXml(stream, includeSecrets);
|
189
|
+
stream << "</process>";
|
190
|
+
}
|
191
|
+
|
192
|
+
stream << "</processes>";
|
193
|
+
}
|
194
|
+
|
195
|
+
|
196
|
+
} // namespace ApplicationPool2
|
197
|
+
} // namespace Passenger
|
@@ -0,0 +1,159 @@
|
|
1
|
+
/*
|
2
|
+
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
+
* Copyright (c) 2011-2015 Phusion
|
4
|
+
*
|
5
|
+
* "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
|
6
|
+
*
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
12
|
+
* furnished to do so, subject to the following conditions:
|
13
|
+
*
|
14
|
+
* The above copyright notice and this permission notice shall be included in
|
15
|
+
* all copies or substantial portions of the Software.
|
16
|
+
*
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
* THE SOFTWARE.
|
24
|
+
*/
|
25
|
+
#include <ApplicationPool2/Group.h>
|
26
|
+
|
27
|
+
/*************************************************************************
|
28
|
+
*
|
29
|
+
* Correctness verification functions for ApplicationPool2::Group
|
30
|
+
*
|
31
|
+
*************************************************************************/
|
32
|
+
|
33
|
+
namespace Passenger {
|
34
|
+
namespace ApplicationPool2 {
|
35
|
+
|
36
|
+
using namespace std;
|
37
|
+
using namespace boost;
|
38
|
+
|
39
|
+
|
40
|
+
/****************************
|
41
|
+
*
|
42
|
+
* Private methods
|
43
|
+
*
|
44
|
+
****************************/
|
45
|
+
|
46
|
+
|
47
|
+
bool
|
48
|
+
Group::selfCheckingEnabled() const {
|
49
|
+
return pool->selfchecking;
|
50
|
+
}
|
51
|
+
|
52
|
+
void
|
53
|
+
Group::verifyInvariants() const {
|
54
|
+
// !a || b: logical equivalent of a IMPLIES b.
|
55
|
+
#ifndef NDEBUG
|
56
|
+
if (!selfCheckingEnabled()) {
|
57
|
+
return;
|
58
|
+
}
|
59
|
+
|
60
|
+
LifeStatus lifeStatus = (LifeStatus) this->lifeStatus.load(boost::memory_order_relaxed);
|
61
|
+
|
62
|
+
assert(enabledCount >= 0);
|
63
|
+
assert(disablingCount >= 0);
|
64
|
+
assert(disabledCount >= 0);
|
65
|
+
assert(nEnabledProcessesTotallyBusy >= 0);
|
66
|
+
assert(!( enabledCount == 0 && disablingCount > 0 ) || ( processesBeingSpawned > 0) );
|
67
|
+
assert(!( !m_spawning ) || ( enabledCount > 0 || disablingCount == 0 ));
|
68
|
+
|
69
|
+
assert((lifeStatus == ALIVE) == (spawner != NULL));
|
70
|
+
|
71
|
+
// Verify getWaitlist invariants.
|
72
|
+
assert(!( !getWaitlist.empty() ) || ( enabledProcesses.empty() || verifyNoRequestsOnGetWaitlistAreRoutable() ));
|
73
|
+
assert(!( enabledProcesses.empty() && !m_spawning && !restarting() && !poolAtFullCapacity() ) || ( getWaitlist.empty() ));
|
74
|
+
assert(!( !getWaitlist.empty() ) || ( !enabledProcesses.empty() || m_spawning || restarting() || poolAtFullCapacity() ));
|
75
|
+
|
76
|
+
// Verify disableWaitlist invariants.
|
77
|
+
assert((int) disableWaitlist.size() >= disablingCount);
|
78
|
+
|
79
|
+
// Verify processesBeingSpawned, m_spawning and m_restarting.
|
80
|
+
assert(!( processesBeingSpawned > 0 ) || ( m_spawning ));
|
81
|
+
assert(!( m_restarting ) || ( processesBeingSpawned == 0 ));
|
82
|
+
|
83
|
+
// Verify lifeStatus.
|
84
|
+
if (lifeStatus != ALIVE) {
|
85
|
+
assert(enabledCount == 0);
|
86
|
+
assert(disablingCount == 0);
|
87
|
+
assert(disabledCount == 0);
|
88
|
+
assert(nEnabledProcessesTotallyBusy == 0);
|
89
|
+
}
|
90
|
+
|
91
|
+
// Verify list sizes.
|
92
|
+
assert((int) enabledProcesses.size() == enabledCount);
|
93
|
+
assert((int) disablingProcesses.size() == disablingCount);
|
94
|
+
assert((int) disabledProcesses.size() == disabledCount);
|
95
|
+
assert(nEnabledProcessesTotallyBusy <= enabledCount);
|
96
|
+
#endif
|
97
|
+
}
|
98
|
+
|
99
|
+
void
|
100
|
+
Group::verifyExpensiveInvariants() const {
|
101
|
+
#ifndef NDEBUG
|
102
|
+
// !a || b: logical equivalent of a IMPLIES b.
|
103
|
+
|
104
|
+
if (!selfCheckingEnabled()) {
|
105
|
+
return;
|
106
|
+
}
|
107
|
+
|
108
|
+
ProcessList::const_iterator it, end;
|
109
|
+
|
110
|
+
end = enabledProcesses.end();
|
111
|
+
for (it = enabledProcesses.begin(); it != end; it++) {
|
112
|
+
const ProcessPtr &process = *it;
|
113
|
+
assert(process->enabled == Process::ENABLED);
|
114
|
+
assert(process->isAlive());
|
115
|
+
assert(process->oobwStatus == Process::OOBW_NOT_ACTIVE
|
116
|
+
|| process->oobwStatus == Process::OOBW_REQUESTED);
|
117
|
+
}
|
118
|
+
|
119
|
+
end = disablingProcesses.end();
|
120
|
+
for (it = disablingProcesses.begin(); it != end; it++) {
|
121
|
+
const ProcessPtr &process = *it;
|
122
|
+
assert(process->enabled == Process::DISABLING);
|
123
|
+
assert(process->isAlive());
|
124
|
+
assert(process->oobwStatus == Process::OOBW_NOT_ACTIVE
|
125
|
+
|| process->oobwStatus == Process::OOBW_IN_PROGRESS);
|
126
|
+
}
|
127
|
+
|
128
|
+
end = disabledProcesses.end();
|
129
|
+
for (it = disabledProcesses.begin(); it != end; it++) {
|
130
|
+
const ProcessPtr &process = *it;
|
131
|
+
assert(process->enabled == Process::DISABLED);
|
132
|
+
assert(process->isAlive());
|
133
|
+
assert(process->oobwStatus == Process::OOBW_NOT_ACTIVE
|
134
|
+
|| process->oobwStatus == Process::OOBW_IN_PROGRESS);
|
135
|
+
}
|
136
|
+
|
137
|
+
foreach (const ProcessPtr &process, detachedProcesses) {
|
138
|
+
assert(process->enabled == Process::DETACHED);
|
139
|
+
}
|
140
|
+
#endif
|
141
|
+
}
|
142
|
+
|
143
|
+
#ifndef NDEBUG
|
144
|
+
bool
|
145
|
+
Group::verifyNoRequestsOnGetWaitlistAreRoutable() const {
|
146
|
+
deque<GetWaiter>::const_iterator it, end = getWaitlist.end();
|
147
|
+
|
148
|
+
for (it = getWaitlist.begin(); it != end; it++) {
|
149
|
+
if (route(it->options).process != NULL) {
|
150
|
+
return false;
|
151
|
+
}
|
152
|
+
}
|
153
|
+
return true;
|
154
|
+
}
|
155
|
+
#endif
|
156
|
+
|
157
|
+
|
158
|
+
} // namespace ApplicationPool2
|
159
|
+
} // namespace Passenger
|
@@ -35,10 +35,26 @@
|
|
35
35
|
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
36
36
|
#include <oxt/backtrace.hpp>
|
37
37
|
#include <ApplicationPool2/Pool.h>
|
38
|
-
#include <ApplicationPool2/SuperGroup.h>
|
39
38
|
#include <ApplicationPool2/Group.h>
|
40
|
-
#include <ApplicationPool2/PipeWatcher.h>
|
41
39
|
#include <ApplicationPool2/ErrorRenderer.h>
|
40
|
+
#include <ApplicationPool2/Pool/InitializationAndShutdown.cpp>
|
41
|
+
#include <ApplicationPool2/Pool/AnalyticsCollection.cpp>
|
42
|
+
#include <ApplicationPool2/Pool/GarbageCollection.cpp>
|
43
|
+
#include <ApplicationPool2/Pool/GeneralUtils.cpp>
|
44
|
+
#include <ApplicationPool2/Pool/GroupUtils.cpp>
|
45
|
+
#include <ApplicationPool2/Pool/ProcessUtils.cpp>
|
46
|
+
#include <ApplicationPool2/Pool/StateInspection.cpp>
|
47
|
+
#include <ApplicationPool2/Pool/Miscellaneous.cpp>
|
48
|
+
#include <ApplicationPool2/Group/InitializationAndShutdown.cpp>
|
49
|
+
#include <ApplicationPool2/Group/LifetimeAndBasics.cpp>
|
50
|
+
#include <ApplicationPool2/Group/SessionManagement.cpp>
|
51
|
+
#include <ApplicationPool2/Group/SpawningAndRestarting.cpp>
|
52
|
+
#include <ApplicationPool2/Group/ProcessListManagement.cpp>
|
53
|
+
#include <ApplicationPool2/Group/OutOfBandWork.cpp>
|
54
|
+
#include <ApplicationPool2/Group/Miscellaneous.cpp>
|
55
|
+
#include <ApplicationPool2/Group/InternalUtils.cpp>
|
56
|
+
#include <ApplicationPool2/Group/StateInspection.cpp>
|
57
|
+
#include <ApplicationPool2/Group/Verification.cpp>
|
42
58
|
#include <Exceptions.h>
|
43
59
|
#include <Hooks.h>
|
44
60
|
#include <MessageReadersWriters.h>
|
@@ -148,7 +164,7 @@ rethrowException(const ExceptionPtr &e) {
|
|
148
164
|
}
|
149
165
|
|
150
166
|
void processAndLogNewSpawnException(SpawnException &e, const Options &options,
|
151
|
-
const
|
167
|
+
const SpawningKit::ConfigPtr &config)
|
152
168
|
{
|
153
169
|
TRACE_POINT();
|
154
170
|
UnionStation::TransactionPtr transaction;
|
@@ -307,1303 +323,6 @@ recreateString(psg_pool_t *pool, StaticString &str) {
|
|
307
323
|
}
|
308
324
|
|
309
325
|
|
310
|
-
const SuperGroupPtr
|
311
|
-
Pool::getSuperGroup(const char *name) {
|
312
|
-
SuperGroupPtr *superGroup;
|
313
|
-
if (superGroups.lookup(name, &superGroup)) {
|
314
|
-
return *superGroup;
|
315
|
-
} else {
|
316
|
-
return SuperGroupPtr();
|
317
|
-
}
|
318
|
-
}
|
319
|
-
|
320
|
-
|
321
|
-
boost::mutex &
|
322
|
-
SuperGroup::getPoolSyncher(Pool *pool) {
|
323
|
-
return pool->syncher;
|
324
|
-
}
|
325
|
-
|
326
|
-
void
|
327
|
-
SuperGroup::runAllActions(const boost::container::vector<Callback> &actions) {
|
328
|
-
Pool::runAllActions(actions);
|
329
|
-
}
|
330
|
-
|
331
|
-
const PoolPtr
|
332
|
-
SuperGroup::getPoolPtr() {
|
333
|
-
return getPool()->shared_from_this();
|
334
|
-
}
|
335
|
-
|
336
|
-
string
|
337
|
-
SuperGroup::generateSecret() const {
|
338
|
-
return getPool()->getRandomGenerator()->generateAsciiString(43);
|
339
|
-
}
|
340
|
-
|
341
|
-
bool
|
342
|
-
SuperGroup::selfCheckingEnabled() const {
|
343
|
-
return getPool()->selfchecking;
|
344
|
-
}
|
345
|
-
|
346
|
-
void
|
347
|
-
SuperGroup::runInitializationHooks() const {
|
348
|
-
getPool()->runHookScripts("after_initialize_supergroup",
|
349
|
-
boost::bind(&SuperGroup::setupInitializationOrDestructionHook, this, _1));
|
350
|
-
}
|
351
|
-
|
352
|
-
void
|
353
|
-
SuperGroup::runDestructionHooks() const {
|
354
|
-
getPool()->runHookScripts("before_destroy_supergroup",
|
355
|
-
boost::bind(&SuperGroup::setupInitializationOrDestructionHook, this, _1));
|
356
|
-
}
|
357
|
-
|
358
|
-
void
|
359
|
-
SuperGroup::setupInitializationOrDestructionHook(HookScriptOptions &options) const {
|
360
|
-
options.environment.push_back(make_pair("PASSENGER_APP_ROOT", this->options.appRoot));
|
361
|
-
}
|
362
|
-
|
363
|
-
void
|
364
|
-
SuperGroup::createInterruptableThread(const boost::function<void ()> &func, const string &name,
|
365
|
-
unsigned int stackSize)
|
366
|
-
{
|
367
|
-
getPool()->interruptableThreads.create_thread(func, name, stackSize);
|
368
|
-
}
|
369
|
-
|
370
|
-
void
|
371
|
-
SuperGroup::realDoInitialize(const Options &options, unsigned int generation) {
|
372
|
-
vector<ComponentInfo> componentInfos;
|
373
|
-
vector<ComponentInfo>::const_iterator it;
|
374
|
-
ExceptionPtr exception;
|
375
|
-
|
376
|
-
Pool *pool = getPool();
|
377
|
-
|
378
|
-
P_TRACE(2, "Initializing SuperGroup " << inspect() << " in the background...");
|
379
|
-
try {
|
380
|
-
componentInfos = loadComponentInfos(options);
|
381
|
-
} catch (const tracable_exception &e) {
|
382
|
-
exception = copyException(e);
|
383
|
-
}
|
384
|
-
if (componentInfos.empty() && exception == NULL) {
|
385
|
-
string message = "The directory " +
|
386
|
-
options.appRoot +
|
387
|
-
" does not seem to contain a web application.";
|
388
|
-
boost::shared_ptr<SpawnException> spawnException =
|
389
|
-
boost::make_shared<SpawnException>(
|
390
|
-
message, message, false);
|
391
|
-
exception = spawnException;
|
392
|
-
processAndLogNewSpawnException(*spawnException, options,
|
393
|
-
pool->getSpawnerConfig());
|
394
|
-
}
|
395
|
-
|
396
|
-
Pool::DebugSupportPtr debug = pool->debugSupport;
|
397
|
-
boost::container::vector<Callback> actions;
|
398
|
-
{
|
399
|
-
if (debug != NULL && debug->superGroup) {
|
400
|
-
debug->debugger->send("About to finish SuperGroup initialization");
|
401
|
-
debug->messages->recv("Proceed with initializing SuperGroup");
|
402
|
-
}
|
403
|
-
|
404
|
-
boost::unique_lock<boost::mutex> lock(getPoolSyncher(pool));
|
405
|
-
this_thread::disable_interruption di;
|
406
|
-
this_thread::disable_syscall_interruption dsi;
|
407
|
-
NOT_EXPECTING_EXCEPTIONS();
|
408
|
-
if (OXT_UNLIKELY(getPool() == NULL || generation != this->generation)) {
|
409
|
-
return;
|
410
|
-
}
|
411
|
-
P_TRACE(2, "Initialization of SuperGroup " << inspect() << " almost done; grabbed lock");
|
412
|
-
assert(state == INITIALIZING);
|
413
|
-
verifyInvariants();
|
414
|
-
|
415
|
-
if (componentInfos.empty()) {
|
416
|
-
/* Somehow initialization failed. Maybe something has deleted
|
417
|
-
* the supergroup files while we're working.
|
418
|
-
*/
|
419
|
-
assert(exception != NULL);
|
420
|
-
setState(DESTROYED);
|
421
|
-
|
422
|
-
actions.reserve(getWaitlist.size());
|
423
|
-
while (!getWaitlist.empty()) {
|
424
|
-
const GetWaiter &waiter = getWaitlist.front();
|
425
|
-
actions.push_back(boost::bind(GetCallback::call,
|
426
|
-
waiter.callback, SessionPtr(), exception));
|
427
|
-
getWaitlist.pop_front();
|
428
|
-
}
|
429
|
-
} else {
|
430
|
-
for (it = componentInfos.begin(); it != componentInfos.end(); it++) {
|
431
|
-
const ComponentInfo &info = *it;
|
432
|
-
GroupPtr group = boost::make_shared<Group>(this,
|
433
|
-
options, info);
|
434
|
-
group->initialize();
|
435
|
-
groups.push_back(group);
|
436
|
-
if (info.isDefault) {
|
437
|
-
defaultGroup = group.get();
|
438
|
-
}
|
439
|
-
}
|
440
|
-
|
441
|
-
setState(READY);
|
442
|
-
assignGetWaitlistToGroups(actions);
|
443
|
-
}
|
444
|
-
|
445
|
-
verifyInvariants();
|
446
|
-
P_TRACE(2, "Done initializing SuperGroup " << inspect());
|
447
|
-
}
|
448
|
-
|
449
|
-
this_thread::disable_interruption di;
|
450
|
-
this_thread::disable_syscall_interruption dsi;
|
451
|
-
runAllActions(actions);
|
452
|
-
runInitializationHooks();
|
453
|
-
}
|
454
|
-
|
455
|
-
void
|
456
|
-
SuperGroup::realDoRestart(const Options &options, unsigned int generation) {
|
457
|
-
TRACE_POINT();
|
458
|
-
vector<ComponentInfo> componentInfos = loadComponentInfos(options);
|
459
|
-
vector<ComponentInfo>::const_iterator it;
|
460
|
-
|
461
|
-
Pool *pool = getPool();
|
462
|
-
Pool::DebugSupportPtr debug = pool->debugSupport;
|
463
|
-
if (debug != NULL && debug->superGroup) {
|
464
|
-
debug->debugger->send("About to finish SuperGroup restart");
|
465
|
-
debug->messages->recv("Proceed with restarting SuperGroup");
|
466
|
-
}
|
467
|
-
|
468
|
-
boost::unique_lock<boost::mutex> lock(getPoolSyncher(pool));
|
469
|
-
if (OXT_UNLIKELY(this->generation != generation)) {
|
470
|
-
return;
|
471
|
-
}
|
472
|
-
|
473
|
-
assert(state == RESTARTING);
|
474
|
-
verifyInvariants();
|
475
|
-
|
476
|
-
SuperGroup::GroupList allGroups;
|
477
|
-
SuperGroup::GroupList updatedGroups;
|
478
|
-
SuperGroup::GroupList newGroups;
|
479
|
-
SuperGroup::GroupList::const_iterator g_it;
|
480
|
-
boost::container::vector<Callback> actions;
|
481
|
-
this->options = options;
|
482
|
-
|
483
|
-
// Update the component information for existing groups.
|
484
|
-
UPDATE_TRACE_POINT();
|
485
|
-
for (it = componentInfos.begin(); it != componentInfos.end(); it++) {
|
486
|
-
const ComponentInfo &info = *it;
|
487
|
-
pair<GroupPtr, unsigned int> result =
|
488
|
-
findGroupCorrespondingToComponent(groups, info);
|
489
|
-
GroupPtr group = result.first;
|
490
|
-
if (group != NULL) {
|
491
|
-
unsigned int index = result.second;
|
492
|
-
group->componentInfo = info;
|
493
|
-
updatedGroups.push_back(group);
|
494
|
-
groups[index].reset();
|
495
|
-
} else {
|
496
|
-
// This is not an existing group but a new one,
|
497
|
-
// so create it.
|
498
|
-
group = boost::make_shared<Group>(this,
|
499
|
-
options, info);
|
500
|
-
group->initialize();
|
501
|
-
newGroups.push_back(group);
|
502
|
-
}
|
503
|
-
// allGroups must be in the same order as componentInfos.
|
504
|
-
allGroups.push_back(group);
|
505
|
-
}
|
506
|
-
|
507
|
-
// Some components might have been deleted, so delete the
|
508
|
-
// corresponding groups.
|
509
|
-
detachAllGroups(groups, actions);
|
510
|
-
|
511
|
-
// Tell all previous existing groups to restart.
|
512
|
-
for (g_it = updatedGroups.begin(); g_it != updatedGroups.end(); g_it++) {
|
513
|
-
GroupPtr group = *g_it;
|
514
|
-
group->restart(options);
|
515
|
-
}
|
516
|
-
|
517
|
-
groups = allGroups;
|
518
|
-
defaultGroup = findDefaultGroup(allGroups);
|
519
|
-
setState(READY);
|
520
|
-
assignGetWaitlistToGroups(actions);
|
521
|
-
|
522
|
-
UPDATE_TRACE_POINT();
|
523
|
-
verifyInvariants();
|
524
|
-
lock.unlock();
|
525
|
-
runAllActions(actions);
|
526
|
-
}
|
527
|
-
|
528
|
-
|
529
|
-
Group::Group(SuperGroup *_superGroup, const Options &_options, const ComponentInfo &info)
|
530
|
-
: superGroup(_superGroup),
|
531
|
-
name(_superGroup->name + "#" + info.name),
|
532
|
-
uuid(generateUuid(_superGroup)),
|
533
|
-
componentInfo(info)
|
534
|
-
{
|
535
|
-
generateSecret(_superGroup, secret);
|
536
|
-
resetOptions(_options);
|
537
|
-
enabledCount = 0;
|
538
|
-
disablingCount = 0;
|
539
|
-
disabledCount = 0;
|
540
|
-
nEnabledProcessesTotallyBusy = 0;
|
541
|
-
spawner = getPool()->spawnerFactory->create(options);
|
542
|
-
restartsInitiated = 0;
|
543
|
-
processesBeingSpawned = 0;
|
544
|
-
m_spawning = false;
|
545
|
-
m_restarting = false;
|
546
|
-
lifeStatus.store(ALIVE, boost::memory_order_relaxed);
|
547
|
-
lastRestartFileMtime = 0;
|
548
|
-
lastRestartFileCheckTime = 0;
|
549
|
-
alwaysRestartFileExists = false;
|
550
|
-
if (options.restartDir.empty()) {
|
551
|
-
restartFile = options.appRoot + "/tmp/restart.txt";
|
552
|
-
alwaysRestartFile = options.appRoot + "/tmp/always_restart.txt";
|
553
|
-
} else if (options.restartDir[0] == '/') {
|
554
|
-
restartFile = options.restartDir + "/restart.txt";
|
555
|
-
alwaysRestartFile = options.restartDir + "/always_restart.txt";
|
556
|
-
} else {
|
557
|
-
restartFile = options.appRoot + "/" + options.restartDir + "/restart.txt";
|
558
|
-
alwaysRestartFile = options.appRoot + "/" + options.restartDir + "/always_restart.txt";
|
559
|
-
}
|
560
|
-
|
561
|
-
detachedProcessesCheckerActive = false;
|
562
|
-
}
|
563
|
-
|
564
|
-
Group::~Group() {
|
565
|
-
LifeStatus lifeStatus = getLifeStatus();
|
566
|
-
if (OXT_UNLIKELY(lifeStatus == ALIVE)) {
|
567
|
-
P_BUG("You must call Group::shutdown() before destroying a Group.");
|
568
|
-
}
|
569
|
-
assert(lifeStatus == SHUT_DOWN);
|
570
|
-
assert(!detachedProcessesCheckerActive);
|
571
|
-
assert(getWaitlist.empty());
|
572
|
-
}
|
573
|
-
|
574
|
-
void
|
575
|
-
Group::initialize() {
|
576
|
-
nullProcess = boost::make_shared<Process>(
|
577
|
-
0, StaticString(),
|
578
|
-
FileDescriptor(), FileDescriptor(),
|
579
|
-
SocketList(), 0, 0);
|
580
|
-
nullProcess->dummy = true;
|
581
|
-
nullProcess->requiresShutdown = false;
|
582
|
-
nullProcess->setGroup(this);
|
583
|
-
}
|
584
|
-
|
585
|
-
OXT_FORCE_INLINE Pool *
|
586
|
-
Group::getPool() const {
|
587
|
-
return getSuperGroup()->getPool();
|
588
|
-
}
|
589
|
-
|
590
|
-
void
|
591
|
-
Group::_onSessionInitiateFailure(Session *session) {
|
592
|
-
Process *process = session->getProcess();
|
593
|
-
assert(process != NULL);
|
594
|
-
process->getGroup()->onSessionInitiateFailure(process, session);
|
595
|
-
}
|
596
|
-
|
597
|
-
void
|
598
|
-
Group::_onSessionClose(Session *session) {
|
599
|
-
Process *process = session->getProcess();
|
600
|
-
assert(process != NULL);
|
601
|
-
process->getGroup()->onSessionClose(process, session);
|
602
|
-
}
|
603
|
-
|
604
|
-
OXT_FORCE_INLINE void
|
605
|
-
Group::onSessionInitiateFailure(Process *process, Session *session) {
|
606
|
-
boost::container::vector<Callback> actions;
|
607
|
-
|
608
|
-
TRACE_POINT();
|
609
|
-
// Standard resource management boilerplate stuff...
|
610
|
-
Pool *pool = getPool();
|
611
|
-
boost::unique_lock<boost::mutex> lock(pool->syncher);
|
612
|
-
assert(process->isAlive());
|
613
|
-
assert(isAlive() || getLifeStatus() == SHUTTING_DOWN);
|
614
|
-
|
615
|
-
UPDATE_TRACE_POINT();
|
616
|
-
P_DEBUG("Could not initiate a session with process " <<
|
617
|
-
process->inspect() << ", detaching from pool if possible");
|
618
|
-
if (!pool->detachProcessUnlocked(process->shared_from_this(), actions)) {
|
619
|
-
P_DEBUG("Process was already detached");
|
620
|
-
}
|
621
|
-
pool->fullVerifyInvariants();
|
622
|
-
lock.unlock();
|
623
|
-
runAllActions(actions);
|
624
|
-
}
|
625
|
-
|
626
|
-
OXT_FORCE_INLINE void
|
627
|
-
Group::onSessionClose(Process *process, Session *session) {
|
628
|
-
TRACE_POINT();
|
629
|
-
// Standard resource management boilerplate stuff...
|
630
|
-
Pool *pool = getPool();
|
631
|
-
boost::unique_lock<boost::mutex> lock(pool->syncher);
|
632
|
-
assert(process->isAlive());
|
633
|
-
assert(isAlive() || getLifeStatus() == SHUTTING_DOWN);
|
634
|
-
|
635
|
-
P_TRACE(2, "Session closed for process " << process->inspect());
|
636
|
-
verifyInvariants();
|
637
|
-
UPDATE_TRACE_POINT();
|
638
|
-
|
639
|
-
/* Update statistics. */
|
640
|
-
bool wasTotallyBusy = process->isTotallyBusy();
|
641
|
-
process->sessionClosed(session);
|
642
|
-
assert(process->getLifeStatus() == Process::ALIVE);
|
643
|
-
assert(process->enabled == Process::ENABLED
|
644
|
-
|| process->enabled == Process::DISABLING
|
645
|
-
|| process->enabled == Process::DETACHED);
|
646
|
-
if (process->enabled == Process::ENABLED) {
|
647
|
-
enabledProcessBusynessLevels[process->index] = process->busyness();
|
648
|
-
if (wasTotallyBusy) {
|
649
|
-
assert(nEnabledProcessesTotallyBusy >= 1);
|
650
|
-
nEnabledProcessesTotallyBusy--;
|
651
|
-
}
|
652
|
-
}
|
653
|
-
|
654
|
-
/* This group now has a process that's guaranteed to be not
|
655
|
-
* totally busy.
|
656
|
-
*/
|
657
|
-
assert(!process->isTotallyBusy());
|
658
|
-
|
659
|
-
bool detachingBecauseOfMaxRequests = false;
|
660
|
-
bool detachingBecauseCapacityNeeded = false;
|
661
|
-
bool shouldDetach =
|
662
|
-
( detachingBecauseOfMaxRequests = (
|
663
|
-
options.maxRequests > 0
|
664
|
-
&& process->processed >= options.maxRequests
|
665
|
-
)) || (
|
666
|
-
detachingBecauseCapacityNeeded = (
|
667
|
-
process->sessions == 0
|
668
|
-
&& getWaitlist.empty()
|
669
|
-
&& (
|
670
|
-
!pool->getWaitlist.empty()
|
671
|
-
|| anotherGroupIsWaitingForCapacity()
|
672
|
-
)
|
673
|
-
)
|
674
|
-
);
|
675
|
-
bool shouldDisable =
|
676
|
-
process->enabled == Process::DISABLING
|
677
|
-
&& process->sessions == 0
|
678
|
-
&& enabledCount > 0;
|
679
|
-
|
680
|
-
if (shouldDetach || shouldDisable) {
|
681
|
-
UPDATE_TRACE_POINT();
|
682
|
-
boost::container::vector<Callback> actions;
|
683
|
-
|
684
|
-
if (shouldDetach) {
|
685
|
-
if (detachingBecauseCapacityNeeded) {
|
686
|
-
/* Someone might be trying to get() a session for a different
|
687
|
-
* group that couldn't be spawned because of lack of pool capacity.
|
688
|
-
* If this group isn't under sufficiently load (as apparent by the
|
689
|
-
* checked conditions) then now's a good time to detach
|
690
|
-
* this process or group in order to free capacity.
|
691
|
-
*/
|
692
|
-
P_DEBUG("Process " << process->inspect() << " is no longer totally "
|
693
|
-
"busy; detaching it in order to make room in the pool");
|
694
|
-
} else {
|
695
|
-
/* This process has processed its maximum number of requests,
|
696
|
-
* so we detach it.
|
697
|
-
*/
|
698
|
-
P_DEBUG("Process " << process->inspect() <<
|
699
|
-
" has reached its maximum number of requests (" <<
|
700
|
-
options.maxRequests << "); detaching it");
|
701
|
-
}
|
702
|
-
pool->detachProcessUnlocked(process->shared_from_this(), actions);
|
703
|
-
} else {
|
704
|
-
ProcessPtr processPtr = process->shared_from_this();
|
705
|
-
removeProcessFromList(processPtr, disablingProcesses);
|
706
|
-
addProcessToList(processPtr, disabledProcesses);
|
707
|
-
removeFromDisableWaitlist(processPtr, DR_SUCCESS, actions);
|
708
|
-
maybeInitiateOobw(process);
|
709
|
-
}
|
710
|
-
|
711
|
-
pool->fullVerifyInvariants();
|
712
|
-
lock.unlock();
|
713
|
-
runAllActions(actions);
|
714
|
-
|
715
|
-
} else {
|
716
|
-
UPDATE_TRACE_POINT();
|
717
|
-
|
718
|
-
// This could change process->enabled.
|
719
|
-
maybeInitiateOobw(process);
|
720
|
-
|
721
|
-
if (!getWaitlist.empty() && process->enabled == Process::ENABLED) {
|
722
|
-
/* If there are clients on this group waiting for a process to
|
723
|
-
* become available then call them now.
|
724
|
-
*/
|
725
|
-
UPDATE_TRACE_POINT();
|
726
|
-
// Already calls verifyInvariants().
|
727
|
-
assignSessionsToGetWaitersQuickly(lock);
|
728
|
-
}
|
729
|
-
}
|
730
|
-
}
|
731
|
-
|
732
|
-
void
|
733
|
-
Group::requestOOBW(const ProcessPtr &process) {
|
734
|
-
// Standard resource management boilerplate stuff...
|
735
|
-
Pool *pool = getPool();
|
736
|
-
boost::unique_lock<boost::mutex> lock(pool->syncher);
|
737
|
-
if (isAlive() && process->isAlive() && process->oobwStatus == Process::OOBW_NOT_ACTIVE) {
|
738
|
-
process->oobwStatus = Process::OOBW_REQUESTED;
|
739
|
-
}
|
740
|
-
}
|
741
|
-
|
742
|
-
bool
|
743
|
-
Group::oobwAllowed() const {
|
744
|
-
unsigned int oobwInstances = 0;
|
745
|
-
foreach (const ProcessPtr &process, disablingProcesses) {
|
746
|
-
if (process->oobwStatus == Process::OOBW_IN_PROGRESS) {
|
747
|
-
oobwInstances += 1;
|
748
|
-
}
|
749
|
-
}
|
750
|
-
foreach (const ProcessPtr &process, disabledProcesses) {
|
751
|
-
if (process->oobwStatus == Process::OOBW_IN_PROGRESS) {
|
752
|
-
oobwInstances += 1;
|
753
|
-
}
|
754
|
-
}
|
755
|
-
return oobwInstances < options.maxOutOfBandWorkInstances;
|
756
|
-
}
|
757
|
-
|
758
|
-
bool
|
759
|
-
Group::shouldInitiateOobw(Process *process) const {
|
760
|
-
return process->oobwStatus == Process::OOBW_REQUESTED
|
761
|
-
&& process->enabled != Process::DETACHED
|
762
|
-
&& process->isAlive()
|
763
|
-
&& oobwAllowed();
|
764
|
-
}
|
765
|
-
|
766
|
-
void
|
767
|
-
Group::maybeInitiateOobw(Process *process) {
|
768
|
-
if (shouldInitiateOobw(process)) {
|
769
|
-
// We keep an extra reference to prevent premature destruction.
|
770
|
-
ProcessPtr p = process->shared_from_this();
|
771
|
-
initiateOobw(p);
|
772
|
-
}
|
773
|
-
}
|
774
|
-
|
775
|
-
// The 'self' parameter is for keeping the current Group object alive
|
776
|
-
void
|
777
|
-
Group::lockAndMaybeInitiateOobw(const ProcessPtr &process, DisableResult result, GroupPtr self) {
|
778
|
-
TRACE_POINT();
|
779
|
-
|
780
|
-
// Standard resource management boilerplate stuff...
|
781
|
-
Pool *pool = getPool();
|
782
|
-
boost::unique_lock<boost::mutex> lock(pool->syncher);
|
783
|
-
if (OXT_UNLIKELY(!process->isAlive() || !isAlive())) {
|
784
|
-
return;
|
785
|
-
}
|
786
|
-
|
787
|
-
assert(process->oobwStatus == Process::OOBW_IN_PROGRESS);
|
788
|
-
|
789
|
-
if (result == DR_SUCCESS) {
|
790
|
-
if (process->enabled == Process::DISABLED) {
|
791
|
-
P_DEBUG("Process " << process->inspect() << " disabled; proceeding " <<
|
792
|
-
"with out-of-band work");
|
793
|
-
process->oobwStatus = Process::OOBW_REQUESTED;
|
794
|
-
if (shouldInitiateOobw(process.get())) {
|
795
|
-
initiateOobw(process);
|
796
|
-
} else {
|
797
|
-
// We do not re-enable the process because it's likely that the
|
798
|
-
// administrator has explicitly changed the state.
|
799
|
-
P_DEBUG("Out-of-band work for process " << process->inspect() << " aborted "
|
800
|
-
"because the process no longer requests out-of-band work");
|
801
|
-
process->oobwStatus = Process::OOBW_NOT_ACTIVE;
|
802
|
-
}
|
803
|
-
} else {
|
804
|
-
// We do not re-enable the process because it's likely that the
|
805
|
-
// administrator has explicitly changed the state.
|
806
|
-
P_DEBUG("Out-of-band work for process " << process->inspect() << " aborted "
|
807
|
-
"because the process was reenabled after disabling");
|
808
|
-
process->oobwStatus = Process::OOBW_NOT_ACTIVE;
|
809
|
-
}
|
810
|
-
} else {
|
811
|
-
P_DEBUG("Out-of-band work for process " << process->inspect() << " aborted "
|
812
|
-
"because the process could not be disabled");
|
813
|
-
process->oobwStatus = Process::OOBW_NOT_ACTIVE;
|
814
|
-
}
|
815
|
-
}
|
816
|
-
|
817
|
-
void
|
818
|
-
Group::initiateOobw(const ProcessPtr &process) {
|
819
|
-
assert(process->oobwStatus == Process::OOBW_REQUESTED);
|
820
|
-
|
821
|
-
process->oobwStatus = Process::OOBW_IN_PROGRESS;
|
822
|
-
|
823
|
-
if (process->enabled == Process::ENABLED
|
824
|
-
|| process->enabled == Process::DISABLING)
|
825
|
-
{
|
826
|
-
// We want the process to be disabled. However, disabling a process is potentially
|
827
|
-
// asynchronous, so we pass a callback which will re-aquire the lock and call this
|
828
|
-
// method again.
|
829
|
-
P_DEBUG("Disabling process " << process->inspect() << " in preparation for OOBW");
|
830
|
-
DisableResult result = disable(process,
|
831
|
-
boost::bind(&Group::lockAndMaybeInitiateOobw, this,
|
832
|
-
_1, _2, shared_from_this()));
|
833
|
-
switch (result) {
|
834
|
-
case DR_SUCCESS:
|
835
|
-
// Continue code flow.
|
836
|
-
break;
|
837
|
-
case DR_DEFERRED:
|
838
|
-
// lockAndMaybeInitiateOobw() will eventually be called.
|
839
|
-
return;
|
840
|
-
case DR_ERROR:
|
841
|
-
case DR_NOOP:
|
842
|
-
P_DEBUG("Out-of-band work for process " << process->inspect() << " aborted "
|
843
|
-
"because the process could not be disabled");
|
844
|
-
process->oobwStatus = Process::OOBW_NOT_ACTIVE;
|
845
|
-
return;
|
846
|
-
default:
|
847
|
-
P_BUG("Unexpected disable() result " << result);
|
848
|
-
}
|
849
|
-
}
|
850
|
-
|
851
|
-
assert(process->enabled == Process::DISABLED);
|
852
|
-
assert(process->sessions == 0);
|
853
|
-
|
854
|
-
P_DEBUG("Initiating OOBW request for process " << process->inspect());
|
855
|
-
interruptableThreads.create_thread(
|
856
|
-
boost::bind(&Group::spawnThreadOOBWRequest, this, shared_from_this(), process),
|
857
|
-
"OOBW request thread for process " + process->inspect(),
|
858
|
-
POOL_HELPER_THREAD_STACK_SIZE);
|
859
|
-
}
|
860
|
-
|
861
|
-
// The 'self' parameter is for keeping the current Group object alive while this thread is running.
|
862
|
-
void
|
863
|
-
Group::spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process) {
|
864
|
-
TRACE_POINT();
|
865
|
-
this_thread::disable_interruption di;
|
866
|
-
this_thread::disable_syscall_interruption dsi;
|
867
|
-
|
868
|
-
Socket *socket;
|
869
|
-
Connection connection;
|
870
|
-
Pool *pool = getPool();
|
871
|
-
Pool::DebugSupportPtr debug = pool->debugSupport;
|
872
|
-
|
873
|
-
UPDATE_TRACE_POINT();
|
874
|
-
P_DEBUG("Performing OOBW request for process " << process->inspect());
|
875
|
-
if (debug != NULL && debug->oobw) {
|
876
|
-
debug->debugger->send("OOBW request about to start");
|
877
|
-
debug->messages->recv("Proceed with OOBW request");
|
878
|
-
}
|
879
|
-
|
880
|
-
UPDATE_TRACE_POINT();
|
881
|
-
{
|
882
|
-
// Standard resource management boilerplate stuff...
|
883
|
-
boost::unique_lock<boost::mutex> lock(pool->syncher);
|
884
|
-
if (OXT_UNLIKELY(!process->isAlive()
|
885
|
-
|| process->enabled == Process::DETACHED
|
886
|
-
|| !isAlive()))
|
887
|
-
{
|
888
|
-
return;
|
889
|
-
}
|
890
|
-
|
891
|
-
if (process->enabled != Process::DISABLED) {
|
892
|
-
UPDATE_TRACE_POINT();
|
893
|
-
P_INFO("Out-of-Band Work canceled: process " << process->inspect() <<
|
894
|
-
" was concurrently re-enabled.");
|
895
|
-
if (debug != NULL && debug->oobw) {
|
896
|
-
debug->debugger->send("OOBW request canceled");
|
897
|
-
}
|
898
|
-
return;
|
899
|
-
}
|
900
|
-
|
901
|
-
assert(process->oobwStatus == Process::OOBW_IN_PROGRESS);
|
902
|
-
assert(process->sessions == 0);
|
903
|
-
socket = process->findSessionSocketWithLowestBusyness();
|
904
|
-
}
|
905
|
-
|
906
|
-
UPDATE_TRACE_POINT();
|
907
|
-
unsigned long long timeout = 1000 * 1000 * 60; // 1 min
|
908
|
-
try {
|
909
|
-
this_thread::restore_interruption ri(di);
|
910
|
-
this_thread::restore_syscall_interruption rsi(dsi);
|
911
|
-
|
912
|
-
// Grab a connection. The connection is marked as fail in order to
|
913
|
-
// ensure it is closed / recycled after this request (otherwise we'd
|
914
|
-
// need to completely read the response).
|
915
|
-
connection = socket->checkoutConnection();
|
916
|
-
connection.fail = true;
|
917
|
-
ScopeGuard guard(boost::bind(&Socket::checkinConnection, socket, connection));
|
918
|
-
|
919
|
-
// This is copied from RequestHandler when it is sending data using the
|
920
|
-
// "session" protocol.
|
921
|
-
char sizeField[sizeof(boost::uint32_t)];
|
922
|
-
SmallVector<StaticString, 10> data;
|
923
|
-
|
924
|
-
data.push_back(StaticString(sizeField, sizeof(boost::uint32_t)));
|
925
|
-
data.push_back(P_STATIC_STRING_WITH_NULL("REQUEST_METHOD"));
|
926
|
-
data.push_back(P_STATIC_STRING_WITH_NULL("OOBW"));
|
927
|
-
|
928
|
-
data.push_back(P_STATIC_STRING_WITH_NULL("PASSENGER_CONNECT_PASSWORD"));
|
929
|
-
data.push_back(StaticString(secret, SECRET_SIZE));
|
930
|
-
data.push_back(StaticString("", 1));
|
931
|
-
|
932
|
-
boost::uint32_t dataSize = 0;
|
933
|
-
for (unsigned int i = 1; i < data.size(); i++) {
|
934
|
-
dataSize += (boost::uint32_t) data[i].size();
|
935
|
-
}
|
936
|
-
Uint32Message::generate(sizeField, dataSize);
|
937
|
-
|
938
|
-
gatheredWrite(connection.fd, &data[0], data.size(), &timeout);
|
939
|
-
|
940
|
-
// We do not care what the actual response is ... just wait for it.
|
941
|
-
UPDATE_TRACE_POINT();
|
942
|
-
waitUntilReadable(connection.fd, &timeout);
|
943
|
-
} catch (const SystemException &e) {
|
944
|
-
P_ERROR("*** ERROR: " << e.what() << "\n" << e.backtrace());
|
945
|
-
} catch (const TimeoutException &e) {
|
946
|
-
P_ERROR("*** ERROR: " << e.what() << "\n" << e.backtrace());
|
947
|
-
}
|
948
|
-
|
949
|
-
UPDATE_TRACE_POINT();
|
950
|
-
boost::container::vector<Callback> actions;
|
951
|
-
{
|
952
|
-
// Standard resource management boilerplate stuff...
|
953
|
-
Pool *pool = getPool();
|
954
|
-
boost::unique_lock<boost::mutex> lock(pool->syncher);
|
955
|
-
if (OXT_UNLIKELY(!process->isAlive() || !isAlive())) {
|
956
|
-
return;
|
957
|
-
}
|
958
|
-
|
959
|
-
process->oobwStatus = Process::OOBW_NOT_ACTIVE;
|
960
|
-
if (process->enabled == Process::DISABLED) {
|
961
|
-
enable(process, actions);
|
962
|
-
assignSessionsToGetWaiters(actions);
|
963
|
-
}
|
964
|
-
|
965
|
-
pool->fullVerifyInvariants();
|
966
|
-
|
967
|
-
initiateNextOobwRequest();
|
968
|
-
}
|
969
|
-
UPDATE_TRACE_POINT();
|
970
|
-
runAllActions(actions);
|
971
|
-
actions.clear();
|
972
|
-
|
973
|
-
UPDATE_TRACE_POINT();
|
974
|
-
P_DEBUG("Finished OOBW request for process " << process->inspect());
|
975
|
-
if (debug != NULL && debug->oobw) {
|
976
|
-
debug->debugger->send("OOBW request finished");
|
977
|
-
}
|
978
|
-
}
|
979
|
-
|
980
|
-
void
|
981
|
-
Group::initiateNextOobwRequest() {
|
982
|
-
ProcessList::const_iterator it, end = enabledProcesses.end();
|
983
|
-
for (it = enabledProcesses.begin(); it != end; it++) {
|
984
|
-
const ProcessPtr &process = *it;
|
985
|
-
if (shouldInitiateOobw(process.get())) {
|
986
|
-
// We keep an extra reference to processes to prevent premature destruction.
|
987
|
-
ProcessPtr p = process;
|
988
|
-
initiateOobw(p);
|
989
|
-
return;
|
990
|
-
}
|
991
|
-
}
|
992
|
-
}
|
993
|
-
|
994
|
-
// The 'self' parameter is for keeping the current Group object alive while this thread is running.
|
995
|
-
void
|
996
|
-
Group::spawnThreadMain(GroupPtr self, SpawnerPtr spawner, Options options, unsigned int restartsInitiated) {
|
997
|
-
spawnThreadRealMain(spawner, options, restartsInitiated);
|
998
|
-
}
|
999
|
-
|
1000
|
-
void
|
1001
|
-
Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options, unsigned int restartsInitiated) {
|
1002
|
-
TRACE_POINT();
|
1003
|
-
this_thread::disable_interruption di;
|
1004
|
-
this_thread::disable_syscall_interruption dsi;
|
1005
|
-
|
1006
|
-
Pool *pool = getPool();
|
1007
|
-
Pool::DebugSupportPtr debug = pool->debugSupport;
|
1008
|
-
|
1009
|
-
bool done = false;
|
1010
|
-
while (!done) {
|
1011
|
-
bool shouldFail = false;
|
1012
|
-
if (debug != NULL && debug->spawning) {
|
1013
|
-
UPDATE_TRACE_POINT();
|
1014
|
-
this_thread::restore_interruption ri(di);
|
1015
|
-
this_thread::restore_syscall_interruption rsi(dsi);
|
1016
|
-
this_thread::interruption_point();
|
1017
|
-
string iteration;
|
1018
|
-
{
|
1019
|
-
LockGuard g(debug->syncher);
|
1020
|
-
debug->spawnLoopIteration++;
|
1021
|
-
iteration = toString(debug->spawnLoopIteration);
|
1022
|
-
}
|
1023
|
-
P_DEBUG("Begin spawn loop iteration " << iteration);
|
1024
|
-
debug->debugger->send("Begin spawn loop iteration " +
|
1025
|
-
iteration);
|
1026
|
-
|
1027
|
-
vector<string> cases;
|
1028
|
-
cases.push_back("Proceed with spawn loop iteration " + iteration);
|
1029
|
-
cases.push_back("Fail spawn loop iteration " + iteration);
|
1030
|
-
MessagePtr message = debug->messages->recvAny(cases);
|
1031
|
-
shouldFail = message->name == "Fail spawn loop iteration " + iteration;
|
1032
|
-
}
|
1033
|
-
|
1034
|
-
SpawnObject spawnObject;
|
1035
|
-
ProcessPtr process;
|
1036
|
-
ExceptionPtr exception;
|
1037
|
-
try {
|
1038
|
-
UPDATE_TRACE_POINT();
|
1039
|
-
this_thread::restore_interruption ri(di);
|
1040
|
-
this_thread::restore_syscall_interruption rsi(dsi);
|
1041
|
-
if (shouldFail) {
|
1042
|
-
SpawnException e("Simulated failure");
|
1043
|
-
processAndLogNewSpawnException(e, options, pool->getSpawnerConfig());
|
1044
|
-
throw e;
|
1045
|
-
} else {
|
1046
|
-
spawnObject = spawner->spawn(options);
|
1047
|
-
process = spawnObject.process;
|
1048
|
-
process->setGroup(this);
|
1049
|
-
}
|
1050
|
-
} catch (const thread_interrupted &) {
|
1051
|
-
break;
|
1052
|
-
} catch (const tracable_exception &e) {
|
1053
|
-
exception = copyException(e);
|
1054
|
-
// Let other (unexpected) exceptions crash the program so
|
1055
|
-
// gdb can generate a backtrace.
|
1056
|
-
}
|
1057
|
-
|
1058
|
-
UPDATE_TRACE_POINT();
|
1059
|
-
ScopeGuard guard(boost::bind(Process::forceTriggerShutdownAndCleanup, process));
|
1060
|
-
boost::unique_lock<boost::mutex> lock(pool->syncher);
|
1061
|
-
|
1062
|
-
if (!isAlive()) {
|
1063
|
-
if (process != NULL) {
|
1064
|
-
P_DEBUG("Group is being shut down so dropping process " <<
|
1065
|
-
process->inspect() << " which we just spawned and exiting spawn loop");
|
1066
|
-
} else {
|
1067
|
-
P_DEBUG("The group is being shut down. A process failed "
|
1068
|
-
"to be spawned anyway, so ignoring this error and exiting "
|
1069
|
-
"spawn loop");
|
1070
|
-
}
|
1071
|
-
// We stop immediately because any previously assumed invariants
|
1072
|
-
// may have been violated.
|
1073
|
-
break;
|
1074
|
-
} else if (restartsInitiated != this->restartsInitiated) {
|
1075
|
-
if (process != NULL) {
|
1076
|
-
P_DEBUG("A restart was issued for the group, so dropping process " <<
|
1077
|
-
process->inspect() << " which we just spawned and exiting spawn loop");
|
1078
|
-
} else {
|
1079
|
-
P_DEBUG("A restart was issued for the group. A process failed "
|
1080
|
-
"to be spawned anyway, so ignoring this error and exiting "
|
1081
|
-
"spawn loop");
|
1082
|
-
}
|
1083
|
-
// We stop immediately because any previously assumed invariants
|
1084
|
-
// may have been violated.
|
1085
|
-
break;
|
1086
|
-
}
|
1087
|
-
|
1088
|
-
verifyInvariants();
|
1089
|
-
assert(m_spawning);
|
1090
|
-
assert(processesBeingSpawned > 0);
|
1091
|
-
|
1092
|
-
processesBeingSpawned--;
|
1093
|
-
assert(processesBeingSpawned == 0);
|
1094
|
-
|
1095
|
-
UPDATE_TRACE_POINT();
|
1096
|
-
boost::container::vector<Callback> actions;
|
1097
|
-
if (process != NULL) {
|
1098
|
-
AttachResult result = attach(spawnObject, actions);
|
1099
|
-
if (result == AR_OK) {
|
1100
|
-
guard.clear();
|
1101
|
-
if (getWaitlist.empty()) {
|
1102
|
-
pool->assignSessionsToGetWaiters(actions);
|
1103
|
-
} else {
|
1104
|
-
assignSessionsToGetWaiters(actions);
|
1105
|
-
}
|
1106
|
-
P_DEBUG("New process count = " << enabledCount <<
|
1107
|
-
", remaining get waiters = " << getWaitlist.size());
|
1108
|
-
} else {
|
1109
|
-
done = true;
|
1110
|
-
P_DEBUG("Unable to attach spawned process " << process->inspect());
|
1111
|
-
if (result == AR_ANOTHER_GROUP_IS_WAITING_FOR_CAPACITY) {
|
1112
|
-
pool->possiblySpawnMoreProcessesForExistingGroups();
|
1113
|
-
}
|
1114
|
-
}
|
1115
|
-
} else {
|
1116
|
-
// TODO: sure this is the best thing? if there are
|
1117
|
-
// processes currently alive we should just use them.
|
1118
|
-
if (enabledCount == 0) {
|
1119
|
-
enableAllDisablingProcesses(actions);
|
1120
|
-
}
|
1121
|
-
Pool::assignExceptionToGetWaiters(getWaitlist, exception, actions);
|
1122
|
-
pool->assignSessionsToGetWaiters(actions);
|
1123
|
-
done = true;
|
1124
|
-
}
|
1125
|
-
|
1126
|
-
done = done
|
1127
|
-
|| (processLowerLimitsSatisfied() && getWaitlist.empty())
|
1128
|
-
|| processUpperLimitsReached()
|
1129
|
-
|| pool->atFullCapacityUnlocked();
|
1130
|
-
m_spawning = !done;
|
1131
|
-
if (done) {
|
1132
|
-
P_DEBUG("Spawn loop done");
|
1133
|
-
} else {
|
1134
|
-
processesBeingSpawned++;
|
1135
|
-
P_DEBUG("Continue spawning");
|
1136
|
-
}
|
1137
|
-
|
1138
|
-
UPDATE_TRACE_POINT();
|
1139
|
-
pool->fullVerifyInvariants();
|
1140
|
-
lock.unlock();
|
1141
|
-
UPDATE_TRACE_POINT();
|
1142
|
-
runAllActions(actions);
|
1143
|
-
UPDATE_TRACE_POINT();
|
1144
|
-
}
|
1145
|
-
|
1146
|
-
if (debug != NULL && debug->spawning) {
|
1147
|
-
debug->debugger->send("Spawn loop done");
|
1148
|
-
}
|
1149
|
-
}
|
1150
|
-
|
1151
|
-
bool
|
1152
|
-
Group::shouldSpawn() const {
|
1153
|
-
return allowSpawn()
|
1154
|
-
&& (
|
1155
|
-
!processLowerLimitsSatisfied()
|
1156
|
-
|| allEnabledProcessesAreTotallyBusy()
|
1157
|
-
|| !getWaitlist.empty()
|
1158
|
-
);
|
1159
|
-
}
|
1160
|
-
|
1161
|
-
bool
|
1162
|
-
Group::shouldSpawnForGetAction() const {
|
1163
|
-
return enabledCount == 0 || shouldSpawn();
|
1164
|
-
}
|
1165
|
-
|
1166
|
-
void
|
1167
|
-
Group::restart(const Options &options, RestartMethod method) {
|
1168
|
-
boost::container::vector<Callback> actions;
|
1169
|
-
|
1170
|
-
assert(isAlive());
|
1171
|
-
P_DEBUG("Restarting group " << name);
|
1172
|
-
|
1173
|
-
// If there is currently a restarter thread or a spawner thread active,
|
1174
|
-
// the following tells them to abort their current work as soon as possible.
|
1175
|
-
restartsInitiated++;
|
1176
|
-
|
1177
|
-
processesBeingSpawned = 0;
|
1178
|
-
m_spawning = false;
|
1179
|
-
m_restarting = true;
|
1180
|
-
uuid = generateUuid(getSuperGroup());
|
1181
|
-
detachAll(actions);
|
1182
|
-
getPool()->interruptableThreads.create_thread(
|
1183
|
-
boost::bind(&Group::finalizeRestart, this, shared_from_this(),
|
1184
|
-
this->options.copyAndPersist().clearPerRequestFields(),
|
1185
|
-
options.copyAndPersist().clearPerRequestFields(),
|
1186
|
-
method, getPool()->spawnerFactory, restartsInitiated, actions),
|
1187
|
-
"Group restarter: " + name,
|
1188
|
-
POOL_HELPER_THREAD_STACK_SIZE
|
1189
|
-
);
|
1190
|
-
}
|
1191
|
-
|
1192
|
-
// The 'self' parameter is for keeping the current Group object alive while this thread is running.
|
1193
|
-
void
|
1194
|
-
Group::finalizeRestart(GroupPtr self,
|
1195
|
-
Options oldOptions,
|
1196
|
-
Options newOptions, RestartMethod method,
|
1197
|
-
SpawnerFactoryPtr spawnerFactory,
|
1198
|
-
unsigned int restartsInitiated,
|
1199
|
-
boost::container::vector<Callback> postLockActions)
|
1200
|
-
{
|
1201
|
-
TRACE_POINT();
|
1202
|
-
|
1203
|
-
Pool::runAllActions(postLockActions);
|
1204
|
-
postLockActions.clear();
|
1205
|
-
|
1206
|
-
this_thread::disable_interruption di;
|
1207
|
-
this_thread::disable_syscall_interruption dsi;
|
1208
|
-
|
1209
|
-
// Create a new spawner.
|
1210
|
-
Options spawnerOptions = oldOptions;
|
1211
|
-
resetOptions(newOptions, &spawnerOptions);
|
1212
|
-
SpawnerPtr newSpawner = spawnerFactory->create(spawnerOptions);
|
1213
|
-
SpawnerPtr oldSpawner;
|
1214
|
-
|
1215
|
-
UPDATE_TRACE_POINT();
|
1216
|
-
Pool *pool = getPool();
|
1217
|
-
|
1218
|
-
Pool::DebugSupportPtr debug = pool->debugSupport;
|
1219
|
-
if (debug != NULL && debug->restarting) {
|
1220
|
-
this_thread::restore_interruption ri(di);
|
1221
|
-
this_thread::restore_syscall_interruption rsi(dsi);
|
1222
|
-
this_thread::interruption_point();
|
1223
|
-
debug->debugger->send("About to end restarting");
|
1224
|
-
debug->messages->recv("Finish restarting");
|
1225
|
-
}
|
1226
|
-
|
1227
|
-
ScopedLock l(pool->syncher);
|
1228
|
-
if (!isAlive()) {
|
1229
|
-
P_DEBUG("Group " << name << " is shutting down, so aborting restart");
|
1230
|
-
return;
|
1231
|
-
}
|
1232
|
-
if (restartsInitiated != this->restartsInitiated) {
|
1233
|
-
// Before this restart could be finalized, another restart command was given.
|
1234
|
-
// The spawner we just created might be out of date now so we abort.
|
1235
|
-
P_DEBUG("Restart of group " << name << " aborted because a new restart was initiated concurrently");
|
1236
|
-
if (debug != NULL && debug->restarting) {
|
1237
|
-
debug->debugger->send("Restarting aborted");
|
1238
|
-
}
|
1239
|
-
return;
|
1240
|
-
}
|
1241
|
-
|
1242
|
-
// Run some sanity checks.
|
1243
|
-
pool->fullVerifyInvariants();
|
1244
|
-
assert(m_restarting);
|
1245
|
-
UPDATE_TRACE_POINT();
|
1246
|
-
|
1247
|
-
// Atomically swap the new spawner with the old one.
|
1248
|
-
resetOptions(newOptions);
|
1249
|
-
oldSpawner = spawner;
|
1250
|
-
spawner = newSpawner;
|
1251
|
-
|
1252
|
-
m_restarting = false;
|
1253
|
-
if (shouldSpawn()) {
|
1254
|
-
spawn();
|
1255
|
-
} else if (isWaitingForCapacity()) {
|
1256
|
-
P_INFO("Group " << name << " is waiting for capacity to become available. "
|
1257
|
-
"Trying to shutdown another idle process to free capacity...");
|
1258
|
-
if (pool->forceFreeCapacity(this, postLockActions) != NULL) {
|
1259
|
-
spawn();
|
1260
|
-
} else {
|
1261
|
-
P_INFO("There are no processes right now that are eligible "
|
1262
|
-
"for shutdown. Will try again later.");
|
1263
|
-
}
|
1264
|
-
}
|
1265
|
-
verifyInvariants();
|
1266
|
-
|
1267
|
-
l.unlock();
|
1268
|
-
oldSpawner.reset();
|
1269
|
-
Pool::runAllActions(postLockActions);
|
1270
|
-
P_DEBUG("Restart of group " << name << " done");
|
1271
|
-
if (debug != NULL && debug->restarting) {
|
1272
|
-
debug->debugger->send("Restarting done");
|
1273
|
-
}
|
1274
|
-
}
|
1275
|
-
|
1276
|
-
/**
|
1277
|
-
* The `immediately` parameter only has effect if the detached processes checker
|
1278
|
-
* thread is active. It means that, if the thread is currently sleeping, it should
|
1279
|
-
* wake up immediately and perform work.
|
1280
|
-
*/
|
1281
|
-
void
|
1282
|
-
Group::startCheckingDetachedProcesses(bool immediately) {
|
1283
|
-
if (!detachedProcessesCheckerActive) {
|
1284
|
-
P_DEBUG("Starting detached processes checker");
|
1285
|
-
getPool()->nonInterruptableThreads.create_thread(
|
1286
|
-
boost::bind(&Group::detachedProcessesCheckerMain, this, shared_from_this()),
|
1287
|
-
"Detached processes checker: " + name,
|
1288
|
-
POOL_HELPER_THREAD_STACK_SIZE
|
1289
|
-
);
|
1290
|
-
detachedProcessesCheckerActive = true;
|
1291
|
-
} else if (detachedProcessesCheckerActive && immediately) {
|
1292
|
-
detachedProcessesCheckerCond.notify_all();
|
1293
|
-
}
|
1294
|
-
}
|
1295
|
-
|
1296
|
-
void
|
1297
|
-
Group::detachedProcessesCheckerMain(GroupPtr self) {
|
1298
|
-
TRACE_POINT();
|
1299
|
-
Pool *pool = getPool();
|
1300
|
-
|
1301
|
-
Pool::DebugSupportPtr debug = pool->debugSupport;
|
1302
|
-
if (debug != NULL && debug->detachedProcessesChecker) {
|
1303
|
-
debug->debugger->send("About to start detached processes checker");
|
1304
|
-
debug->messages->recv("Proceed with starting detached processes checker");
|
1305
|
-
}
|
1306
|
-
|
1307
|
-
boost::unique_lock<boost::mutex> lock(pool->syncher);
|
1308
|
-
while (true) {
|
1309
|
-
assert(detachedProcessesCheckerActive);
|
1310
|
-
|
1311
|
-
if (getLifeStatus() == SHUT_DOWN || this_thread::interruption_requested()) {
|
1312
|
-
UPDATE_TRACE_POINT();
|
1313
|
-
P_DEBUG("Stopping detached processes checker");
|
1314
|
-
detachedProcessesCheckerActive = false;
|
1315
|
-
break;
|
1316
|
-
}
|
1317
|
-
|
1318
|
-
UPDATE_TRACE_POINT();
|
1319
|
-
if (!detachedProcesses.empty()) {
|
1320
|
-
P_TRACE(2, "Checking whether any of the " << detachedProcesses.size() <<
|
1321
|
-
" detached processes have exited...");
|
1322
|
-
ProcessList::iterator it, end = detachedProcesses.end();
|
1323
|
-
ProcessList processesToRemove;
|
1324
|
-
|
1325
|
-
for (it = detachedProcesses.begin(); it != end; it++) {
|
1326
|
-
const ProcessPtr process = *it;
|
1327
|
-
switch (process->getLifeStatus()) {
|
1328
|
-
case Process::ALIVE:
|
1329
|
-
if (process->canTriggerShutdown()) {
|
1330
|
-
P_DEBUG("Detached process " << process->inspect() <<
|
1331
|
-
" has 0 active sessions now. Triggering shutdown.");
|
1332
|
-
process->triggerShutdown();
|
1333
|
-
assert(process->getLifeStatus() == Process::SHUTDOWN_TRIGGERED);
|
1334
|
-
}
|
1335
|
-
break;
|
1336
|
-
case Process::SHUTDOWN_TRIGGERED:
|
1337
|
-
if (process->canCleanup()) {
|
1338
|
-
P_DEBUG("Detached process " << process->inspect() << " has shut down. Cleaning up associated resources.");
|
1339
|
-
process->cleanup();
|
1340
|
-
assert(process->getLifeStatus() == Process::DEAD);
|
1341
|
-
processesToRemove.push_back(process);
|
1342
|
-
} else if (process->shutdownTimeoutExpired()) {
|
1343
|
-
P_WARN("Detached process " << process->inspect() <<
|
1344
|
-
" didn't shut down within " PROCESS_SHUTDOWN_TIMEOUT_DISPLAY
|
1345
|
-
". Forcefully killing it with SIGKILL.");
|
1346
|
-
kill(process->pid, SIGKILL);
|
1347
|
-
}
|
1348
|
-
break;
|
1349
|
-
default:
|
1350
|
-
P_BUG("Unknown 'lifeStatus' state " << (int) process->getLifeStatus());
|
1351
|
-
}
|
1352
|
-
}
|
1353
|
-
|
1354
|
-
end = processesToRemove.end();
|
1355
|
-
for (it = processesToRemove.begin(); it != end; it++) {
|
1356
|
-
removeProcessFromList(*it, detachedProcesses);
|
1357
|
-
}
|
1358
|
-
}
|
1359
|
-
|
1360
|
-
UPDATE_TRACE_POINT();
|
1361
|
-
if (detachedProcesses.empty()) {
|
1362
|
-
UPDATE_TRACE_POINT();
|
1363
|
-
P_DEBUG("Stopping detached processes checker");
|
1364
|
-
detachedProcessesCheckerActive = false;
|
1365
|
-
|
1366
|
-
boost::container::vector<Callback> actions;
|
1367
|
-
if (shutdownCanFinish()) {
|
1368
|
-
UPDATE_TRACE_POINT();
|
1369
|
-
finishShutdown(actions);
|
1370
|
-
}
|
1371
|
-
|
1372
|
-
verifyInvariants();
|
1373
|
-
verifyExpensiveInvariants();
|
1374
|
-
lock.unlock();
|
1375
|
-
UPDATE_TRACE_POINT();
|
1376
|
-
runAllActions(actions);
|
1377
|
-
break;
|
1378
|
-
} else {
|
1379
|
-
UPDATE_TRACE_POINT();
|
1380
|
-
verifyInvariants();
|
1381
|
-
verifyExpensiveInvariants();
|
1382
|
-
}
|
1383
|
-
|
1384
|
-
// Not all processes can be shut down yet. Sleep for a while unless
|
1385
|
-
// someone wakes us up.
|
1386
|
-
UPDATE_TRACE_POINT();
|
1387
|
-
detachedProcessesCheckerCond.timed_wait(lock,
|
1388
|
-
posix_time::milliseconds(100));
|
1389
|
-
}
|
1390
|
-
}
|
1391
|
-
|
1392
|
-
bool
|
1393
|
-
Group::selfCheckingEnabled() const {
|
1394
|
-
return getPool()->selfchecking;
|
1395
|
-
}
|
1396
|
-
|
1397
|
-
void
|
1398
|
-
Group::wakeUpGarbageCollector() {
|
1399
|
-
getPool()->garbageCollectionCond.notify_all();
|
1400
|
-
}
|
1401
|
-
|
1402
|
-
bool
|
1403
|
-
Group::poolAtFullCapacity() const {
|
1404
|
-
return getPool()->atFullCapacityUnlocked();
|
1405
|
-
}
|
1406
|
-
|
1407
|
-
bool
|
1408
|
-
Group::anotherGroupIsWaitingForCapacity() const {
|
1409
|
-
return findOtherGroupWaitingForCapacity() != NULL;
|
1410
|
-
}
|
1411
|
-
|
1412
|
-
Group *
|
1413
|
-
Group::findOtherGroupWaitingForCapacity() const {
|
1414
|
-
Pool *pool = getPool();
|
1415
|
-
// TODO: this only works if SuperGroup can only have one Group.
|
1416
|
-
// If we ever extend SuperGroup then this needs to be changed.
|
1417
|
-
if (pool->superGroups.size() == 1) {
|
1418
|
-
return NULL;
|
1419
|
-
}
|
1420
|
-
|
1421
|
-
SuperGroupMap::ConstIterator sg_it(pool->superGroups);
|
1422
|
-
while (*sg_it != NULL) {
|
1423
|
-
const SuperGroupPtr &superGroup = sg_it.getValue();
|
1424
|
-
SuperGroup::GroupList::const_iterator g_it, g_end = superGroup->groups.end();
|
1425
|
-
for (g_it = superGroup->groups.begin(); g_it != g_end; g_it++) {
|
1426
|
-
if (g_it->get() != this && (*g_it)->isWaitingForCapacity()) {
|
1427
|
-
return g_it->get();
|
1428
|
-
}
|
1429
|
-
}
|
1430
|
-
sg_it.next();
|
1431
|
-
}
|
1432
|
-
return NULL;
|
1433
|
-
}
|
1434
|
-
|
1435
|
-
ProcessPtr
|
1436
|
-
Group::poolForceFreeCapacity(const Group *exclude,
|
1437
|
-
boost::container::vector<Callback> &postLockActions)
|
1438
|
-
{
|
1439
|
-
return getPool()->forceFreeCapacity(exclude, postLockActions);
|
1440
|
-
}
|
1441
|
-
|
1442
|
-
bool
|
1443
|
-
Group::testOverflowRequestQueue() const {
|
1444
|
-
// This has a performance penalty, although I'm not sure whether the penalty is
|
1445
|
-
// any greater than a hash table lookup if I were to implement it in Options.
|
1446
|
-
Pool::DebugSupportPtr debug = getPool()->debugSupport;
|
1447
|
-
if (debug) {
|
1448
|
-
return debug->testOverflowRequestQueue;
|
1449
|
-
} else {
|
1450
|
-
return false;
|
1451
|
-
}
|
1452
|
-
}
|
1453
|
-
|
1454
|
-
void
|
1455
|
-
Group::callAbortLongRunningConnectionsCallback(const ProcessPtr &process) {
|
1456
|
-
Pool::AbortLongRunningConnectionsCallback callback =
|
1457
|
-
getPool()->abortLongRunningConnectionsCallback;
|
1458
|
-
if (callback != NULL) {
|
1459
|
-
callback(process);
|
1460
|
-
}
|
1461
|
-
}
|
1462
|
-
|
1463
|
-
psg_pool_t *
|
1464
|
-
Group::getPallocPool() const {
|
1465
|
-
return getPool()->palloc;
|
1466
|
-
}
|
1467
|
-
|
1468
|
-
const ResourceLocator &
|
1469
|
-
Group::getResourceLocator() const {
|
1470
|
-
return *getPool()->getSpawnerConfig()->resourceLocator;
|
1471
|
-
}
|
1472
|
-
|
1473
|
-
/* Given a hook name like "queue_full_error", we return HookScriptOptions filled in with this name and a spec
|
1474
|
-
* (user settings that can be queried from agentsOptions using the external hook name that is prefixed with "hook_")
|
1475
|
-
*
|
1476
|
-
* @return false if the user parameters (agentsOptions) are not available (e.g. during ApplicationPool2_PoolTest)
|
1477
|
-
*/
|
1478
|
-
bool
|
1479
|
-
Group::prepareHookScriptOptions(HookScriptOptions &hsOptions, const char *name) {
|
1480
|
-
SpawnerConfigPtr config = getPool()->getSpawnerConfig();
|
1481
|
-
if (config->agentsOptions == NULL) {
|
1482
|
-
return false;
|
1483
|
-
}
|
1484
|
-
|
1485
|
-
hsOptions.name = name;
|
1486
|
-
string hookName = string("hook_") + name;
|
1487
|
-
hsOptions.spec = config->agentsOptions->get(hookName, false);
|
1488
|
-
|
1489
|
-
return true;
|
1490
|
-
}
|
1491
|
-
|
1492
|
-
// 'process' is not a reference so that bind(runAttachHooks, ...) causes the shared
|
1493
|
-
// pointer reference to increment.
|
1494
|
-
void
|
1495
|
-
Group::runAttachHooks(const ProcessPtr process) const {
|
1496
|
-
getPool()->runHookScripts("attached_process",
|
1497
|
-
boost::bind(&Group::setupAttachOrDetachHook, this, process, _1));
|
1498
|
-
}
|
1499
|
-
|
1500
|
-
void
|
1501
|
-
Group::runDetachHooks(const ProcessPtr process) const {
|
1502
|
-
getPool()->runHookScripts("detached_process",
|
1503
|
-
boost::bind(&Group::setupAttachOrDetachHook, this, process, _1));
|
1504
|
-
}
|
1505
|
-
|
1506
|
-
void
|
1507
|
-
Group::setupAttachOrDetachHook(const ProcessPtr process, HookScriptOptions &options) const {
|
1508
|
-
options.environment.push_back(make_pair("PASSENGER_PROCESS_PID", toString(process->pid)));
|
1509
|
-
options.environment.push_back(make_pair("PASSENGER_APP_ROOT", this->options.appRoot));
|
1510
|
-
}
|
1511
|
-
|
1512
|
-
void
|
1513
|
-
Group::generateSecret(const SuperGroup *superGroup, char *secret) {
|
1514
|
-
superGroup->getPool()->getRandomGenerator()->generateAsciiString(secret, SECRET_SIZE);
|
1515
|
-
}
|
1516
|
-
|
1517
|
-
string
|
1518
|
-
Group::generateUuid(const SuperGroup *superGroup) {
|
1519
|
-
return superGroup->getPool()->getRandomGenerator()->generateAsciiString(20);
|
1520
|
-
}
|
1521
|
-
|
1522
|
-
|
1523
|
-
Pool *
|
1524
|
-
Process::getPool() const {
|
1525
|
-
assert(getLifeStatus() != DEAD);
|
1526
|
-
return getGroup()->getPool();
|
1527
|
-
}
|
1528
|
-
|
1529
|
-
SuperGroup *
|
1530
|
-
Process::getSuperGroup() const {
|
1531
|
-
assert(getLifeStatus() != DEAD);
|
1532
|
-
return getGroup()->getSuperGroup();
|
1533
|
-
}
|
1534
|
-
|
1535
|
-
StaticString
|
1536
|
-
Process::getGroupSecret() const {
|
1537
|
-
return StaticString(getGroup()->secret, Group::SECRET_SIZE);
|
1538
|
-
}
|
1539
|
-
|
1540
|
-
SessionPtr
|
1541
|
-
Process::createSessionObject(Socket *socket) {
|
1542
|
-
Pool *pool = NULL;
|
1543
|
-
Group *group = getGroup();
|
1544
|
-
if (group != NULL) {
|
1545
|
-
// Backpointers are normally never NULL, but they can be
|
1546
|
-
// NULL in unit tests
|
1547
|
-
SuperGroup *superGroup = group->getSuperGroup();
|
1548
|
-
if (superGroup != NULL) {
|
1549
|
-
pool = superGroup->getPool();
|
1550
|
-
}
|
1551
|
-
}
|
1552
|
-
|
1553
|
-
Session *session;
|
1554
|
-
if (OXT_LIKELY(pool != NULL)) {
|
1555
|
-
{
|
1556
|
-
LockGuard l(pool->sessionObjectPoolSyncher);
|
1557
|
-
session = pool->sessionObjectPool.malloc();
|
1558
|
-
}
|
1559
|
-
session = new (session) Session(pool, this, socket);
|
1560
|
-
} else {
|
1561
|
-
session = new Session(NULL, this, socket);
|
1562
|
-
}
|
1563
|
-
return SessionPtr(session, false);
|
1564
|
-
}
|
1565
|
-
|
1566
|
-
string
|
1567
|
-
Process::inspect() const {
|
1568
|
-
assert(getLifeStatus() != DEAD);
|
1569
|
-
stringstream result;
|
1570
|
-
result << "(pid=" << pid;
|
1571
|
-
Group *group = getGroup();
|
1572
|
-
if (group != NULL) {
|
1573
|
-
// This Process hasn't been attached to a Group yet.
|
1574
|
-
result << ", group=" << group->name;
|
1575
|
-
}
|
1576
|
-
result << ")";
|
1577
|
-
return result.str();
|
1578
|
-
}
|
1579
|
-
|
1580
|
-
|
1581
|
-
StaticString
|
1582
|
-
Session::getGroupSecret() const {
|
1583
|
-
return StaticString(getGroup()->secret, Group::SECRET_SIZE);
|
1584
|
-
}
|
1585
|
-
|
1586
|
-
pid_t
|
1587
|
-
Session::getPid() const {
|
1588
|
-
return getProcess()->pid;
|
1589
|
-
}
|
1590
|
-
|
1591
|
-
StaticString
|
1592
|
-
Session::getGupid() const {
|
1593
|
-
const Process *process = getProcess();
|
1594
|
-
return StaticString(process->gupid, process->gupidSize);
|
1595
|
-
}
|
1596
|
-
|
1597
|
-
unsigned int
|
1598
|
-
Session::getStickySessionId() const {
|
1599
|
-
return getProcess()->stickySessionId;
|
1600
|
-
}
|
1601
|
-
|
1602
|
-
Group *
|
1603
|
-
Session::getGroup() const {
|
1604
|
-
return getProcess()->getGroup();
|
1605
|
-
}
|
1606
|
-
|
1607
326
|
void
|
1608
327
|
Session::requestOOBW() {
|
1609
328
|
ProcessPtr process = getProcess()->shared_from_this();
|
@@ -1611,107 +330,6 @@ Session::requestOOBW() {
|
|
1611
330
|
process->getGroup()->requestOOBW(process);
|
1612
331
|
}
|
1613
332
|
|
1614
|
-
int
|
1615
|
-
Session::kill(int signo) {
|
1616
|
-
return getProcess()->kill(signo);
|
1617
|
-
}
|
1618
|
-
|
1619
|
-
void
|
1620
|
-
Session::destroySelf() const {
|
1621
|
-
Pool *pool = this->pool;
|
1622
|
-
// Backpointers are normally never NULL, but they can be
|
1623
|
-
// NULL in unit tests
|
1624
|
-
if (pool != NULL) {
|
1625
|
-
this->~Session();
|
1626
|
-
LockGuard l(pool->sessionObjectPoolSyncher);
|
1627
|
-
pool->sessionObjectPool.free(const_cast<Session *>(this));
|
1628
|
-
} else {
|
1629
|
-
delete this;
|
1630
|
-
}
|
1631
|
-
}
|
1632
|
-
|
1633
|
-
|
1634
|
-
PipeWatcher::DataCallback PipeWatcher::onData;
|
1635
|
-
|
1636
|
-
PipeWatcher::PipeWatcher(const FileDescriptor &_fd, const char *_name, pid_t _pid)
|
1637
|
-
: fd(_fd),
|
1638
|
-
name(_name),
|
1639
|
-
pid(_pid)
|
1640
|
-
{
|
1641
|
-
started = false;
|
1642
|
-
}
|
1643
|
-
|
1644
|
-
void
|
1645
|
-
PipeWatcher::initialize() {
|
1646
|
-
oxt::thread(boost::bind(threadMain, shared_from_this()),
|
1647
|
-
"PipeWatcher: PID " + toString(pid) + " " + name + ", fd " + toString(fd),
|
1648
|
-
POOL_HELPER_THREAD_STACK_SIZE);
|
1649
|
-
}
|
1650
|
-
|
1651
|
-
void
|
1652
|
-
PipeWatcher::start() {
|
1653
|
-
boost::lock_guard<boost::mutex> lock(startSyncher);
|
1654
|
-
started = true;
|
1655
|
-
startCond.notify_all();
|
1656
|
-
}
|
1657
|
-
|
1658
|
-
void
|
1659
|
-
PipeWatcher::threadMain(boost::shared_ptr<PipeWatcher> self) {
|
1660
|
-
TRACE_POINT();
|
1661
|
-
self->threadMain();
|
1662
|
-
}
|
1663
|
-
|
1664
|
-
void
|
1665
|
-
PipeWatcher::threadMain() {
|
1666
|
-
TRACE_POINT();
|
1667
|
-
{
|
1668
|
-
boost::unique_lock<boost::mutex> lock(startSyncher);
|
1669
|
-
while (!started) {
|
1670
|
-
startCond.wait(lock);
|
1671
|
-
}
|
1672
|
-
}
|
1673
|
-
|
1674
|
-
UPDATE_TRACE_POINT();
|
1675
|
-
while (!this_thread::interruption_requested()) {
|
1676
|
-
char buf[1024 * 8];
|
1677
|
-
ssize_t ret;
|
1678
|
-
|
1679
|
-
UPDATE_TRACE_POINT();
|
1680
|
-
ret = syscalls::read(fd, buf, sizeof(buf));
|
1681
|
-
if (ret == 0) {
|
1682
|
-
break;
|
1683
|
-
} else if (ret == -1) {
|
1684
|
-
UPDATE_TRACE_POINT();
|
1685
|
-
if (errno == ECONNRESET) {
|
1686
|
-
break;
|
1687
|
-
} else if (errno != EAGAIN) {
|
1688
|
-
int e = errno;
|
1689
|
-
P_WARN("Cannot read from process " << pid << " " << name <<
|
1690
|
-
": " << strerror(e) << " (errno=" << e << ")");
|
1691
|
-
break;
|
1692
|
-
}
|
1693
|
-
} else if (ret == 1 && buf[0] == '\n') {
|
1694
|
-
UPDATE_TRACE_POINT();
|
1695
|
-
printAppOutput(pid, name, "", 0);
|
1696
|
-
} else {
|
1697
|
-
UPDATE_TRACE_POINT();
|
1698
|
-
vector<StaticString> lines;
|
1699
|
-
ssize_t ret2 = ret;
|
1700
|
-
if (ret2 > 0 && buf[ret2 - 1] == '\n') {
|
1701
|
-
ret2--;
|
1702
|
-
}
|
1703
|
-
split(StaticString(buf, ret2), '\n', lines);
|
1704
|
-
foreach (const StaticString line, lines) {
|
1705
|
-
printAppOutput(pid, name, line.data(), line.size());
|
1706
|
-
}
|
1707
|
-
}
|
1708
|
-
|
1709
|
-
if (onData != NULL) {
|
1710
|
-
onData(buf, ret);
|
1711
|
-
}
|
1712
|
-
}
|
1713
|
-
}
|
1714
|
-
|
1715
333
|
|
1716
334
|
} // namespace ApplicationPool2
|
1717
335
|
} // namespace Passenger
|