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_private.h" 18#include "apr_arch_threadproc.h" 19#include "apr_thread_proc.h" 20#include "apr_general.h" 21#include "apr_lib.h" 22#include "apr_portable.h" 23#if APR_HAVE_PROCESS_H 24#include <process.h> 25#endif 26#include "apr_arch_misc.h" 27 28/* Chosen for us by apr_initialize */ 29DWORD tls_apr_thread = 0; 30 31APR_DECLARE(apr_status_t) apr_threadattr_create(apr_threadattr_t **new, 32 apr_pool_t *pool) 33{ 34 (*new) = (apr_threadattr_t *)apr_palloc(pool, 35 sizeof(apr_threadattr_t)); 36 37 if ((*new) == NULL) { 38 return APR_ENOMEM; 39 } 40 41 (*new)->pool = pool; 42 (*new)->detach = 0; 43 (*new)->stacksize = 0; 44 45 return APR_SUCCESS; 46} 47 48APR_DECLARE(apr_status_t) apr_threadattr_detach_set(apr_threadattr_t *attr, 49 apr_int32_t on) 50{ 51 attr->detach = on; 52 return APR_SUCCESS; 53} 54 55APR_DECLARE(apr_status_t) apr_threadattr_detach_get(apr_threadattr_t *attr) 56{ 57 if (attr->detach == 1) 58 return APR_DETACH; 59 return APR_NOTDETACH; 60} 61 62APR_DECLARE(apr_status_t) apr_threadattr_stacksize_set(apr_threadattr_t *attr, 63 apr_size_t stacksize) 64{ 65 attr->stacksize = stacksize; 66 return APR_SUCCESS; 67} 68 69APR_DECLARE(apr_status_t) apr_threadattr_guardsize_set(apr_threadattr_t *attr, 70 apr_size_t size) 71{ 72 return APR_ENOTIMPL; 73} 74 75static void *dummy_worker(void *opaque) 76{ 77 apr_thread_t *thd = (apr_thread_t *)opaque; 78 TlsSetValue(tls_apr_thread, thd->td); 79 return thd->func(thd, thd->data); 80} 81 82APR_DECLARE(apr_status_t) apr_thread_create(apr_thread_t **new, 83 apr_threadattr_t *attr, 84 apr_thread_start_t func, 85 void *data, apr_pool_t *pool) 86{ 87 apr_status_t stat; 88 unsigned temp; 89 HANDLE handle; 90 91 (*new) = (apr_thread_t *)apr_palloc(pool, sizeof(apr_thread_t)); 92 93 if ((*new) == NULL) { 94 return APR_ENOMEM; 95 } 96 97 (*new)->data = data; 98 (*new)->func = func; 99 (*new)->td = NULL; 100 stat = apr_pool_create(&(*new)->pool, pool); 101 if (stat != APR_SUCCESS) { 102 return stat; 103 } 104 105 /* Use 0 for default Thread Stack Size, because that will 106 * default the stack to the same size as the calling thread. 107 */ 108#ifndef _WIN32_WCE 109 if ((handle = (HANDLE)_beginthreadex(NULL, 110 (DWORD) (attr ? attr->stacksize : 0), 111 (unsigned int (APR_THREAD_FUNC *)(void *))dummy_worker, 112 (*new), 0, &temp)) == 0) { 113 return APR_FROM_OS_ERROR(_doserrno); 114 } 115#else 116 if ((handle = CreateThread(NULL, 117 attr && attr->stacksize > 0 ? attr->stacksize : 0, 118 (unsigned int (APR_THREAD_FUNC *)(void *))dummy_worker, 119 (*new), 0, &temp)) == 0) { 120 return apr_get_os_error(); 121 } 122#endif 123 if (attr && attr->detach) { 124 CloseHandle(handle); 125 } 126 else 127 (*new)->td = handle; 128 129 return APR_SUCCESS; 130} 131 132APR_DECLARE(apr_status_t) apr_thread_exit(apr_thread_t *thd, 133 apr_status_t retval) 134{ 135 thd->exitval = retval; 136 apr_pool_destroy(thd->pool); 137 thd->pool = NULL; 138#ifndef _WIN32_WCE 139 _endthreadex(0); 140#else 141 ExitThread(0); 142#endif 143 return APR_SUCCESS; 144} 145 146APR_DECLARE(apr_status_t) apr_thread_join(apr_status_t *retval, 147 apr_thread_t *thd) 148{ 149 apr_status_t rv = APR_SUCCESS; 150 151 if (!thd->td) { 152 /* Can not join on detached threads */ 153 return APR_DETACH; 154 } 155 rv = WaitForSingleObject(thd->td, INFINITE); 156 if ( rv == WAIT_OBJECT_0 || rv == WAIT_ABANDONED) { 157 /* If the thread_exit has been called */ 158 if (!thd->pool) 159 *retval = thd->exitval; 160 else 161 rv = APR_INCOMPLETE; 162 } 163 else 164 rv = apr_get_os_error(); 165 CloseHandle(thd->td); 166 thd->td = NULL; 167 168 return rv; 169} 170 171APR_DECLARE(apr_status_t) apr_thread_detach(apr_thread_t *thd) 172{ 173 if (thd->td && CloseHandle(thd->td)) { 174 thd->td = NULL; 175 return APR_SUCCESS; 176 } 177 else { 178 return apr_get_os_error(); 179 } 180} 181 182APR_DECLARE(void) apr_thread_yield() 183{ 184 /* SwitchToThread is not supported on Win9x, but since it's 185 * primarily a noop (entering time consuming code, therefore 186 * providing more critical threads a bit larger timeslice) 187 * we won't worry too much if it's not available. 188 */ 189#ifndef _WIN32_WCE 190 if (apr_os_level >= APR_WIN_NT) { 191 SwitchToThread(); 192 } 193#endif 194} 195 196APR_DECLARE(apr_status_t) apr_thread_data_get(void **data, const char *key, 197 apr_thread_t *thread) 198{ 199 return apr_pool_userdata_get(data, key, thread->pool); 200} 201 202APR_DECLARE(apr_status_t) apr_thread_data_set(void *data, const char *key, 203 apr_status_t (*cleanup) (void *), 204 apr_thread_t *thread) 205{ 206 return apr_pool_userdata_set(data, key, cleanup, thread->pool); 207} 208 209 210APR_DECLARE(apr_os_thread_t) apr_os_thread_current(void) 211{ 212 HANDLE hthread = (HANDLE)TlsGetValue(tls_apr_thread); 213 HANDLE hproc; 214 215 if (hthread) { 216 return hthread; 217 } 218 219 hproc = GetCurrentProcess(); 220 hthread = GetCurrentThread(); 221 if (!DuplicateHandle(hproc, hthread, 222 hproc, &hthread, 0, FALSE, 223 DUPLICATE_SAME_ACCESS)) { 224 return NULL; 225 } 226 TlsSetValue(tls_apr_thread, hthread); 227 return hthread; 228} 229 230APR_DECLARE(apr_status_t) apr_os_thread_get(apr_os_thread_t **thethd, 231 apr_thread_t *thd) 232{ 233 if (thd == NULL) { 234 return APR_ENOTHREAD; 235 } 236 *thethd = thd->td; 237 return APR_SUCCESS; 238} 239 240APR_DECLARE(apr_status_t) apr_os_thread_put(apr_thread_t **thd, 241 apr_os_thread_t *thethd, 242 apr_pool_t *pool) 243{ 244 if (pool == NULL) { 245 return APR_ENOPOOL; 246 } 247 if ((*thd) == NULL) { 248 (*thd) = (apr_thread_t *)apr_palloc(pool, sizeof(apr_thread_t)); 249 (*thd)->pool = pool; 250 } 251 (*thd)->td = thethd; 252 return APR_SUCCESS; 253} 254 255APR_DECLARE(apr_status_t) apr_thread_once_init(apr_thread_once_t **control, 256 apr_pool_t *p) 257{ 258 (*control) = apr_pcalloc(p, sizeof(**control)); 259 return APR_SUCCESS; 260} 261 262APR_DECLARE(apr_status_t) apr_thread_once(apr_thread_once_t *control, 263 void (*func)(void)) 264{ 265 if (!InterlockedExchange(&control->value, 1)) { 266 func(); 267 } 268 return APR_SUCCESS; 269} 270 271APR_DECLARE(int) apr_os_thread_equal(apr_os_thread_t tid1, 272 apr_os_thread_t tid2) 273{ 274 /* Since the only tid's we support our are own, and 275 * apr_os_thread_current returns the identical handle 276 * to the one we created initially, the test is simple. 277 */ 278 return (tid1 == tid2); 279} 280 281APR_POOL_IMPLEMENT_ACCESSOR(thread) 282