1 /** @file
2
3 A brief file description
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 #include "ProxyConfig.h"
25 #include "P_EventSystem.h"
26 #if TS_HAS_TESTS
27 #include "tscore/TestBox.h"
28 #endif
29
30 ConfigProcessor configProcessor;
31
32 class ConfigInfoReleaser : public Continuation
33 {
34 public:
ConfigInfoReleaser(unsigned int id,ConfigInfo * info)35 ConfigInfoReleaser(unsigned int id, ConfigInfo *info) : Continuation(new_ProxyMutex()), m_id(id), m_info(info)
36 {
37 SET_HANDLER(&ConfigInfoReleaser::handle_event);
38 }
39
40 int
handle_event(int,void *)41 handle_event(int /* event ATS_UNUSED */, void * /* edata ATS_UNUSED */)
42 {
43 configProcessor.release(m_id, m_info);
44 delete this;
45 return EVENT_DONE;
46 }
47
48 public:
49 unsigned int m_id;
50 ConfigInfo *m_info;
51 };
52
53 unsigned int
set(unsigned int id,ConfigInfo * info,unsigned timeout_secs)54 ConfigProcessor::set(unsigned int id, ConfigInfo *info, unsigned timeout_secs)
55 {
56 ConfigInfo *old_info;
57 int idx;
58
59 if (id == 0) {
60 id = ++ninfos;
61 ink_assert(id != 0);
62 ink_assert(id <= MAX_CONFIGS);
63 }
64
65 // Don't be an idiot and use a zero timeout ...
66 ink_assert(timeout_secs > 0);
67
68 // New objects *must* start with a zero refcount. The config
69 // processor holds it's own refcount. We should be the only
70 // refcount holder at this point.
71 ink_release_assert(info->refcount_inc() == 1);
72
73 if (id > MAX_CONFIGS) {
74 // invalid index
75 Error("[ConfigProcessor::set] invalid index");
76 return 0;
77 }
78
79 idx = id - 1;
80 old_info = infos[idx].exchange(info);
81
82 Debug("config", "Set for slot %d 0x%" PRId64 " was 0x%" PRId64 " with ref count %d", id, (int64_t)info, (int64_t)old_info,
83 (old_info) ? old_info->refcount() : 0);
84
85 if (old_info) {
86 // The ConfigInfoReleaser now takes our refcount, but
87 // some other thread might also have one ...
88 ink_assert(old_info->refcount() > 0);
89 eventProcessor.schedule_in(new ConfigInfoReleaser(id, old_info), HRTIME_SECONDS(timeout_secs));
90 }
91
92 return id;
93 }
94
95 ConfigInfo *
get(unsigned int id)96 ConfigProcessor::get(unsigned int id)
97 {
98 ConfigInfo *info;
99 int idx;
100
101 ink_assert(id <= MAX_CONFIGS);
102
103 if (id == 0 || id > MAX_CONFIGS) {
104 // because of an invalid index
105 return nullptr;
106 }
107
108 idx = id - 1;
109 info = infos[idx];
110
111 // Hand out a refcount to the caller. We should still have out
112 // own refcount, so it should be at least 2.
113 ink_release_assert(info->refcount_inc() > 1);
114 return info;
115 }
116
117 void
release(unsigned int id,ConfigInfo * info)118 ConfigProcessor::release(unsigned int id, ConfigInfo *info)
119 {
120 int idx;
121
122 if (id == 0 || id > MAX_CONFIGS) {
123 // nothing to delete since we have an invalid index
124 ink_abort("released an invalid id '%u'", id);
125 }
126
127 idx = id - 1;
128
129 if (info && info->refcount_dec() == 0) {
130 // When we release, we should already have replaced this object in the index.
131 Debug("config", "Release config %d 0x%" PRId64, id, (int64_t)info);
132 ink_release_assert(info != this->infos[idx]);
133 delete info;
134 }
135 }
136
137 #if TS_HAS_TESTS
138
139 enum {
140 REGRESSION_CONFIG_FIRST = 1, // last config in a sequence
141 REGRESSION_CONFIG_LAST = 2, // last config in a sequence
142 REGRESSION_CONFIG_SINGLE = 4, // single-owner config
143 };
144
145 struct RegressionConfig : public ConfigInfo {
146 static int nobjects; // count of outstanding RegressionConfig objects (not-reentrant)
147
148 // DeferredCall is a simple function call wrapper that defers itself until the RegressionConfig
149 // object count drops below the specified count.
150 template <typename CallType> struct DeferredCall : public Continuation {
DeferredCallRegressionConfig::DeferredCall151 DeferredCall(int _r, CallType _c) : remain(_r), call(_c) { SET_HANDLER(&DeferredCall::handleEvent); }
152 int
handleEventRegressionConfig::DeferredCall153 handleEvent(int event ATS_UNUSED, Event *e)
154 {
155 if (RegressionConfig::nobjects > this->remain) {
156 e->schedule_in(HRTIME_MSECONDS(500));
157 return EVENT_CONT;
158 }
159
160 call();
161 delete this;
162 return EVENT_DONE;
163 }
164
165 int remain; // Number of remaining RegressionConfig objects to wait for.
166 CallType call;
167 };
168
169 template <typename CallType>
170 static void
deferRegressionConfig171 defer(int count, CallType call)
172 {
173 eventProcessor.schedule_in(new RegressionConfig::DeferredCall<CallType>(count, call), HRTIME_MSECONDS(500));
174 }
175
RegressionConfigRegressionConfig176 RegressionConfig(RegressionTest *r, int *ps, unsigned f) : test(r), pstatus(ps), flags(f)
177 {
178 if (this->flags & REGRESSION_CONFIG_SINGLE) {
179 TestBox box(this->test, this->pstatus);
180 box.check(this->refcount() == 1, "invalid refcount %d (should be 1)", this->refcount());
181 }
182
183 ink_atomic_increment(&nobjects, 1);
184 }
185
~RegressionConfigRegressionConfig186 ~RegressionConfig() override
187 {
188 TestBox box(this->test, this->pstatus);
189
190 box.check(this->refcount() == 0, "invalid refcount %d (should be 0)", this->refcount());
191
192 // If we are the last config to be scheduled, pass the test.
193 // Otherwise, verify that the test is still running ...
194 if (REGRESSION_CONFIG_LAST & flags) {
195 *this->pstatus = REGRESSION_TEST_PASSED;
196 } else {
197 box.check(*this->pstatus == REGRESSION_TEST_INPROGRESS, "intermediate config out of sequence, *pstatus is %d", *pstatus);
198 }
199
200 ink_atomic_increment(&nobjects, -1);
201 }
202
203 RegressionTest *test;
204 int *pstatus;
205 unsigned flags;
206 };
207
208 int RegressionConfig::nobjects = 0;
209
210 struct ProxyConfig_Set_Completion {
ProxyConfig_Set_CompletionProxyConfig_Set_Completion211 ProxyConfig_Set_Completion(int _id, RegressionConfig *_c) : configid(_id), config(_c) {}
212 void
operator ()ProxyConfig_Set_Completion213 operator()() const
214 {
215 // Push one more RegressionConfig to force the LAST-tagged one to get destroyed.
216 rprintf(config->test, "setting LAST config object %p\n", config);
217 configProcessor.set(configid, config, 1);
218 }
219
220 int configid;
221 RegressionConfig *config;
222 };
223
224 // Test that ConfigProcessor::set() correctly releases the old ConfigInfo after a timeout.
EXCLUSIVE_REGRESSION_TEST(ProxyConfig_Set)225 EXCLUSIVE_REGRESSION_TEST(ProxyConfig_Set)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
226 {
227 int configid = 0;
228
229 *pstatus = REGRESSION_TEST_INPROGRESS;
230 RegressionConfig::nobjects = 0;
231
232 configid = configProcessor.set(configid, new RegressionConfig(test, pstatus, REGRESSION_CONFIG_FIRST), 1);
233 configid = configProcessor.set(configid, new RegressionConfig(test, pstatus, REGRESSION_CONFIG_FIRST), 1);
234 configid = configProcessor.set(configid, new RegressionConfig(test, pstatus, REGRESSION_CONFIG_FIRST), 1);
235 configid = configProcessor.set(configid, new RegressionConfig(test, pstatus, REGRESSION_CONFIG_FIRST), 1);
236 configid = configProcessor.set(configid, new RegressionConfig(test, pstatus, REGRESSION_CONFIG_FIRST), 1);
237 configid = configProcessor.set(configid, new RegressionConfig(test, pstatus, REGRESSION_CONFIG_FIRST), 1);
238 configid = configProcessor.set(configid, new RegressionConfig(test, pstatus, REGRESSION_CONFIG_LAST), 1);
239
240 // Wait until there's only 2 objects remaining, the one in ConfigProcessor, and the one we make here.
241 RegressionConfig::defer(2, ProxyConfig_Set_Completion(configid, new RegressionConfig(test, pstatus, 0)));
242 }
243
244 struct ProxyConfig_Release_Completion {
ProxyConfig_Release_CompletionProxyConfig_Release_Completion245 ProxyConfig_Release_Completion(int _id, RegressionConfig *_c) : configid(_id), config(_c) {}
246 void
operator ()ProxyConfig_Release_Completion247 operator()() const
248 {
249 // Release the reference count. Since we were keeping this alive, it should be the last to die.
250 configProcessor.release(configid, config);
251 }
252
253 int configid;
254 RegressionConfig *config;
255 };
256
257 // Test that ConfigProcessor::release() correctly releases the old ConfigInfo across an implicit
258 // release timeout.
EXCLUSIVE_REGRESSION_TEST(ProxyConfig_Release)259 EXCLUSIVE_REGRESSION_TEST(ProxyConfig_Release)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
260 {
261 int configid = 0;
262 RegressionConfig *config;
263
264 *pstatus = REGRESSION_TEST_INPROGRESS;
265 RegressionConfig::nobjects = 0;
266
267 // Set an initial config, then get it back to hold a reference count.
268 configid = configProcessor.set(configid, new RegressionConfig(test, pstatus, REGRESSION_CONFIG_LAST), 1);
269 config = (RegressionConfig *)configProcessor.get(configid);
270
271 // Now update the config a few times.
272 configid = configProcessor.set(configid, new RegressionConfig(test, pstatus, REGRESSION_CONFIG_FIRST), 1);
273 configid = configProcessor.set(configid, new RegressionConfig(test, pstatus, REGRESSION_CONFIG_FIRST), 1);
274 configid = configProcessor.set(configid, new RegressionConfig(test, pstatus, REGRESSION_CONFIG_FIRST), 1);
275
276 configid = configProcessor.set(configid, new RegressionConfig(test, pstatus, 0), 1);
277
278 // Defer the release of the object that we held back until there are only 2 left. The one we are holding
279 // and the one in the ConfigProcessor. Then releasing the one we hold will trigger the LAST check
280 RegressionConfig::defer(2, ProxyConfig_Release_Completion(configid, config));
281 }
282
283 #endif /* TS_HAS_TESTS */
284