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 "apr_thread_proc.h" 18#include "apr_file_io.h" 19#include "apr_thread_mutex.h" 20#include "apr_thread_rwlock.h" 21#include "apr_thread_cond.h" 22#include "apr_errno.h" 23#include "apr_general.h" 24#include "apr_getopt.h" 25#include "testutil.h" 26 27#if APR_HAS_THREADS 28 29#define MAX_ITER 40000 30#define MAX_COUNTER 100000 31#define MAX_RETRY 5 32 33static void *APR_THREAD_FUNC thread_rwlock_func(apr_thread_t *thd, void *data); 34static void *APR_THREAD_FUNC thread_mutex_function(apr_thread_t *thd, void *data); 35static void *APR_THREAD_FUNC thread_cond_producer(apr_thread_t *thd, void *data); 36static void *APR_THREAD_FUNC thread_cond_consumer(apr_thread_t *thd, void *data); 37 38static apr_thread_mutex_t *thread_mutex; 39static apr_thread_rwlock_t *rwlock; 40static int i = 0, x = 0; 41 42static int buff[MAX_COUNTER]; 43 44struct { 45 apr_thread_mutex_t *mutex; 46 int nput; 47 int nval; 48} put; 49 50struct { 51 apr_thread_mutex_t *mutex; 52 apr_thread_cond_t *cond; 53 int nready; 54} nready; 55 56static apr_thread_mutex_t *timeout_mutex; 57static apr_thread_cond_t *timeout_cond; 58 59static void *APR_THREAD_FUNC thread_rwlock_func(apr_thread_t *thd, void *data) 60{ 61 int exitLoop = 1; 62 63 while (1) 64 { 65 apr_thread_rwlock_rdlock(rwlock); 66 if (i == MAX_ITER) 67 exitLoop = 0; 68 apr_thread_rwlock_unlock(rwlock); 69 70 if (!exitLoop) 71 break; 72 73 apr_thread_rwlock_wrlock(rwlock); 74 if (i != MAX_ITER) 75 { 76 i++; 77 x++; 78 } 79 apr_thread_rwlock_unlock(rwlock); 80 } 81 return NULL; 82} 83 84static void *APR_THREAD_FUNC thread_mutex_function(apr_thread_t *thd, void *data) 85{ 86 int exitLoop = 1; 87 88 /* slight delay to allow things to settle */ 89 apr_sleep (1); 90 91 while (1) 92 { 93 apr_thread_mutex_lock(thread_mutex); 94 if (i == MAX_ITER) 95 exitLoop = 0; 96 else 97 { 98 i++; 99 x++; 100 } 101 apr_thread_mutex_unlock(thread_mutex); 102 103 if (!exitLoop) 104 break; 105 } 106 return NULL; 107} 108 109static void *APR_THREAD_FUNC thread_cond_producer(apr_thread_t *thd, void *data) 110{ 111 for (;;) { 112 apr_thread_mutex_lock(put.mutex); 113 if (put.nput >= MAX_COUNTER) { 114 apr_thread_mutex_unlock(put.mutex); 115 return NULL; 116 } 117 buff[put.nput] = put.nval; 118 put.nput++; 119 put.nval++; 120 apr_thread_mutex_unlock(put.mutex); 121 122 apr_thread_mutex_lock(nready.mutex); 123 if (nready.nready == 0) 124 apr_thread_cond_signal(nready.cond); 125 nready.nready++; 126 apr_thread_mutex_unlock(nready.mutex); 127 128 *((int *) data) += 1; 129 } 130 131 return NULL; 132} 133 134static void *APR_THREAD_FUNC thread_cond_consumer(apr_thread_t *thd, void *data) 135{ 136 int i; 137 138 for (i = 0; i < MAX_COUNTER; i++) { 139 apr_thread_mutex_lock(nready.mutex); 140 while (nready.nready == 0) 141 apr_thread_cond_wait(nready.cond, nready.mutex); 142 nready.nready--; 143 apr_thread_mutex_unlock(nready.mutex); 144 145 if (buff[i] != i) 146 printf("buff[%d] = %d\n", i, buff[i]); 147 } 148 149 return NULL; 150} 151 152static void test_thread_mutex(abts_case *tc, void *data) 153{ 154 apr_thread_t *t1, *t2, *t3, *t4; 155 apr_status_t s1, s2, s3, s4; 156 157 s1 = apr_thread_mutex_create(&thread_mutex, APR_THREAD_MUTEX_DEFAULT, p); 158 ABTS_INT_EQUAL(tc, APR_SUCCESS, s1); 159 ABTS_PTR_NOTNULL(tc, thread_mutex); 160 161 i = 0; 162 x = 0; 163 164 s1 = apr_thread_create(&t1, NULL, thread_mutex_function, NULL, p); 165 ABTS_INT_EQUAL(tc, APR_SUCCESS, s1); 166 s2 = apr_thread_create(&t2, NULL, thread_mutex_function, NULL, p); 167 ABTS_INT_EQUAL(tc, APR_SUCCESS, s2); 168 s3 = apr_thread_create(&t3, NULL, thread_mutex_function, NULL, p); 169 ABTS_INT_EQUAL(tc, APR_SUCCESS, s3); 170 s4 = apr_thread_create(&t4, NULL, thread_mutex_function, NULL, p); 171 ABTS_INT_EQUAL(tc, APR_SUCCESS, s4); 172 173 apr_thread_join(&s1, t1); 174 apr_thread_join(&s2, t2); 175 apr_thread_join(&s3, t3); 176 apr_thread_join(&s4, t4); 177 178 ABTS_INT_EQUAL(tc, MAX_ITER, x); 179} 180 181static void test_thread_rwlock(abts_case *tc, void *data) 182{ 183 apr_thread_t *t1, *t2, *t3, *t4; 184 apr_status_t s1, s2, s3, s4; 185 186 s1 = apr_thread_rwlock_create(&rwlock, p); 187 if (s1 == APR_ENOTIMPL) { 188 ABTS_NOT_IMPL(tc, "rwlocks not implemented"); 189 return; 190 } 191 APR_ASSERT_SUCCESS(tc, "rwlock_create", s1); 192 ABTS_PTR_NOTNULL(tc, rwlock); 193 194 i = 0; 195 x = 0; 196 197 s1 = apr_thread_create(&t1, NULL, thread_rwlock_func, NULL, p); 198 APR_ASSERT_SUCCESS(tc, "create thread 1", s1); 199 s2 = apr_thread_create(&t2, NULL, thread_rwlock_func, NULL, p); 200 APR_ASSERT_SUCCESS(tc, "create thread 2", s2); 201 s3 = apr_thread_create(&t3, NULL, thread_rwlock_func, NULL, p); 202 APR_ASSERT_SUCCESS(tc, "create thread 3", s3); 203 s4 = apr_thread_create(&t4, NULL, thread_rwlock_func, NULL, p); 204 APR_ASSERT_SUCCESS(tc, "create thread 4", s4); 205 206 apr_thread_join(&s1, t1); 207 apr_thread_join(&s2, t2); 208 apr_thread_join(&s3, t3); 209 apr_thread_join(&s4, t4); 210 211 ABTS_INT_EQUAL(tc, MAX_ITER, x); 212 213 apr_thread_rwlock_destroy(rwlock); 214} 215 216static void test_cond(abts_case *tc, void *data) 217{ 218 apr_thread_t *p1, *p2, *p3, *p4, *c1; 219 apr_status_t s0, s1, s2, s3, s4; 220 int count1, count2, count3, count4; 221 int sum; 222 223 APR_ASSERT_SUCCESS(tc, "create put mutex", 224 apr_thread_mutex_create(&put.mutex, 225 APR_THREAD_MUTEX_DEFAULT, p)); 226 ABTS_PTR_NOTNULL(tc, put.mutex); 227 228 APR_ASSERT_SUCCESS(tc, "create nready mutex", 229 apr_thread_mutex_create(&nready.mutex, 230 APR_THREAD_MUTEX_DEFAULT, p)); 231 ABTS_PTR_NOTNULL(tc, nready.mutex); 232 233 APR_ASSERT_SUCCESS(tc, "create condvar", 234 apr_thread_cond_create(&nready.cond, p)); 235 ABTS_PTR_NOTNULL(tc, nready.cond); 236 237 count1 = count2 = count3 = count4 = 0; 238 put.nput = put.nval = 0; 239 nready.nready = 0; 240 i = 0; 241 x = 0; 242 243 s0 = apr_thread_create(&p1, NULL, thread_cond_producer, &count1, p); 244 ABTS_INT_EQUAL(tc, APR_SUCCESS, s0); 245 s1 = apr_thread_create(&p2, NULL, thread_cond_producer, &count2, p); 246 ABTS_INT_EQUAL(tc, APR_SUCCESS, s1); 247 s2 = apr_thread_create(&p3, NULL, thread_cond_producer, &count3, p); 248 ABTS_INT_EQUAL(tc, APR_SUCCESS, s2); 249 s3 = apr_thread_create(&p4, NULL, thread_cond_producer, &count4, p); 250 ABTS_INT_EQUAL(tc, APR_SUCCESS, s3); 251 s4 = apr_thread_create(&c1, NULL, thread_cond_consumer, NULL, p); 252 ABTS_INT_EQUAL(tc, APR_SUCCESS, s4); 253 254 apr_thread_join(&s0, p1); 255 apr_thread_join(&s1, p2); 256 apr_thread_join(&s2, p3); 257 apr_thread_join(&s3, p4); 258 apr_thread_join(&s4, c1); 259 260 APR_ASSERT_SUCCESS(tc, "destroy condvar", 261 apr_thread_cond_destroy(nready.cond)); 262 263 sum = count1 + count2 + count3 + count4; 264 /* 265 printf("count1 = %d count2 = %d count3 = %d count4 = %d\n", 266 count1, count2, count3, count4); 267 */ 268 ABTS_INT_EQUAL(tc, MAX_COUNTER, sum); 269} 270 271static void test_timeoutcond(abts_case *tc, void *data) 272{ 273 apr_status_t s; 274 apr_interval_time_t timeout; 275 apr_time_t begin, end; 276 int i; 277 278 s = apr_thread_mutex_create(&timeout_mutex, APR_THREAD_MUTEX_DEFAULT, p); 279 ABTS_INT_EQUAL(tc, APR_SUCCESS, s); 280 ABTS_PTR_NOTNULL(tc, timeout_mutex); 281 282 s = apr_thread_cond_create(&timeout_cond, p); 283 ABTS_INT_EQUAL(tc, APR_SUCCESS, s); 284 ABTS_PTR_NOTNULL(tc, timeout_cond); 285 286 timeout = apr_time_from_sec(5); 287 288 for (i = 0; i < MAX_RETRY; i++) { 289 apr_thread_mutex_lock(timeout_mutex); 290 291 begin = apr_time_now(); 292 s = apr_thread_cond_timedwait(timeout_cond, timeout_mutex, timeout); 293 end = apr_time_now(); 294 apr_thread_mutex_unlock(timeout_mutex); 295 296 if (s != APR_SUCCESS && !APR_STATUS_IS_TIMEUP(s)) { 297 continue; 298 } 299 ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(s)); 300 ABTS_ASSERT(tc, "Timer returned too late", end - begin - timeout < 100000); 301 break; 302 } 303 ABTS_ASSERT(tc, "Too many retries", i < MAX_RETRY); 304 APR_ASSERT_SUCCESS(tc, "Unable to destroy the conditional", 305 apr_thread_cond_destroy(timeout_cond)); 306} 307 308#endif /* !APR_HAS_THREADS */ 309 310#if !APR_HAS_THREADS 311static void threads_not_impl(abts_case *tc, void *data) 312{ 313 ABTS_NOT_IMPL(tc, "Threads not implemented on this platform"); 314} 315#endif 316 317 318abts_suite *testlock(abts_suite *suite) 319{ 320 suite = ADD_SUITE(suite) 321 322#if !APR_HAS_THREADS 323 abts_run_test(suite, threads_not_impl, NULL); 324#else 325 abts_run_test(suite, test_thread_mutex, NULL); 326 abts_run_test(suite, test_thread_rwlock, NULL); 327 abts_run_test(suite, test_cond, NULL); 328 abts_run_test(suite, test_timeoutcond, NULL); 329#endif 330 331 return suite; 332} 333 334