1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <stdio.h> 18#include <stdlib.h> 19 20#include "apr_general.h" 21#include "apu.h" 22#include "apr_reslist.h" 23#include "apr_thread_pool.h" 24 25#if APR_HAVE_TIME_H 26#include <time.h> 27#endif /* APR_HAVE_TIME_H */ 28 29#include "abts.h" 30#include "testutil.h" 31 32#if APR_HAS_THREADS 33 34#define RESLIST_MIN 3 35#define RESLIST_SMAX 10 36#define RESLIST_HMAX 20 37#define RESLIST_TTL APR_TIME_C(35000) /* 35 ms */ 38#define CONSUMER_THREADS 25 39#define CONSUMER_ITERATIONS 250 40#define CONSTRUCT_SLEEP_TIME APR_TIME_C(25000) /* 25 ms */ 41#define DESTRUCT_SLEEP_TIME APR_TIME_C(10000) /* 10 ms */ 42#define WORK_DELAY_SLEEP_TIME APR_TIME_C(15000) /* 15 ms */ 43 44typedef struct { 45 apr_interval_time_t sleep_upon_construct; 46 apr_interval_time_t sleep_upon_destruct; 47 int c_count; 48 int d_count; 49} my_parameters_t; 50 51typedef struct { 52 int id; 53} my_resource_t; 54 55/* Linear congruential generator */ 56static apr_uint32_t lgc(apr_uint32_t a) 57{ 58 apr_uint64_t z = a; 59 z *= 279470273; 60 z %= APR_UINT64_C(4294967291); 61 return (apr_uint32_t)z; 62} 63 64static apr_status_t my_constructor(void **resource, void *params, 65 apr_pool_t *pool) 66{ 67 my_resource_t *res; 68 my_parameters_t *my_params = params; 69 70 /* Create some resource */ 71 res = apr_palloc(pool, sizeof(*res)); 72 res->id = my_params->c_count++; 73 74 /* Sleep for awhile, to simulate construction overhead. */ 75 apr_sleep(my_params->sleep_upon_construct); 76 77 /* Set the resource so it can be managed by the reslist */ 78 *resource = res; 79 return APR_SUCCESS; 80} 81 82static apr_status_t my_destructor(void *resource, void *params, 83 apr_pool_t *pool) 84{ 85 my_resource_t *res = resource; 86 my_parameters_t *my_params = params; 87 res->id = my_params->d_count++; 88 89 apr_sleep(my_params->sleep_upon_destruct); 90 91 return APR_SUCCESS; 92} 93 94typedef struct { 95 int tid; 96 abts_case *tc; 97 apr_reslist_t *reslist; 98 apr_interval_time_t work_delay_sleep; 99} my_thread_info_t; 100 101/* MAX_UINT * .95 = 2**32 * .95 = 4080218931u */ 102#define PERCENT95th 4080218931u 103 104static void * APR_THREAD_FUNC resource_consuming_thread(apr_thread_t *thd, 105 void *data) 106{ 107 int i; 108 apr_uint32_t chance; 109 void *vp; 110 apr_status_t rv; 111 my_resource_t *res; 112 my_thread_info_t *thread_info = data; 113 apr_reslist_t *rl = thread_info->reslist; 114 115#if APR_HAS_RANDOM 116 apr_generate_random_bytes((void*)&chance, sizeof(chance)); 117#else 118 chance = (apr_uint32_t)(apr_time_now() % APR_TIME_C(4294967291)); 119#endif 120 121 for (i = 0; i < CONSUMER_ITERATIONS; i++) { 122 rv = apr_reslist_acquire(rl, &vp); 123 ABTS_INT_EQUAL(thread_info->tc, APR_SUCCESS, rv); 124 res = vp; 125 apr_sleep(thread_info->work_delay_sleep); 126 127 /* simulate a 5% chance of the resource being bad */ 128 chance = lgc(chance); 129 if ( chance < PERCENT95th ) { 130 rv = apr_reslist_release(rl, res); 131 ABTS_INT_EQUAL(thread_info->tc, APR_SUCCESS, rv); 132 } else { 133 rv = apr_reslist_invalidate(rl, res); 134 ABTS_INT_EQUAL(thread_info->tc, APR_SUCCESS, rv); 135 } 136 } 137 138 return APR_SUCCESS; 139} 140 141static void test_timeout(abts_case *tc, apr_reslist_t *rl) 142{ 143 apr_status_t rv; 144 my_resource_t *resources[RESLIST_HMAX]; 145 void *vp; 146 int i; 147 148 apr_reslist_timeout_set(rl, 1000); 149 150 /* deplete all possible resources from the resource list 151 * so that the next call will block until timeout is reached 152 * (since there are no other threads to make a resource 153 * available) 154 */ 155 156 for (i = 0; i < RESLIST_HMAX; i++) { 157 rv = apr_reslist_acquire(rl, (void**)&resources[i]); 158 ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); 159 } 160 161 /* next call will block until timeout is reached */ 162 rv = apr_reslist_acquire(rl, &vp); 163 ABTS_TRUE(tc, APR_STATUS_IS_TIMEUP(rv)); 164 165 /* release the resources; otherwise the destroy operation 166 * will blow 167 */ 168 for (i = 0; i < RESLIST_HMAX; i++) { 169 rv = apr_reslist_release(rl, resources[i]); 170 ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); 171 } 172} 173 174static void test_shrinking(abts_case *tc, apr_reslist_t *rl) 175{ 176 apr_status_t rv; 177 my_resource_t *resources[RESLIST_HMAX]; 178 my_resource_t *res; 179 void *vp; 180 int i; 181 int sleep_time = RESLIST_TTL / RESLIST_HMAX; 182 183 /* deplete all possible resources from the resource list */ 184 for (i = 0; i < RESLIST_HMAX; i++) { 185 rv = apr_reslist_acquire(rl, (void**)&resources[i]); 186 ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); 187 } 188 189 /* Free all resources above RESLIST_SMAX - 1 */ 190 for (i = RESLIST_SMAX - 1; i < RESLIST_HMAX; i++) { 191 rv = apr_reslist_release(rl, resources[i]); 192 ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); 193 } 194 195 for (i = 0; i < RESLIST_HMAX; i++) { 196 rv = apr_reslist_acquire(rl, &vp); 197 ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); 198 res = vp; 199 apr_sleep(sleep_time); 200 rv = apr_reslist_release(rl, res); 201 ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); 202 } 203 apr_sleep(sleep_time); 204 205 /* 206 * Now free the remaining elements. This should trigger the shrinking of 207 * the list 208 */ 209 for (i = 0; i < RESLIST_SMAX - 1; i++) { 210 rv = apr_reslist_release(rl, resources[i]); 211 ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); 212 } 213} 214 215static void test_reslist(abts_case *tc, void *data) 216{ 217 int i; 218 apr_status_t rv; 219 apr_reslist_t *rl; 220 my_parameters_t *params; 221 apr_thread_pool_t *thrp; 222 my_thread_info_t thread_info[CONSUMER_THREADS]; 223 224 rv = apr_thread_pool_create(&thrp, CONSUMER_THREADS/2, CONSUMER_THREADS, p); 225 ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); 226 227 /* Create some parameters that will be passed into each 228 * constructor and destructor call. */ 229 params = apr_pcalloc(p, sizeof(*params)); 230 params->sleep_upon_construct = CONSTRUCT_SLEEP_TIME; 231 params->sleep_upon_destruct = DESTRUCT_SLEEP_TIME; 232 233 /* We're going to want 10 blocks of data from our target rmm. */ 234 rv = apr_reslist_create(&rl, RESLIST_MIN, RESLIST_SMAX, RESLIST_HMAX, 235 RESLIST_TTL, my_constructor, my_destructor, 236 params, p); 237 ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); 238 239 for (i = 0; i < CONSUMER_THREADS; i++) { 240 thread_info[i].tid = i; 241 thread_info[i].tc = tc; 242 thread_info[i].reslist = rl; 243 thread_info[i].work_delay_sleep = WORK_DELAY_SLEEP_TIME; 244 rv = apr_thread_pool_push(thrp, resource_consuming_thread, 245 &thread_info[i], 0, NULL); 246 ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); 247 } 248 249 rv = apr_thread_pool_destroy(thrp); 250 ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); 251 252 test_timeout(tc, rl); 253 254 test_shrinking(tc, rl); 255 ABTS_INT_EQUAL(tc, RESLIST_SMAX, params->c_count - params->d_count); 256 257 rv = apr_reslist_destroy(rl); 258 ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); 259} 260 261#endif /* APR_HAS_THREADS */ 262 263abts_suite *testreslist(abts_suite *suite) 264{ 265 suite = ADD_SUITE(suite); 266 267#if APR_HAS_THREADS 268 abts_run_test(suite, test_reslist, NULL); 269#endif 270 271 return suite; 272} 273