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_arch_thread_mutex.h" 18#include "apr_arch_thread_cond.h" 19#include "apr_strings.h" 20#include "apr_portable.h" 21 22static apr_status_t thread_cond_cleanup(void *data) 23{ 24 struct waiter *w; 25 apr_thread_cond_t *cond = (apr_thread_cond_t *)data; 26 27 acquire_sem(cond->lock); 28 delete_sem(cond->lock); 29 30 return APR_SUCCESS; 31} 32 33static struct waiter_t *make_waiter(apr_pool_t *pool) 34{ 35 struct waiter_t *w = (struct waiter_t*) 36 apr_palloc(pool, sizeof(struct waiter_t)); 37 if (w == NULL) 38 return NULL; 39 40 w->sem = create_sem(0, "apr conditional waiter"); 41 if (w->sem < 0) 42 return NULL; 43 44 APR_RING_ELEM_INIT(w, link); 45 46 return w; 47} 48 49APR_DECLARE(apr_status_t) apr_thread_cond_create(apr_thread_cond_t **cond, 50 apr_pool_t *pool) 51{ 52 apr_thread_cond_t *new_cond; 53 sem_id rv; 54 int i; 55 56 new_cond = (apr_thread_cond_t *)apr_palloc(pool, sizeof(apr_thread_cond_t)); 57 58 if (new_cond == NULL) 59 return APR_ENOMEM; 60 61 if ((rv = create_sem(1, "apr conditional lock")) < B_OK) 62 return rv; 63 64 new_cond->lock = rv; 65 new_cond->pool = pool; 66 APR_RING_INIT(&new_cond->alist, waiter_t, link); 67 APR_RING_INIT(&new_cond->flist, waiter_t, link); 68 69 for (i=0;i < 10 ;i++) { 70 struct waiter_t *nw = make_waiter(pool); 71 APR_RING_INSERT_TAIL(&new_cond->flist, nw, waiter_t, link); 72 } 73 74 apr_pool_cleanup_register(new_cond->pool, 75 (void *)new_cond, thread_cond_cleanup, 76 apr_pool_cleanup_null); 77 78 *cond = new_cond; 79 return APR_SUCCESS; 80} 81 82 83static apr_status_t do_wait(apr_thread_cond_t *cond, apr_thread_mutex_t *mutex, 84 int timeout) 85{ 86 struct waiter_t *wait; 87 thread_id cth = find_thread(NULL); 88 apr_status_t rv; 89 int flags = B_RELATIVE_TIMEOUT; 90 91 /* We must be the owner of the mutex or we can't do this... */ 92 if (mutex->owner != cth) { 93 /* What should we return??? */ 94 return APR_EINVAL; 95 } 96 97 acquire_sem(cond->lock); 98 wait = APR_RING_FIRST(&cond->flist); 99 if (wait) 100 APR_RING_REMOVE(wait, link); 101 else 102 wait = make_waiter(cond->pool); 103 APR_RING_INSERT_TAIL(&cond->alist, wait, waiter_t, link); 104 cond->condlock = mutex; 105 release_sem(cond->lock); 106 107 apr_thread_mutex_unlock(cond->condlock); 108 109 if (timeout == 0) 110 flags = 0; 111 112 rv = acquire_sem_etc(wait->sem, 1, flags, timeout); 113 114 apr_thread_mutex_lock(cond->condlock); 115 116 if (rv != B_OK) 117 if (rv == B_TIMED_OUT) 118 return APR_TIMEUP; 119 return rv; 120 121 acquire_sem(cond->lock); 122 APR_RING_REMOVE(wait, link); 123 APR_RING_INSERT_TAIL(&cond->flist, wait, waiter_t, link); 124 release_sem(cond->lock); 125 126 return APR_SUCCESS; 127} 128 129APR_DECLARE(apr_status_t) apr_thread_cond_wait(apr_thread_cond_t *cond, 130 apr_thread_mutex_t *mutex) 131{ 132 return do_wait(cond, mutex, 0); 133} 134 135APR_DECLARE(apr_status_t) apr_thread_cond_timedwait(apr_thread_cond_t *cond, 136 apr_thread_mutex_t *mutex, 137 apr_interval_time_t timeout) 138{ 139 return do_wait(cond, mutex, timeout); 140} 141 142APR_DECLARE(apr_status_t) apr_thread_cond_signal(apr_thread_cond_t *cond) 143{ 144 struct waiter_t *wake; 145 146 acquire_sem(cond->lock); 147 if (!APR_RING_EMPTY(&cond->alist, waiter_t, link)) { 148 wake = APR_RING_FIRST(&cond->alist); 149 APR_RING_REMOVE(wake, link); 150 release_sem(wake->sem); 151 APR_RING_INSERT_TAIL(&cond->flist, wake, waiter_t, link); 152 } 153 release_sem(cond->lock); 154 155 return APR_SUCCESS; 156} 157 158APR_DECLARE(apr_status_t) apr_thread_cond_broadcast(apr_thread_cond_t *cond) 159{ 160 struct waiter_t *wake; 161 162 acquire_sem(cond->lock); 163 while (! APR_RING_EMPTY(&cond->alist, waiter_t, link)) { 164 wake = APR_RING_FIRST(&cond->alist); 165 APR_RING_REMOVE(wake, link); 166 release_sem(wake->sem); 167 APR_RING_INSERT_TAIL(&cond->flist, wake, waiter_t, link); 168 } 169 release_sem(cond->lock); 170 171 return APR_SUCCESS; 172} 173 174APR_DECLARE(apr_status_t) apr_thread_cond_destroy(apr_thread_cond_t *cond) 175{ 176 apr_status_t stat; 177 if ((stat = thread_cond_cleanup(cond)) == APR_SUCCESS) { 178 apr_pool_cleanup_kill(cond->pool, cond, thread_cond_cleanup); 179 return APR_SUCCESS; 180 } 181 return stat; 182} 183 184APR_POOL_IMPLEMENT_ACCESSOR(thread_cond) 185 186