tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

evthread_win32.c (8595B)


      1 /*
      2 * Copyright 2009-2012 Niels Provos and Nick Mathewson
      3 *
      4 * Redistribution and use in source and binary forms, with or without
      5 * modification, are permitted provided that the following conditions
      6 * are met:
      7 * 1. Redistributions of source code must retain the above copyright
      8 *    notice, this list of conditions and the following disclaimer.
      9 * 2. Redistributions in binary form must reproduce the above copyright
     10 *    notice, this list of conditions and the following disclaimer in the
     11 *    documentation and/or other materials provided with the distribution.
     12 * 3. The name of the author may not be used to endorse or promote products
     13 *    derived from this software without specific prior written permission.
     14 *
     15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25 */
     26 #include "event2/event-config.h"
     27 #include "evconfig-private.h"
     28 
     29 #ifdef _WIN32
     30 #ifndef _WIN32_WINNT
     31 /* Minimum required for InitializeCriticalSectionAndSpinCount */
     32 #define _WIN32_WINNT 0x0403
     33 #endif
     34 #include <winsock2.h>
     35 #define WIN32_LEAN_AND_MEAN
     36 #include <windows.h>
     37 #undef WIN32_LEAN_AND_MEAN
     38 #include <sys/locking.h>
     39 #endif
     40 
     41 struct event_base;
     42 #include "event2/thread.h"
     43 
     44 #include "mm-internal.h"
     45 #include "evthread-internal.h"
     46 #include "time-internal.h"
     47 
     48 #define SPIN_COUNT 2000
     49 
     50 static void *
     51 evthread_win32_lock_create(unsigned locktype)
     52 {
     53 CRITICAL_SECTION *lock = mm_malloc(sizeof(CRITICAL_SECTION));
     54 if (!lock)
     55 	return NULL;
     56 if (InitializeCriticalSectionAndSpinCount(lock, SPIN_COUNT) == 0) {
     57 	mm_free(lock);
     58 	return NULL;
     59 }
     60 return lock;
     61 }
     62 
     63 static void
     64 evthread_win32_lock_free(void *lock_, unsigned locktype)
     65 {
     66 CRITICAL_SECTION *lock = lock_;
     67 DeleteCriticalSection(lock);
     68 mm_free(lock);
     69 }
     70 
     71 static int
     72 evthread_win32_lock(unsigned mode, void *lock_)
     73 {
     74 CRITICAL_SECTION *lock = lock_;
     75 if ((mode & EVTHREAD_TRY)) {
     76 	return ! TryEnterCriticalSection(lock);
     77 } else {
     78 	EnterCriticalSection(lock);
     79 	return 0;
     80 }
     81 }
     82 
     83 static int
     84 evthread_win32_unlock(unsigned mode, void *lock_)
     85 {
     86 CRITICAL_SECTION *lock = lock_;
     87 LeaveCriticalSection(lock);
     88 return 0;
     89 }
     90 
     91 static unsigned long
     92 evthread_win32_get_id(void)
     93 {
     94 return (unsigned long) GetCurrentThreadId();
     95 }
     96 
     97 #ifdef WIN32_HAVE_CONDITION_VARIABLES
     98 static void WINAPI (*InitializeConditionVariable_fn)(PCONDITION_VARIABLE)
     99 = NULL;
    100 static BOOL WINAPI (*SleepConditionVariableCS_fn)(
    101 PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD) = NULL;
    102 static void WINAPI (*WakeAllConditionVariable_fn)(PCONDITION_VARIABLE) = NULL;
    103 static void WINAPI (*WakeConditionVariable_fn)(PCONDITION_VARIABLE) = NULL;
    104 
    105 static int
    106 evthread_win32_condvar_init(void)
    107 {
    108 HANDLE lib;
    109 
    110 lib = GetModuleHandle(TEXT("kernel32.dll"));
    111 if (lib == NULL)
    112 	return 0;
    113 
    114 #define LOAD(name)				\
    115 name##_fn = GetProcAddress(lib, #name)
    116 LOAD(InitializeConditionVariable);
    117 LOAD(SleepConditionVariableCS);
    118 LOAD(WakeAllConditionVariable);
    119 LOAD(WakeConditionVariable);
    120 
    121 return InitializeConditionVariable_fn && SleepConditionVariableCS_fn &&
    122     WakeAllConditionVariable_fn && WakeConditionVariable_fn;
    123 }
    124 
    125 /* XXXX Even if we can build this, we don't necessarily want to: the functions
    126 * in question didn't exist before Vista, so we'd better LoadProc them. */
    127 static void *
    128 evthread_win32_condvar_alloc(unsigned condflags)
    129 {
    130 CONDITION_VARIABLE *cond = mm_malloc(sizeof(CONDITION_VARIABLE));
    131 if (!cond)
    132 	return NULL;
    133 InitializeConditionVariable_fn(cond);
    134 return cond;
    135 }
    136 
    137 static void
    138 evthread_win32_condvar_free(void *cond_)
    139 {
    140 CONDITION_VARIABLE *cond = cond_;
    141 /* There doesn't _seem_ to be a cleaup fn here... */
    142 mm_free(cond);
    143 }
    144 
    145 static int
    146 evthread_win32_condvar_signal(void *cond, int broadcast)
    147 {
    148 CONDITION_VARIABLE *cond = cond_;
    149 if (broadcast)
    150 	WakeAllConditionVariable_fn(cond);
    151 else
    152 	WakeConditionVariable_fn(cond);
    153 return 0;
    154 }
    155 
    156 static int
    157 evthread_win32_condvar_wait(void *cond_, void *lock_, const struct timeval *tv)
    158 {
    159 CONDITION_VARIABLE *cond = cond_;
    160 CRITICAL_SECTION *lock = lock_;
    161 DWORD ms, err;
    162 BOOL result;
    163 
    164 if (tv)
    165 	ms = evutil_tv_to_msec_(tv);
    166 else
    167 	ms = INFINITE;
    168 result = SleepConditionVariableCS_fn(cond, lock, ms);
    169 if (result) {
    170 	if (GetLastError() == WAIT_TIMEOUT)
    171 		return 1;
    172 	else
    173 		return -1;
    174 } else {
    175 	return 0;
    176 }
    177 }
    178 #endif
    179 
    180 struct evthread_win32_cond {
    181 HANDLE event;
    182 
    183 CRITICAL_SECTION lock;
    184 int n_waiting;
    185 int n_to_wake;
    186 int generation;
    187 };
    188 
    189 static void *
    190 evthread_win32_cond_alloc(unsigned flags)
    191 {
    192 struct evthread_win32_cond *cond;
    193 if (!(cond = mm_malloc(sizeof(struct evthread_win32_cond))))
    194 	return NULL;
    195 if (InitializeCriticalSectionAndSpinCount(&cond->lock, SPIN_COUNT)==0) {
    196 	mm_free(cond);
    197 	return NULL;
    198 }
    199 if ((cond->event = CreateEvent(NULL,TRUE,FALSE,NULL)) == NULL) {
    200 	DeleteCriticalSection(&cond->lock);
    201 	mm_free(cond);
    202 	return NULL;
    203 }
    204 cond->n_waiting = cond->n_to_wake = cond->generation = 0;
    205 return cond;
    206 }
    207 
    208 static void
    209 evthread_win32_cond_free(void *cond_)
    210 {
    211 struct evthread_win32_cond *cond = cond_;
    212 DeleteCriticalSection(&cond->lock);
    213 CloseHandle(cond->event);
    214 mm_free(cond);
    215 }
    216 
    217 static int
    218 evthread_win32_cond_signal(void *cond_, int broadcast)
    219 {
    220 struct evthread_win32_cond *cond = cond_;
    221 EnterCriticalSection(&cond->lock);
    222 if (broadcast)
    223 	cond->n_to_wake = cond->n_waiting;
    224 else
    225 	++cond->n_to_wake;
    226 cond->generation++;
    227 SetEvent(cond->event);
    228 LeaveCriticalSection(&cond->lock);
    229 return 0;
    230 }
    231 
    232 static int
    233 evthread_win32_cond_wait(void *cond_, void *lock_, const struct timeval *tv)
    234 {
    235 struct evthread_win32_cond *cond = cond_;
    236 CRITICAL_SECTION *lock = lock_;
    237 int generation_at_start;
    238 int waiting = 1;
    239 int result = -1;
    240 DWORD ms = INFINITE, ms_orig = INFINITE, startTime, endTime;
    241 if (tv)
    242 	ms_orig = ms = evutil_tv_to_msec_(tv);
    243 
    244 EnterCriticalSection(&cond->lock);
    245 ++cond->n_waiting;
    246 generation_at_start = cond->generation;
    247 LeaveCriticalSection(&cond->lock);
    248 
    249 LeaveCriticalSection(lock);
    250 
    251 startTime = GetTickCount();
    252 do {
    253 	DWORD res;
    254 	res = WaitForSingleObject(cond->event, ms);
    255 	EnterCriticalSection(&cond->lock);
    256 	if (cond->n_to_wake &&
    257 	    cond->generation != generation_at_start) {
    258 		--cond->n_to_wake;
    259 		--cond->n_waiting;
    260 		result = 0;
    261 		waiting = 0;
    262 		goto out;
    263 	} else if (res != WAIT_OBJECT_0) {
    264 		result = (res==WAIT_TIMEOUT) ? 1 : -1;
    265 		--cond->n_waiting;
    266 		waiting = 0;
    267 		goto out;
    268 	} else if (ms != INFINITE) {
    269 		endTime = GetTickCount();
    270 		if (startTime + ms_orig <= endTime) {
    271 			result = 1; /* Timeout */
    272 			--cond->n_waiting;
    273 			waiting = 0;
    274 			goto out;
    275 		} else {
    276 			ms = startTime + ms_orig - endTime;
    277 		}
    278 	}
    279 	/* If we make it here, we are still waiting. */
    280 	if (cond->n_to_wake == 0) {
    281 		/* There is nobody else who should wake up; reset
    282 		 * the event. */
    283 		ResetEvent(cond->event);
    284 	}
    285 out:
    286 	LeaveCriticalSection(&cond->lock);
    287 } while (waiting);
    288 
    289 EnterCriticalSection(lock);
    290 
    291 EnterCriticalSection(&cond->lock);
    292 if (!cond->n_waiting)
    293 	ResetEvent(cond->event);
    294 LeaveCriticalSection(&cond->lock);
    295 
    296 return result;
    297 }
    298 
    299 int
    300 evthread_use_windows_threads(void)
    301 {
    302 struct evthread_lock_callbacks cbs = {
    303 	EVTHREAD_LOCK_API_VERSION,
    304 	EVTHREAD_LOCKTYPE_RECURSIVE,
    305 	evthread_win32_lock_create,
    306 	evthread_win32_lock_free,
    307 	evthread_win32_lock,
    308 	evthread_win32_unlock
    309 };
    310 
    311 
    312 struct evthread_condition_callbacks cond_cbs = {
    313 	EVTHREAD_CONDITION_API_VERSION,
    314 	evthread_win32_cond_alloc,
    315 	evthread_win32_cond_free,
    316 	evthread_win32_cond_signal,
    317 	evthread_win32_cond_wait
    318 };
    319 #ifdef WIN32_HAVE_CONDITION_VARIABLES
    320 struct evthread_condition_callbacks condvar_cbs = {
    321 	EVTHREAD_CONDITION_API_VERSION,
    322 	evthread_win32_condvar_alloc,
    323 	evthread_win32_condvar_free,
    324 	evthread_win32_condvar_signal,
    325 	evthread_win32_condvar_wait
    326 };
    327 #endif
    328 
    329 evthread_set_lock_callbacks(&cbs);
    330 evthread_set_id_callback(evthread_win32_get_id);
    331 #ifdef WIN32_HAVE_CONDITION_VARIABLES
    332 if (evthread_win32_condvar_init()) {
    333 	evthread_set_condition_callbacks(&condvar_cbs);
    334 	return 0;
    335 }
    336 #endif
    337 evthread_set_condition_callbacks(&cond_cbs);
    338 
    339 return 0;
    340 }