1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 #include <cstring>
20 #include <stdlib.h> // for abort
21 #include <ts/ts.h>  // for debug
22 
23 // debug messages viewable by setting 'proxy.config.diags.debug.tags'
24 // in 'records.config'
25 
26 // debug messages during one-time initialization
27 static const char DEBUG_TAG_INIT[] = "TSContSchedule_test.init";
28 static const char DEBUG_TAG_SCHD[] = "TSContSchedule_test.schedule";
29 static const char DEBUG_TAG_HDL[]  = "TSContSchedule_test.handler";
30 static const char DEBUG_TAG_CHK[]  = "TSContSchedule_test.check";
31 
32 // plugin registration info
33 static char plugin_name[]   = "TSContSchedule_test";
34 static char vendor_name[]   = "apache";
35 static char support_email[] = "duke8253@apache.org";
36 
37 static int test_flag = 0;
38 
39 static TSEventThread thread_1 = nullptr;
40 static TSEventThread thread_2 = nullptr;
41 
42 static TSCont contp_1 = nullptr;
43 static TSCont contp_2 = nullptr;
44 
45 static int TSContSchedule_handler_1(TSCont contp, TSEvent event, void *edata);
46 static int TSContSchedule_handler_2(TSCont contp, TSEvent event, void *edata);
47 static int TSContScheduleOnPool_handler_1(TSCont contp, TSEvent event, void *edata);
48 static int TSContScheduleOnPool_handler_2(TSCont contp, TSEvent event, void *edata);
49 static int TSContScheduleOnThread_handler_1(TSCont contp, TSEvent event, void *edata);
50 static int TSContScheduleOnThread_handler_2(TSCont contp, TSEvent event, void *edata);
51 static int TSContThreadAffinity_handler(TSCont contp, TSEvent event, void *edata);
52 
53 static int
TSContSchedule_handler_1(TSCont contp,TSEvent event,void * edata)54 TSContSchedule_handler_1(TSCont contp, TSEvent event, void *edata)
55 {
56   TSDebug(DEBUG_TAG_HDL, "TSContSchedule handler 1 thread [%p]", TSThreadSelf());
57   if (thread_1 == nullptr) {
58     // First time entering this handler, before everything else starts.
59     thread_1 = TSEventThreadSelf();
60 
61     // Set the affinity of contp_2 to thread_1, and schedule it twice.
62     // Since it's on the same thread, we don't need a delay.
63     TSDebug(DEBUG_TAG_HDL, "[%s] scheduling continuation", plugin_name);
64     TSContThreadAffinitySet(contp_2, thread_1);
65     TSContSchedule(contp_2, 0);
66     TSContSchedule(contp_2, 0);
67   } else if (thread_2 == nullptr) {
68     TSDebug(DEBUG_TAG_CHK, "fail [schedule delay not applied]");
69   } else {
70     // Second time in here, should be after the two scheduled handler_2 runs.
71     // Since handler_1 has no affinity set, we should be on a different thread now.
72     // Also, thread_2 should be the same as thread_1, since thread_1 was set as
73     // affinity for handler_2.
74     if (thread_2 != TSEventThreadSelf() && thread_2 == thread_1) {
75       TSDebug(DEBUG_TAG_CHK, "pass [should not be the same thread]");
76     } else {
77       TSDebug(DEBUG_TAG_CHK, "fail [on the same thread]");
78     }
79   }
80   return 0;
81 }
82 
83 static int
TSContSchedule_handler_2(TSCont contp,TSEvent event,void * edata)84 TSContSchedule_handler_2(TSCont contp, TSEvent event, void *edata)
85 {
86   TSDebug(DEBUG_TAG_HDL, "TSContSchedule handler 2 thread [%p]", TSThreadSelf());
87   if (thread_2 == nullptr) {
88     // First time in this handler, should get here after handler_1,
89     // and also record the thread id.
90     thread_2 = TSEventThreadSelf();
91   } else if (thread_2 == TSEventThreadSelf()) {
92     // Second time in here, since the affinity is set to thread_1, we should be
93     // on the same thread as last time.
94     TSDebug(DEBUG_TAG_CHK, "pass [should be the same thread]");
95   } else {
96     TSDebug(DEBUG_TAG_CHK, "fail [not the same thread]");
97   }
98   return 0;
99 }
100 
101 void
TSContSchedule_test()102 TSContSchedule_test()
103 {
104   contp_1 = TSContCreate(TSContSchedule_handler_1, TSMutexCreate());
105   contp_2 = TSContCreate(TSContSchedule_handler_2, TSMutexCreate());
106 
107   if (contp_1 == nullptr || contp_2 == nullptr) {
108     TSDebug(DEBUG_TAG_SCHD, "[%s] could not create continuation", plugin_name);
109     abort();
110   } else {
111     TSDebug(DEBUG_TAG_SCHD, "[%s] scheduling continuation", plugin_name);
112     TSContScheduleOnPool(contp_1, 0, TS_THREAD_POOL_NET);
113     TSContThreadAffinityClear(contp_1);
114     TSContScheduleOnPool(contp_1, 200, TS_THREAD_POOL_NET);
115   }
116 }
117 
118 static int
TSContScheduleOnPool_handler_1(TSCont contp,TSEvent event,void * edata)119 TSContScheduleOnPool_handler_1(TSCont contp, TSEvent event, void *edata)
120 {
121   // This runs on ET_NET threads.
122   TSDebug(DEBUG_TAG_HDL, "TSContScheduleOnPool handler 1 thread [%p]", TSThreadSelf());
123   if (thread_1 == nullptr) {
124     // First time here, record thread id.
125     thread_1 = TSEventThreadSelf();
126   } else {
127     // Second time here, we should be on a different thread since affinity was cleared.
128     if (thread_1 != TSEventThreadSelf()) {
129       TSDebug(DEBUG_TAG_CHK, "pass [should not be the same thread]");
130     } else {
131       TSDebug(DEBUG_TAG_CHK, "fail [on the same thread]");
132     }
133   }
134   return 0;
135 }
136 
137 static int
TSContScheduleOnPool_handler_2(TSCont contp,TSEvent event,void * edata)138 TSContScheduleOnPool_handler_2(TSCont contp, TSEvent event, void *edata)
139 {
140   // This runs on ET_TASK threads.
141   TSDebug(DEBUG_TAG_HDL, "TSContScheduleOnPool handler 2 thread [%p]", TSThreadSelf());
142   if (thread_2 == nullptr) {
143     // First time here, record thread id.
144     thread_2 = TSEventThreadSelf();
145   } else {
146     if (thread_2 == TSEventThreadSelf()) {
147       // Second time there, we should be on the same thread even though affinity was cleared,
148       // reason being plugin is running on ET_TASK threads, and we were scheduled on ET_TASK
149       // threads as well, so the thread the plugin is on is used and set to affinity.
150       TSDebug(DEBUG_TAG_CHK, "pass [should be the same thread]");
151     } else {
152       TSDebug(DEBUG_TAG_CHK, "fail [not the same thread]");
153     }
154   }
155   return 0;
156 }
157 
158 void
TSContScheduleOnPool_test()159 TSContScheduleOnPool_test()
160 {
161   contp_1 = TSContCreate(TSContScheduleOnPool_handler_1, TSMutexCreate());
162   contp_2 = TSContCreate(TSContScheduleOnPool_handler_2, TSMutexCreate());
163 
164   if (contp_1 == nullptr || contp_2 == nullptr) {
165     TSDebug(DEBUG_TAG_SCHD, "[%s] could not create continuation", plugin_name);
166     abort();
167   } else {
168     TSDebug(DEBUG_TAG_SCHD, "[%s] scheduling continuation", plugin_name);
169 
170     TSContScheduleOnPool(contp_1, 0, TS_THREAD_POOL_NET);
171     TSContThreadAffinityClear(contp_1);
172     TSContScheduleOnPool(contp_1, 100, TS_THREAD_POOL_NET);
173 
174     TSContScheduleOnPool(contp_2, 200, TS_THREAD_POOL_TASK);
175     TSContThreadAffinityClear(contp_2);
176     TSContScheduleOnPool(contp_2, 300, TS_THREAD_POOL_TASK);
177   }
178 }
179 
180 static int
TSContScheduleOnThread_handler_1(TSCont contp,TSEvent event,void * edata)181 TSContScheduleOnThread_handler_1(TSCont contp, TSEvent event, void *edata)
182 {
183   // Mostly same as TSContSchedule_handler_1, no need to set affinity
184   // since we are scheduling directly on to a thread.
185   TSDebug(DEBUG_TAG_HDL, "TSContScheduleOnThread handler 1 thread [%p]", TSThreadSelf());
186   if (thread_1 == nullptr) {
187     thread_1 = TSEventThreadSelf();
188 
189     TSDebug(DEBUG_TAG_HDL, "[%s] scheduling continuation", plugin_name);
190     TSContScheduleOnThread(contp_2, 0, thread_1);
191     TSContScheduleOnThread(contp_2, 0, thread_1);
192   } else if (thread_2 == nullptr) {
193     TSDebug(DEBUG_TAG_CHK, "fail [schedule delay not applied]");
194   } else {
195     if (thread_2 != TSEventThreadSelf()) {
196       TSDebug(DEBUG_TAG_CHK, "pass [should not be the same thread]");
197     } else {
198       TSDebug(DEBUG_TAG_CHK, "fail [on the same thread]");
199     }
200   }
201   return 0;
202 }
203 
204 static int
TSContScheduleOnThread_handler_2(TSCont contp,TSEvent event,void * edata)205 TSContScheduleOnThread_handler_2(TSCont contp, TSEvent event, void *edata)
206 {
207   TSDebug(DEBUG_TAG_HDL, "TSContScheduleOnThread handler 2 thread [%p]", TSThreadSelf());
208   if (thread_2 == nullptr) {
209     thread_2 = TSEventThreadSelf();
210   } else if (thread_2 == TSEventThreadSelf()) {
211     TSDebug(DEBUG_TAG_CHK, "pass [should be the same thread]");
212   } else {
213     TSDebug(DEBUG_TAG_CHK, "fail [not the same thread]");
214   }
215   return 0;
216 }
217 
218 void
TSContScheduleOnThread_test()219 TSContScheduleOnThread_test()
220 {
221   contp_1 = TSContCreate(TSContScheduleOnThread_handler_1, TSMutexCreate());
222   contp_2 = TSContCreate(TSContScheduleOnThread_handler_2, TSMutexCreate());
223 
224   if (contp_1 == nullptr || contp_2 == nullptr) {
225     TSDebug(DEBUG_TAG_SCHD, "[%s] could not create continuation", plugin_name);
226     abort();
227   } else {
228     TSDebug(DEBUG_TAG_SCHD, "[%s] scheduling continuation", plugin_name);
229     TSContScheduleOnPool(contp_1, 0, TS_THREAD_POOL_NET);
230     TSContThreadAffinityClear(contp_1);
231     TSContScheduleOnPool(contp_1, 200, TS_THREAD_POOL_NET);
232   }
233 }
234 
235 static int
TSContThreadAffinity_handler(TSCont contp,TSEvent event,void * edata)236 TSContThreadAffinity_handler(TSCont contp, TSEvent event, void *edata)
237 {
238   TSDebug(DEBUG_TAG_HDL, "TSContThreadAffinity handler thread [%p]", TSThreadSelf());
239 
240   thread_1 = TSEventThreadSelf();
241 
242   if (TSContThreadAffinityGet(contp) != nullptr) {
243     TSDebug(DEBUG_TAG_CHK, "pass [affinity thread is not null]");
244     TSContThreadAffinityClear(contp);
245     if (TSContThreadAffinityGet(contp) == nullptr) {
246       TSDebug(DEBUG_TAG_CHK, "pass [affinity thread is cleared]");
247       TSContThreadAffinitySet(contp, TSEventThreadSelf());
248       if (TSContThreadAffinityGet(contp) == thread_1) {
249         TSDebug(DEBUG_TAG_CHK, "pass [affinity thread is set]");
250       } else {
251         TSDebug(DEBUG_TAG_CHK, "fail [affinity thread is not set]");
252       }
253     } else {
254       TSDebug(DEBUG_TAG_CHK, "fail [affinity thread is not cleared]");
255     }
256   } else {
257     TSDebug(DEBUG_TAG_CHK, "fail [affinity thread is null]");
258   }
259 
260   return 0;
261 }
262 
263 void
TSContThreadAffinity_test()264 TSContThreadAffinity_test()
265 {
266   TSCont contp = TSContCreate(TSContThreadAffinity_handler, TSMutexCreate());
267 
268   if (contp == nullptr) {
269     TSDebug(DEBUG_TAG_SCHD, "[%s] could not create continuation", plugin_name);
270     abort();
271   } else {
272     TSDebug(DEBUG_TAG_SCHD, "[%s] scheduling continuation", plugin_name);
273     TSContScheduleOnPool(contp, 0, TS_THREAD_POOL_NET);
274   }
275 }
276 
277 static int
LifecycleHookTracer(TSCont contp,TSEvent event,void * edata)278 LifecycleHookTracer(TSCont contp, TSEvent event, void *edata)
279 {
280   if (event == TS_EVENT_LIFECYCLE_TASK_THREADS_READY) {
281     switch (test_flag) {
282     case 1:
283       TSContSchedule_test();
284       break;
285     case 2:
286       TSContScheduleOnPool_test();
287       break;
288     case 3:
289       TSContScheduleOnThread_test();
290       break;
291     case 4:
292       TSContThreadAffinity_test();
293       break;
294     default:
295       break;
296     }
297   }
298   return 0;
299 }
300 
301 void
TSPluginInit(int argc,const char * argv[])302 TSPluginInit(int argc, const char *argv[])
303 {
304   if (argc == 1) {
305     TSDebug(DEBUG_TAG_INIT, "initializing plugin for testing TSContSchedule");
306     test_flag = 1;
307   } else if (argc == 2) {
308     int len = strlen(argv[1]);
309     if (len == 4 && strncmp(argv[1], "pool", 4) == 0) {
310       TSDebug(DEBUG_TAG_INIT, "initializing plugin for testing TSContScheduleOnPool");
311       test_flag = 2;
312     } else if (len == 6 && strncmp(argv[1], "thread", 6) == 0) {
313       TSDebug(DEBUG_TAG_INIT, "initializing plugin for testing TSContScheduleOnThread");
314       test_flag = 3;
315     } else if (len == 8 && strncmp(argv[1], "affinity", 8) == 0) {
316       TSDebug(DEBUG_TAG_INIT, "initializing plugin for testing TSContThreadAffinity");
317       test_flag = 4;
318     } else {
319       goto Lerror;
320     }
321   } else {
322     goto Lerror;
323   }
324 
325   TSPluginRegistrationInfo info;
326 
327   info.plugin_name   = plugin_name;
328   info.vendor_name   = vendor_name;
329   info.support_email = support_email;
330 
331   if (TSPluginRegister(&info) != TS_SUCCESS) {
332     TSDebug(DEBUG_TAG_INIT, "[%s] plugin registration failed", plugin_name);
333     abort();
334   }
335 
336   TSLifecycleHookAdd(TS_LIFECYCLE_TASK_THREADS_READY_HOOK, TSContCreate(LifecycleHookTracer, TSMutexCreate()));
337 
338   return;
339 
340 Lerror:
341   TSDebug(DEBUG_TAG_INIT, "[%s] plugin invalid argument", plugin_name);
342   abort();
343 }
344