Home | History | Annotate | Download | only in libevent
      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 }
    341 
    342