mirror of
https://github.com/MariaDB/server.git
synced 2025-01-15 19:42:28 +01:00
MDEV-9154 : Remove workarounds (mainly dynamic function loading)
for running obsolete versions of Windows
This commit is contained in:
parent
22ede741f0
commit
3a7bc23a16
16 changed files with 62 additions and 723 deletions
|
@ -50,7 +50,7 @@ IF(CMAKE_C_COMPILER MATCHES "icl")
|
|||
ENDIF()
|
||||
|
||||
ADD_DEFINITIONS(-D_WINDOWS -D__WIN__ -D_CRT_SECURE_NO_DEPRECATE)
|
||||
ADD_DEFINITIONS(-D_WIN32_WINNT=0x0501)
|
||||
ADD_DEFINITIONS(-D_WIN32_WINNT=0x0600)
|
||||
# We do not want the windows.h macros min/max
|
||||
ADD_DEFINITIONS(-DNOMINMAX)
|
||||
# Speed up build process excluding unused header files
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include <time.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define _WIN32_WINNT 0x0400
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
#else
|
||||
|
|
|
@ -54,26 +54,7 @@ typedef struct st_pthread_link {
|
|||
We use native conditions on Vista and later, and fallback to own
|
||||
implementation on earlier OS version.
|
||||
*/
|
||||
typedef union
|
||||
{
|
||||
/* Native condition (used on Vista and later) */
|
||||
CONDITION_VARIABLE native_cond;
|
||||
|
||||
/* Own implementation (used on XP) */
|
||||
struct
|
||||
{
|
||||
uint32 waiting;
|
||||
CRITICAL_SECTION lock_waiting;
|
||||
enum
|
||||
{
|
||||
SIGNAL= 0,
|
||||
BROADCAST= 1,
|
||||
MAX_EVENTS= 2
|
||||
} EVENTS;
|
||||
HANDLE events[MAX_EVENTS];
|
||||
HANDLE broadcast_block_event;
|
||||
};
|
||||
} pthread_cond_t;
|
||||
typedef CONDITION_VARIABLE pthread_cond_t;
|
||||
|
||||
|
||||
typedef int pthread_mutexattr_t;
|
||||
|
@ -81,10 +62,8 @@ typedef int pthread_mutexattr_t;
|
|||
#define pthread_handler_t EXTERNC void * __cdecl
|
||||
typedef void * (__cdecl *pthread_handler)(void *);
|
||||
|
||||
typedef volatile LONG my_pthread_once_t;
|
||||
#define MY_PTHREAD_ONCE_INIT 0
|
||||
#define MY_PTHREAD_ONCE_INPROGRESS 1
|
||||
#define MY_PTHREAD_ONCE_DONE 2
|
||||
typedef INIT_ONCE my_pthread_once_t;
|
||||
#define MY_PTHREAD_ONCE_INIT INIT_ONCE_STATIC_INIT;
|
||||
|
||||
#if !STRUCT_TIMESPEC_HAS_TV_SEC || !STRUCT_TIMESPEC_HAS_TV_NSEC
|
||||
struct timespec {
|
||||
|
|
|
@ -211,14 +211,6 @@ void vio_end(void);
|
|||
#define SHUT_RD SD_RECEIVE
|
||||
#endif
|
||||
|
||||
/*
|
||||
Set thread id for io cancellation (required on Windows XP only,
|
||||
and should to be removed if XP is no more supported)
|
||||
*/
|
||||
|
||||
#define vio_set_thread_id(vio, tid) if(vio) vio->thread_id= tid
|
||||
#else
|
||||
#define vio_set_thread_id(vio, tid)
|
||||
#endif
|
||||
|
||||
/* This enumerator is used in parser - should be always visible */
|
||||
|
@ -288,7 +280,6 @@ struct st_vio
|
|||
#ifdef _WIN32
|
||||
HANDLE hPipe;
|
||||
OVERLAPPED overlapped;
|
||||
DWORD thread_id; /* Used on XP only by vio_shutdown() */
|
||||
DWORD read_timeout_ms;
|
||||
DWORD write_timeout_ms;
|
||||
#endif
|
||||
|
|
|
@ -1124,43 +1124,7 @@ static int add_directory(MEM_ROOT *alloc, const char *dir, const char **dirs)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __WIN__
|
||||
/*
|
||||
This wrapper for GetSystemWindowsDirectory() will dynamically bind to the
|
||||
function if it is available, emulate it on NT4 Terminal Server by stripping
|
||||
the \SYSTEM32 from the end of the results of GetSystemDirectory(), or just
|
||||
return GetSystemDirectory().
|
||||
*/
|
||||
|
||||
typedef UINT (WINAPI *GET_SYSTEM_WINDOWS_DIRECTORY)(LPSTR, UINT);
|
||||
|
||||
static size_t my_get_system_windows_directory(char *buffer, size_t size)
|
||||
{
|
||||
size_t count;
|
||||
GET_SYSTEM_WINDOWS_DIRECTORY
|
||||
func_ptr= (GET_SYSTEM_WINDOWS_DIRECTORY)
|
||||
GetProcAddress(GetModuleHandle("kernel32.dll"),
|
||||
"GetSystemWindowsDirectoryA");
|
||||
|
||||
if (func_ptr)
|
||||
return func_ptr(buffer, (uint) size);
|
||||
|
||||
/*
|
||||
Windows NT 4.0 Terminal Server Edition:
|
||||
To retrieve the shared Windows directory, call GetSystemDirectory and
|
||||
trim the "System32" element from the end of the returned path.
|
||||
*/
|
||||
count= GetSystemDirectory(buffer, (uint) size);
|
||||
if (count > 8 && stricmp(buffer+(count-8), "\\System32") == 0)
|
||||
{
|
||||
count-= 8;
|
||||
buffer[count] = '\0';
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static const char *my_get_module_parent(char *buf, size_t size)
|
||||
{
|
||||
char *last= NULL;
|
||||
|
@ -1209,7 +1173,7 @@ static const char **init_default_directories(MEM_ROOT *alloc)
|
|||
|
||||
{
|
||||
char fname_buffer[FN_REFLEN];
|
||||
if (my_get_system_windows_directory(fname_buffer, sizeof(fname_buffer)))
|
||||
if (GetSystemWindowsDirectory(fname_buffer, sizeof(fname_buffer)))
|
||||
errors += add_directory(alloc, fname_buffer, dirs);
|
||||
|
||||
if (GetWindowsDirectory(fname_buffer, sizeof(fname_buffer)))
|
||||
|
|
|
@ -26,63 +26,6 @@
|
|||
#include <sys/timeb.h>
|
||||
|
||||
|
||||
/*
|
||||
Windows native condition variables. We use runtime loading / function
|
||||
pointers, because they are not available on XP
|
||||
*/
|
||||
|
||||
/* Prototypes and function pointers for condition variable functions */
|
||||
typedef void (WINAPI * InitializeConditionVariableProc)
|
||||
(PCONDITION_VARIABLE ConditionVariable);
|
||||
|
||||
typedef BOOL (WINAPI * SleepConditionVariableCSProc)
|
||||
(PCONDITION_VARIABLE ConditionVariable,
|
||||
PCRITICAL_SECTION CriticalSection,
|
||||
DWORD dwMilliseconds);
|
||||
|
||||
typedef void (WINAPI * WakeAllConditionVariableProc)
|
||||
(PCONDITION_VARIABLE ConditionVariable);
|
||||
|
||||
typedef void (WINAPI * WakeConditionVariableProc)
|
||||
(PCONDITION_VARIABLE ConditionVariable);
|
||||
|
||||
static InitializeConditionVariableProc my_InitializeConditionVariable;
|
||||
static SleepConditionVariableCSProc my_SleepConditionVariableCS;
|
||||
static WakeAllConditionVariableProc my_WakeAllConditionVariable;
|
||||
static WakeConditionVariableProc my_WakeConditionVariable;
|
||||
|
||||
|
||||
/**
|
||||
Indicates if we have native condition variables,
|
||||
initialized first time pthread_cond_init is called.
|
||||
*/
|
||||
|
||||
static BOOL have_native_conditions= FALSE;
|
||||
|
||||
|
||||
/**
|
||||
Check if native conditions can be used, load function pointers
|
||||
*/
|
||||
|
||||
static void check_native_cond_availability(void)
|
||||
{
|
||||
HMODULE module= GetModuleHandle("kernel32");
|
||||
|
||||
my_InitializeConditionVariable= (InitializeConditionVariableProc)
|
||||
GetProcAddress(module, "InitializeConditionVariable");
|
||||
my_SleepConditionVariableCS= (SleepConditionVariableCSProc)
|
||||
GetProcAddress(module, "SleepConditionVariableCS");
|
||||
my_WakeAllConditionVariable= (WakeAllConditionVariableProc)
|
||||
GetProcAddress(module, "WakeAllConditionVariable");
|
||||
my_WakeConditionVariable= (WakeConditionVariableProc)
|
||||
GetProcAddress(module, "WakeConditionVariable");
|
||||
|
||||
if (my_InitializeConditionVariable)
|
||||
have_native_conditions= TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Convert abstime to milliseconds
|
||||
*/
|
||||
|
@ -105,202 +48,40 @@ static DWORD get_milliseconds(const struct timespec *abstime)
|
|||
return (DWORD)ms;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Old (pre-vista) implementation using events
|
||||
*/
|
||||
|
||||
static int legacy_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
|
||||
{
|
||||
cond->waiting= 0;
|
||||
InitializeCriticalSection(&cond->lock_waiting);
|
||||
|
||||
cond->events[SIGNAL]= CreateEvent(NULL, /* no security */
|
||||
FALSE, /* auto-reset event */
|
||||
FALSE, /* non-signaled initially */
|
||||
NULL); /* unnamed */
|
||||
|
||||
/* Create a manual-reset event. */
|
||||
cond->events[BROADCAST]= CreateEvent(NULL, /* no security */
|
||||
TRUE, /* manual-reset */
|
||||
FALSE, /* non-signaled initially */
|
||||
NULL); /* unnamed */
|
||||
|
||||
|
||||
cond->broadcast_block_event= CreateEvent(NULL, /* no security */
|
||||
TRUE, /* manual-reset */
|
||||
TRUE, /* signaled initially */
|
||||
NULL); /* unnamed */
|
||||
|
||||
if( cond->events[SIGNAL] == NULL ||
|
||||
cond->events[BROADCAST] == NULL ||
|
||||
cond->broadcast_block_event == NULL )
|
||||
return ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int legacy_cond_destroy(pthread_cond_t *cond)
|
||||
{
|
||||
DeleteCriticalSection(&cond->lock_waiting);
|
||||
|
||||
if (CloseHandle(cond->events[SIGNAL]) == 0 ||
|
||||
CloseHandle(cond->events[BROADCAST]) == 0 ||
|
||||
CloseHandle(cond->broadcast_block_event) == 0)
|
||||
return EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int legacy_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
|
||||
struct timespec *abstime)
|
||||
{
|
||||
int result;
|
||||
DWORD timeout;
|
||||
|
||||
timeout= get_milliseconds(abstime);
|
||||
/*
|
||||
Block access if previous broadcast hasn't finished.
|
||||
This is just for safety and should normally not
|
||||
affect the total time spent in this function.
|
||||
*/
|
||||
WaitForSingleObject(cond->broadcast_block_event, INFINITE);
|
||||
|
||||
EnterCriticalSection(&cond->lock_waiting);
|
||||
cond->waiting++;
|
||||
LeaveCriticalSection(&cond->lock_waiting);
|
||||
|
||||
LeaveCriticalSection(mutex);
|
||||
result= WaitForMultipleObjects(2, cond->events, FALSE, timeout);
|
||||
|
||||
EnterCriticalSection(&cond->lock_waiting);
|
||||
cond->waiting--;
|
||||
|
||||
if (cond->waiting == 0)
|
||||
{
|
||||
/*
|
||||
We're the last waiter to be notified or to stop waiting, so
|
||||
reset the manual event.
|
||||
*/
|
||||
/* Close broadcast gate */
|
||||
ResetEvent(cond->events[BROADCAST]);
|
||||
/* Open block gate */
|
||||
SetEvent(cond->broadcast_block_event);
|
||||
}
|
||||
LeaveCriticalSection(&cond->lock_waiting);
|
||||
|
||||
EnterCriticalSection(mutex);
|
||||
|
||||
return result == WAIT_TIMEOUT ? ETIMEDOUT : 0;
|
||||
}
|
||||
|
||||
static int legacy_cond_signal(pthread_cond_t *cond)
|
||||
{
|
||||
EnterCriticalSection(&cond->lock_waiting);
|
||||
|
||||
if(cond->waiting > 0)
|
||||
SetEvent(cond->events[SIGNAL]);
|
||||
|
||||
LeaveCriticalSection(&cond->lock_waiting);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int legacy_cond_broadcast(pthread_cond_t *cond)
|
||||
{
|
||||
EnterCriticalSection(&cond->lock_waiting);
|
||||
/*
|
||||
The mutex protect us from broadcasting if
|
||||
there isn't any thread waiting to open the
|
||||
block gate after this call has closed it.
|
||||
*/
|
||||
if(cond->waiting > 0)
|
||||
{
|
||||
/* Close block gate */
|
||||
ResetEvent(cond->broadcast_block_event);
|
||||
/* Open broadcast gate */
|
||||
SetEvent(cond->events[BROADCAST]);
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&cond->lock_waiting);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Posix API functions. Just choose between native and legacy implementation.
|
||||
*/
|
||||
|
||||
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
|
||||
{
|
||||
/*
|
||||
Once initialization is used here rather than in my_init(), to
|
||||
1) avoid my_init() pitfalls- undefined order in which initialization should
|
||||
run
|
||||
2) be potentially useful C++ (in static constructors that run before main())
|
||||
3) just to simplify the API.
|
||||
Also, the overhead of my_pthread_once is very small.
|
||||
*/
|
||||
static my_pthread_once_t once_control= MY_PTHREAD_ONCE_INIT;
|
||||
my_pthread_once(&once_control, check_native_cond_availability);
|
||||
|
||||
if (have_native_conditions)
|
||||
{
|
||||
my_InitializeConditionVariable(&cond->native_cond);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return legacy_cond_init(cond, attr);
|
||||
InitializeConditionVariable(cond);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int pthread_cond_destroy(pthread_cond_t *cond)
|
||||
{
|
||||
if (have_native_conditions)
|
||||
return 0; /* no destroy function */
|
||||
else
|
||||
return legacy_cond_destroy(cond);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int pthread_cond_broadcast(pthread_cond_t *cond)
|
||||
{
|
||||
if (have_native_conditions)
|
||||
{
|
||||
my_WakeAllConditionVariable(&cond->native_cond);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return legacy_cond_broadcast(cond);
|
||||
WakeAllConditionVariable(cond);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int pthread_cond_signal(pthread_cond_t *cond)
|
||||
{
|
||||
if (have_native_conditions)
|
||||
{
|
||||
my_WakeConditionVariable(&cond->native_cond);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return legacy_cond_signal(cond);
|
||||
WakeConditionVariable(cond);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
|
||||
const struct timespec *abstime)
|
||||
{
|
||||
if (have_native_conditions)
|
||||
{
|
||||
DWORD timeout= get_milliseconds(abstime);
|
||||
if (!my_SleepConditionVariableCS(&cond->native_cond, mutex, timeout))
|
||||
return ETIMEDOUT;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return legacy_cond_timedwait(cond, mutex, abstime);
|
||||
DWORD timeout= get_milliseconds(abstime);
|
||||
if (!SleepConditionVariableCS(cond, mutex, timeout))
|
||||
return ETIMEDOUT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -149,45 +149,22 @@ int pthread_cancel(pthread_t thread)
|
|||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
One time initialization. For simplicity, we assume initializer thread
|
||||
does not exit within init_routine().
|
||||
One time initialization.
|
||||
*/
|
||||
int my_pthread_once(my_pthread_once_t *once_control,
|
||||
void (*init_routine)(void))
|
||||
|
||||
static BOOL CALLBACK init_once_callback(my_pthread_once_t *once_control, PVOID param, PVOID *context)
|
||||
{
|
||||
LONG state;
|
||||
typedef void(*void_f)(void);
|
||||
((void_f)param)();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
Do "dirty" read to find out if initialization is already done, to
|
||||
save an interlocked operation in common case. Memory barriers are ensured by
|
||||
Visual C++ volatile implementation.
|
||||
*/
|
||||
if (*once_control == MY_PTHREAD_ONCE_DONE)
|
||||
return 0;
|
||||
|
||||
state= InterlockedCompareExchange(once_control, MY_PTHREAD_ONCE_INPROGRESS,
|
||||
MY_PTHREAD_ONCE_INIT);
|
||||
|
||||
switch(state)
|
||||
{
|
||||
case MY_PTHREAD_ONCE_INIT:
|
||||
/* This is initializer thread */
|
||||
(*init_routine)();
|
||||
*once_control= MY_PTHREAD_ONCE_DONE;
|
||||
break;
|
||||
|
||||
case MY_PTHREAD_ONCE_INPROGRESS:
|
||||
/* init_routine in progress. Wait for its completion */
|
||||
while(*once_control == MY_PTHREAD_ONCE_INPROGRESS)
|
||||
{
|
||||
Sleep(1);
|
||||
}
|
||||
break;
|
||||
case MY_PTHREAD_ONCE_DONE:
|
||||
/* Nothing to do */
|
||||
break;
|
||||
}
|
||||
int my_pthread_once(my_pthread_once_t *once_control, void (*func)(void))
|
||||
{
|
||||
InitOnceExecuteOnce(once_control, init_once_callback, func, NULL);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -4922,25 +4922,14 @@ static void end_ssl()
|
|||
/**
|
||||
Registers a file to be collected when Windows Error Reporting creates a crash
|
||||
report.
|
||||
|
||||
@note only works on Vista and later, since WerRegisterFile() is not available
|
||||
on earlier Windows.
|
||||
*/
|
||||
#include <werapi.h>
|
||||
static void add_file_to_crash_report(char *file)
|
||||
{
|
||||
/* Load WerRegisterFile function dynamically.*/
|
||||
HRESULT (WINAPI *pWerRegisterFile)(PCWSTR, WER_REGISTER_FILE_TYPE, DWORD)
|
||||
=(HRESULT (WINAPI *) (PCWSTR, WER_REGISTER_FILE_TYPE, DWORD))
|
||||
GetProcAddress(GetModuleHandle("kernel32"),"WerRegisterFile");
|
||||
|
||||
if (pWerRegisterFile)
|
||||
wchar_t wfile[MAX_PATH+1]= {0};
|
||||
if (mbstowcs(wfile, file, MAX_PATH) != (size_t)-1)
|
||||
{
|
||||
wchar_t wfile[MAX_PATH+1]= {0};
|
||||
if (mbstowcs(wfile, file, MAX_PATH) != (size_t)-1)
|
||||
{
|
||||
pWerRegisterFile(wfile, WerRegFileTypeOther, WER_FILE_ANONYMOUS_DATA);
|
||||
}
|
||||
WerRegisterFile(wfile, WerRegFileTypeOther, WER_FILE_ANONYMOUS_DATA);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -9640,13 +9629,6 @@ static int get_options(int *argc_ptr, char ***argv_ptr)
|
|||
one_thread_scheduler(extra_thread_scheduler);
|
||||
#else
|
||||
|
||||
#ifdef _WIN32
|
||||
/* workaround: disable thread pool on XP */
|
||||
if (GetProcAddress(GetModuleHandle("kernel32"),"CreateThreadpool") == 0 &&
|
||||
thread_handling > SCHEDULER_NO_THREADS)
|
||||
SYSVAR_AUTOSIZE(thread_handling, SCHEDULER_ONE_THREAD_PER_CONNECTION);
|
||||
#endif
|
||||
|
||||
if (thread_handling <= SCHEDULER_ONE_THREAD_PER_CONNECTION)
|
||||
one_thread_per_connection_scheduler(thread_scheduler, &max_connections,
|
||||
&connection_count);
|
||||
|
|
|
@ -2100,7 +2100,6 @@ bool THD::store_globals()
|
|||
STACK_DIRECTION * (long)my_thread_stack_size;
|
||||
if (net.vio)
|
||||
{
|
||||
vio_set_thread_id(net.vio, real_id);
|
||||
net.thd= this;
|
||||
}
|
||||
/*
|
||||
|
|
|
@ -2933,7 +2933,6 @@ public:
|
|||
{
|
||||
mysql_mutex_lock(&LOCK_thd_data);
|
||||
active_vio = vio;
|
||||
vio_set_thread_id(vio, pthread_self());
|
||||
mysql_mutex_unlock(&LOCK_thd_data);
|
||||
}
|
||||
inline void clear_active_vio()
|
||||
|
|
|
@ -32,16 +32,6 @@
|
|||
#include <windows.h>
|
||||
|
||||
|
||||
/*
|
||||
Threadpool API is not available on XP. We still want to compile a single
|
||||
version on Windows, but use the latest functionality if available.
|
||||
We cannot use threadpool functionality directly, since executable won't
|
||||
start on XP and loader will complain about missing symbols.
|
||||
|
||||
We solve using the usual way it is done on Windows, i.e with dynamic loading.
|
||||
We'll need to load a lot of function, and make this less painful with the
|
||||
WEAK_SYMBOL macro below
|
||||
*/
|
||||
|
||||
/*
|
||||
WEAK_SYMBOL(return_type, function_name, argument_type1,..,argument_typeN)
|
||||
|
@ -61,107 +51,10 @@
|
|||
static pFN_##function my_##function = (pFN_##function) \
|
||||
(GetProcAddress(GetModuleHandle("kernel32"),#function))
|
||||
|
||||
WEAK_SYMBOL(VOID, CancelThreadpoolIo, PTP_IO);
|
||||
#define CancelThreadpoolIo my_CancelThreadpoolIo
|
||||
|
||||
WEAK_SYMBOL(VOID, CloseThreadpool, PTP_POOL);
|
||||
#define CloseThreadpool my_CloseThreadpool
|
||||
|
||||
WEAK_SYMBOL(VOID, CloseThreadpoolIo, PTP_IO);
|
||||
#define CloseThreadpoolIo my_CloseThreadpoolIo
|
||||
|
||||
WEAK_SYMBOL(VOID, CloseThreadpoolTimer,PTP_TIMER);
|
||||
#define CloseThreadpoolTimer my_CloseThreadpoolTimer
|
||||
|
||||
WEAK_SYMBOL(VOID, CloseThreadpoolWait,PTP_WAIT);
|
||||
#define CloseThreadpoolWait my_CloseThreadpoolWait
|
||||
|
||||
WEAK_SYMBOL(PTP_POOL, CreateThreadpool,PVOID);
|
||||
#define CreateThreadpool my_CreateThreadpool
|
||||
|
||||
WEAK_SYMBOL(PTP_IO, CreateThreadpoolIo, HANDLE, PTP_WIN32_IO_CALLBACK, PVOID ,
|
||||
PTP_CALLBACK_ENVIRON);
|
||||
#define CreateThreadpoolIo my_CreateThreadpoolIo
|
||||
|
||||
WEAK_SYMBOL(PTP_TIMER, CreateThreadpoolTimer, PTP_TIMER_CALLBACK ,
|
||||
PVOID pv, PTP_CALLBACK_ENVIRON pcbe);
|
||||
#define CreateThreadpoolTimer my_CreateThreadpoolTimer
|
||||
|
||||
WEAK_SYMBOL(PTP_WAIT, CreateThreadpoolWait, PTP_WAIT_CALLBACK, PVOID,
|
||||
PTP_CALLBACK_ENVIRON);
|
||||
#define CreateThreadpoolWait my_CreateThreadpoolWait
|
||||
|
||||
WEAK_SYMBOL(VOID, DisassociateCurrentThreadFromCallback, PTP_CALLBACK_INSTANCE);
|
||||
#define DisassociateCurrentThreadFromCallback my_DisassociateCurrentThreadFromCallback
|
||||
|
||||
WEAK_SYMBOL(DWORD, FlsAlloc, PFLS_CALLBACK_FUNCTION);
|
||||
#define FlsAlloc my_FlsAlloc
|
||||
|
||||
WEAK_SYMBOL(PVOID, FlsGetValue, DWORD);
|
||||
#define FlsGetValue my_FlsGetValue
|
||||
|
||||
WEAK_SYMBOL(BOOL, FlsSetValue, DWORD, PVOID);
|
||||
#define FlsSetValue my_FlsSetValue
|
||||
|
||||
WEAK_SYMBOL(VOID, SetThreadpoolThreadMaximum, PTP_POOL, DWORD);
|
||||
#define SetThreadpoolThreadMaximum my_SetThreadpoolThreadMaximum
|
||||
|
||||
WEAK_SYMBOL(BOOL, SetThreadpoolThreadMinimum, PTP_POOL, DWORD);
|
||||
#define SetThreadpoolThreadMinimum my_SetThreadpoolThreadMinimum
|
||||
|
||||
WEAK_SYMBOL(VOID, SetThreadpoolTimer, PTP_TIMER, PFILETIME,DWORD,DWORD);
|
||||
#define SetThreadpoolTimer my_SetThreadpoolTimer
|
||||
|
||||
WEAK_SYMBOL(VOID, SetThreadpoolWait, PTP_WAIT,HANDLE,PFILETIME);
|
||||
#define SetThreadpoolWait my_SetThreadpoolWait
|
||||
|
||||
WEAK_SYMBOL(VOID, StartThreadpoolIo, PTP_IO);
|
||||
#define StartThreadpoolIo my_StartThreadpoolIo
|
||||
|
||||
WEAK_SYMBOL(VOID, WaitForThreadpoolIoCallbacks,PTP_IO, BOOL);
|
||||
#define WaitForThreadpoolIoCallbacks my_WaitForThreadpoolIoCallbacks
|
||||
|
||||
WEAK_SYMBOL(VOID, WaitForThreadpoolTimerCallbacks, PTP_TIMER, BOOL);
|
||||
#define WaitForThreadpoolTimerCallbacks my_WaitForThreadpoolTimerCallbacks
|
||||
|
||||
WEAK_SYMBOL(VOID, WaitForThreadpoolWaitCallbacks, PTP_WAIT, BOOL);
|
||||
#define WaitForThreadpoolWaitCallbacks my_WaitForThreadpoolWaitCallbacks
|
||||
|
||||
WEAK_SYMBOL(BOOL, SetFileCompletionNotificationModes, HANDLE, UCHAR);
|
||||
#define SetFileCompletionNotificationModes my_SetFileCompletionNotificationModes
|
||||
|
||||
WEAK_SYMBOL(BOOL, TrySubmitThreadpoolCallback, PTP_SIMPLE_CALLBACK pfns,
|
||||
PVOID pv,PTP_CALLBACK_ENVIRON pcbe);
|
||||
#define TrySubmitThreadpoolCallback my_TrySubmitThreadpoolCallback
|
||||
|
||||
WEAK_SYMBOL(PTP_WORK, CreateThreadpoolWork, PTP_WORK_CALLBACK pfnwk, PVOID pv,
|
||||
PTP_CALLBACK_ENVIRON pcbe);
|
||||
#define CreateThreadpoolWork my_CreateThreadpoolWork
|
||||
|
||||
WEAK_SYMBOL(VOID, SubmitThreadpoolWork,PTP_WORK pwk);
|
||||
#define SubmitThreadpoolWork my_SubmitThreadpoolWork
|
||||
|
||||
WEAK_SYMBOL(VOID, CloseThreadpoolWork, PTP_WORK pwk);
|
||||
#define CloseThreadpoolWork my_CloseThreadpoolWork
|
||||
|
||||
WEAK_SYMBOL(BOOL, CallbackMayRunLong, PTP_CALLBACK_INSTANCE pci);
|
||||
#define CallbackMayRunLong my_CallbackMayRunLong
|
||||
|
||||
#if _MSC_VER >= 1600
|
||||
/* Stack size manipulation available only on Win7+ /declarations in VS10 */
|
||||
WEAK_SYMBOL(BOOL, SetThreadpoolStackInformation, PTP_POOL,
|
||||
PTP_POOL_STACK_INFORMATION);
|
||||
#define SetThreadpoolStackInformation my_SetThreadpoolStackInformation
|
||||
#else /* _MSC_VER < 1600 */
|
||||
#define SetThreadpoolCallbackPriority(env,prio)
|
||||
typedef enum _TP_CALLBACK_PRIORITY {
|
||||
TP_CALLBACK_PRIORITY_HIGH,
|
||||
TP_CALLBACK_PRIORITY_NORMAL,
|
||||
TP_CALLBACK_PRIORITY_LOW,
|
||||
TP_CALLBACK_PRIORITY_INVALID
|
||||
} TP_CALLBACK_PRIORITY;
|
||||
#endif
|
||||
|
||||
|
||||
/* Log a warning */
|
||||
static void tp_log_warning(const char *msg, const char *fct)
|
||||
|
|
|
@ -297,9 +297,6 @@ use simulated aio we build below with threads.
|
|||
Currently we support native aio on windows and linux */
|
||||
extern my_bool srv_use_native_aio;
|
||||
extern my_bool srv_numa_interleave;
|
||||
#ifdef __WIN__
|
||||
extern ibool srv_use_native_conditions;
|
||||
#endif /* __WIN__ */
|
||||
#endif /* !UNIV_HOTBACKUP */
|
||||
|
||||
/* Use trim operation */
|
||||
|
|
|
@ -85,31 +85,6 @@ event embedded inside a mutex, on free, this generates a recursive call.
|
|||
This version of the free event function doesn't acquire the global lock */
|
||||
static void os_event_free_internal(os_event_t event);
|
||||
|
||||
/* On Windows (Vista and later), load function pointers for condition
|
||||
variable handling. Those functions are not available in prior versions,
|
||||
so we have to use them via runtime loading, as long as we support XP. */
|
||||
static void os_cond_module_init(void);
|
||||
|
||||
#ifdef __WIN__
|
||||
/* Prototypes and function pointers for condition variable functions */
|
||||
typedef VOID (WINAPI* InitializeConditionVariableProc)
|
||||
(PCONDITION_VARIABLE ConditionVariable);
|
||||
static InitializeConditionVariableProc initialize_condition_variable;
|
||||
|
||||
typedef BOOL (WINAPI* SleepConditionVariableCSProc)
|
||||
(PCONDITION_VARIABLE ConditionVariable,
|
||||
PCRITICAL_SECTION CriticalSection,
|
||||
DWORD dwMilliseconds);
|
||||
static SleepConditionVariableCSProc sleep_condition_variable;
|
||||
|
||||
typedef VOID (WINAPI* WakeAllConditionVariableProc)
|
||||
(PCONDITION_VARIABLE ConditionVariable);
|
||||
static WakeAllConditionVariableProc wake_all_condition_variable;
|
||||
|
||||
typedef VOID (WINAPI* WakeConditionVariableProc)
|
||||
(PCONDITION_VARIABLE ConditionVariable);
|
||||
static WakeConditionVariableProc wake_condition_variable;
|
||||
#endif
|
||||
|
||||
/*********************************************************//**
|
||||
Initialitze condition variable */
|
||||
|
@ -122,8 +97,7 @@ os_cond_init(
|
|||
ut_a(cond);
|
||||
|
||||
#ifdef __WIN__
|
||||
ut_a(initialize_condition_variable != NULL);
|
||||
initialize_condition_variable(cond);
|
||||
InitializeConditionVariable(cond);
|
||||
#else
|
||||
ut_a(pthread_cond_init(cond, NULL) == 0);
|
||||
#endif
|
||||
|
@ -151,9 +125,8 @@ os_cond_wait_timed(
|
|||
BOOL ret;
|
||||
DWORD err;
|
||||
|
||||
ut_a(sleep_condition_variable != NULL);
|
||||
|
||||
ret = sleep_condition_variable(cond, mutex, time_in_ms);
|
||||
ret = SleepConditionVariableCS(cond, mutex, time_in_ms);
|
||||
|
||||
if (!ret) {
|
||||
err = GetLastError();
|
||||
|
@ -208,8 +181,7 @@ os_cond_wait(
|
|||
ut_a(mutex);
|
||||
|
||||
#ifdef __WIN__
|
||||
ut_a(sleep_condition_variable != NULL);
|
||||
ut_a(sleep_condition_variable(cond, mutex, INFINITE));
|
||||
ut_a(SleepConditionVariableCS(cond, mutex, INFINITE));
|
||||
#else
|
||||
ut_a(pthread_cond_wait(cond, mutex) == 0);
|
||||
#endif
|
||||
|
@ -226,8 +198,7 @@ os_cond_broadcast(
|
|||
ut_a(cond);
|
||||
|
||||
#ifdef __WIN__
|
||||
ut_a(wake_all_condition_variable != NULL);
|
||||
wake_all_condition_variable(cond);
|
||||
WakeAllConditionVariable(cond);
|
||||
#else
|
||||
ut_a(pthread_cond_broadcast(cond) == 0);
|
||||
#endif
|
||||
|
@ -248,40 +219,6 @@ os_cond_destroy(
|
|||
#endif
|
||||
}
|
||||
|
||||
/*********************************************************//**
|
||||
On Windows (Vista and later), load function pointers for condition variable
|
||||
handling. Those functions are not available in prior versions, so we have to
|
||||
use them via runtime loading, as long as we support XP. */
|
||||
static
|
||||
void
|
||||
os_cond_module_init(void)
|
||||
/*=====================*/
|
||||
{
|
||||
#ifdef __WIN__
|
||||
HMODULE h_dll;
|
||||
|
||||
if (!srv_use_native_conditions)
|
||||
return;
|
||||
|
||||
h_dll = GetModuleHandle("kernel32");
|
||||
|
||||
initialize_condition_variable = (InitializeConditionVariableProc)
|
||||
GetProcAddress(h_dll, "InitializeConditionVariable");
|
||||
sleep_condition_variable = (SleepConditionVariableCSProc)
|
||||
GetProcAddress(h_dll, "SleepConditionVariableCS");
|
||||
wake_all_condition_variable = (WakeAllConditionVariableProc)
|
||||
GetProcAddress(h_dll, "WakeAllConditionVariable");
|
||||
wake_condition_variable = (WakeConditionVariableProc)
|
||||
GetProcAddress(h_dll, "WakeConditionVariable");
|
||||
|
||||
/* When using native condition variables, check function pointers */
|
||||
ut_a(initialize_condition_variable);
|
||||
ut_a(sleep_condition_variable);
|
||||
ut_a(wake_all_condition_variable);
|
||||
ut_a(wake_condition_variable);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*********************************************************//**
|
||||
Initializes global event and OS 'slow' mutex lists. */
|
||||
UNIV_INTERN
|
||||
|
@ -295,9 +232,6 @@ os_sync_init(void)
|
|||
os_sync_mutex = NULL;
|
||||
os_sync_mutex_inited = FALSE;
|
||||
|
||||
/* Now for Windows only */
|
||||
os_cond_module_init();
|
||||
|
||||
os_sync_mutex = os_mutex_create();
|
||||
|
||||
os_sync_mutex_inited = TRUE;
|
||||
|
@ -350,43 +284,27 @@ os_event_t
|
|||
os_event_create(void)
|
||||
/*==================*/
|
||||
{
|
||||
os_event_t event;
|
||||
os_event_t event;
|
||||
|
||||
#ifdef __WIN__
|
||||
if(!srv_use_native_conditions) {
|
||||
|
||||
event = static_cast<os_event_t>(ut_malloc(sizeof(*event)));
|
||||
|
||||
event->handle = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!event->handle) {
|
||||
fprintf(stderr,
|
||||
"InnoDB: Could not create a Windows event"
|
||||
" semaphore; Windows error %lu\n",
|
||||
(ulong) GetLastError());
|
||||
}
|
||||
} else /* Windows with condition variables */
|
||||
#endif
|
||||
{
|
||||
event = static_cast<os_event_t>(ut_malloc(sizeof *event));
|
||||
event = static_cast<os_event_t>(ut_malloc(sizeof *event));
|
||||
|
||||
#ifndef PFS_SKIP_EVENT_MUTEX
|
||||
os_fast_mutex_init(event_os_mutex_key, &event->os_mutex);
|
||||
os_fast_mutex_init(event_os_mutex_key, &event->os_mutex);
|
||||
#else
|
||||
os_fast_mutex_init(PFS_NOT_INSTRUMENTED, &event->os_mutex);
|
||||
os_fast_mutex_init(PFS_NOT_INSTRUMENTED, &event->os_mutex);
|
||||
#endif
|
||||
|
||||
os_cond_init(&(event->cond_var));
|
||||
os_cond_init(&(event->cond_var));
|
||||
|
||||
event->is_set = FALSE;
|
||||
event->is_set = FALSE;
|
||||
|
||||
/* We return this value in os_event_reset(), which can then be
|
||||
be used to pass to the os_event_wait_low(). The value of zero
|
||||
is reserved in os_event_wait_low() for the case when the
|
||||
caller does not want to pass any signal_count value. To
|
||||
distinguish between the two cases we initialize signal_count
|
||||
to 1 here. */
|
||||
event->signal_count = 1;
|
||||
}
|
||||
/* We return this value in os_event_reset(), which can then be
|
||||
be used to pass to the os_event_wait_low(). The value of zero
|
||||
is reserved in os_event_wait_low() for the case when the
|
||||
caller does not want to pass any signal_count value. To
|
||||
distinguish between the two cases we initialize signal_count
|
||||
to 1 here. */
|
||||
event->signal_count = 1;
|
||||
|
||||
/* The os_sync_mutex can be NULL because during startup an event
|
||||
can be created [ because it's embedded in the mutex/rwlock ] before
|
||||
|
@ -418,13 +336,6 @@ os_event_set(
|
|||
{
|
||||
ut_a(event);
|
||||
|
||||
#ifdef __WIN__
|
||||
if (!srv_use_native_conditions) {
|
||||
ut_a(SetEvent(event->handle));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
os_fast_mutex_lock(&(event->os_mutex));
|
||||
|
||||
if (event->is_set) {
|
||||
|
@ -456,13 +367,6 @@ os_event_reset(
|
|||
|
||||
ut_a(event);
|
||||
|
||||
#ifdef __WIN__
|
||||
if(!srv_use_native_conditions) {
|
||||
ut_a(ResetEvent(event->handle));
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
os_fast_mutex_lock(&(event->os_mutex));
|
||||
|
||||
if (!event->is_set) {
|
||||
|
@ -484,20 +388,13 @@ os_event_free_internal(
|
|||
/*===================*/
|
||||
os_event_t event) /*!< in: event to free */
|
||||
{
|
||||
#ifdef __WIN__
|
||||
if(!srv_use_native_conditions) {
|
||||
ut_a(event);
|
||||
ut_a(CloseHandle(event->handle));
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
ut_a(event);
|
||||
|
||||
/* This is to avoid freeing the mutex twice */
|
||||
os_fast_mutex_free(&(event->os_mutex));
|
||||
ut_a(event);
|
||||
|
||||
os_cond_destroy(&(event->cond_var));
|
||||
}
|
||||
/* This is to avoid freeing the mutex twice */
|
||||
os_fast_mutex_free(&(event->os_mutex));
|
||||
|
||||
os_cond_destroy(&(event->cond_var));
|
||||
|
||||
/* Remove from the list of events */
|
||||
UT_LIST_REMOVE(os_event_list, os_event_list, event);
|
||||
|
@ -517,16 +414,10 @@ os_event_free(
|
|||
|
||||
{
|
||||
ut_a(event);
|
||||
#ifdef __WIN__
|
||||
if(!srv_use_native_conditions){
|
||||
ut_a(CloseHandle(event->handle));
|
||||
} else /*Windows with condition variables */
|
||||
#endif
|
||||
{
|
||||
os_fast_mutex_free(&(event->os_mutex));
|
||||
|
||||
os_cond_destroy(&(event->cond_var));
|
||||
}
|
||||
os_fast_mutex_free(&(event->os_mutex));
|
||||
|
||||
os_cond_destroy(&(event->cond_var));
|
||||
|
||||
/* Remove from the list of events */
|
||||
os_mutex_enter(os_sync_mutex);
|
||||
|
@ -566,21 +457,6 @@ os_event_wait_low(
|
|||
returned by previous call of
|
||||
os_event_reset(). */
|
||||
{
|
||||
#ifdef __WIN__
|
||||
if(!srv_use_native_conditions) {
|
||||
DWORD err;
|
||||
|
||||
ut_a(event);
|
||||
|
||||
UT_NOT_USED(reset_sig_count);
|
||||
|
||||
/* Specify an infinite wait */
|
||||
err = WaitForSingleObject(event->handle, INFINITE);
|
||||
|
||||
ut_a(err == WAIT_OBJECT_0);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
os_fast_mutex_lock(&event->os_mutex);
|
||||
|
||||
|
@ -619,36 +495,10 @@ os_event_wait_time_low(
|
|||
|
||||
#ifdef __WIN__
|
||||
DWORD time_in_ms;
|
||||
|
||||
if (!srv_use_native_conditions) {
|
||||
DWORD err;
|
||||
|
||||
ut_a(event);
|
||||
|
||||
if (time_in_usec != OS_SYNC_INFINITE_TIME) {
|
||||
time_in_ms = static_cast<DWORD>(time_in_usec / 1000);
|
||||
err = WaitForSingleObject(event->handle, time_in_ms);
|
||||
} else {
|
||||
err = WaitForSingleObject(event->handle, INFINITE);
|
||||
}
|
||||
|
||||
if (err == WAIT_OBJECT_0) {
|
||||
return(0);
|
||||
} else if ((err == WAIT_TIMEOUT) || (err == ERROR_TIMEOUT)) {
|
||||
return(OS_SYNC_TIME_EXCEEDED);
|
||||
}
|
||||
|
||||
ut_error;
|
||||
/* Dummy value to eliminate compiler warning. */
|
||||
return(42);
|
||||
if (time_in_usec != OS_SYNC_INFINITE_TIME) {
|
||||
time_in_ms = static_cast<DWORD>(time_in_usec / 1000);
|
||||
} else {
|
||||
ut_a(sleep_condition_variable != NULL);
|
||||
|
||||
if (time_in_usec != OS_SYNC_INFINITE_TIME) {
|
||||
time_in_ms = static_cast<DWORD>(time_in_usec / 1000);
|
||||
} else {
|
||||
time_in_ms = INFINITE;
|
||||
}
|
||||
time_in_ms = INFINITE;
|
||||
}
|
||||
#else
|
||||
struct timespec abstime;
|
||||
|
|
|
@ -1836,32 +1836,7 @@ innobase_start_or_create_for_mysql(void)
|
|||
srv_startup_is_before_trx_rollback_phase = TRUE;
|
||||
|
||||
#ifdef __WIN__
|
||||
switch (os_get_os_version()) {
|
||||
case OS_WIN95:
|
||||
case OS_WIN31:
|
||||
case OS_WINNT:
|
||||
srv_use_native_conditions = FALSE;
|
||||
/* On Win 95, 98, ME, Win32 subsystem for Windows 3.1,
|
||||
and NT use simulated aio. In NT Windows provides async i/o,
|
||||
but when run in conjunction with InnoDB Hot Backup, it seemed
|
||||
to corrupt the data files. */
|
||||
|
||||
srv_use_native_aio = FALSE;
|
||||
break;
|
||||
|
||||
case OS_WIN2000:
|
||||
case OS_WINXP:
|
||||
/* On 2000 and XP, async IO is available, but no condition variables. */
|
||||
srv_use_native_aio = TRUE;
|
||||
srv_use_native_conditions = FALSE;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Vista and later have both async IO and condition variables */
|
||||
srv_use_native_aio = TRUE;
|
||||
srv_use_native_conditions = TRUE;
|
||||
break;
|
||||
}
|
||||
srv_use_native_aio = TRUE;
|
||||
|
||||
#elif defined(LINUX_NATIVE_AIO)
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ int vio_shared_memory_shutdown(Vio *vio, int how)
|
|||
|
||||
int vio_pipe_shutdown(Vio *vio, int how)
|
||||
{
|
||||
return cancel_io(vio->hPipe, vio->thread_id);
|
||||
return CancelIoEx(vio->hPipe, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -299,59 +299,12 @@ size_t vio_write(Vio *vio, const uchar* buf, size_t size)
|
|||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static void CALLBACK cancel_io_apc(ULONG_PTR data)
|
||||
{
|
||||
CancelIo((HANDLE)data);
|
||||
}
|
||||
|
||||
/*
|
||||
Cancel IO on Windows.
|
||||
|
||||
On XP, issue CancelIo as asynchronous procedure call to the thread
|
||||
that started IO. On Vista+, simpler cancelation is done with
|
||||
CancelIoEx.
|
||||
*/
|
||||
|
||||
int cancel_io(HANDLE handle, DWORD thread_id)
|
||||
{
|
||||
static BOOL (WINAPI *fp_CancelIoEx) (HANDLE, OVERLAPPED *);
|
||||
static volatile int first_time= 1;
|
||||
int rc;
|
||||
HANDLE thread_handle;
|
||||
|
||||
if (first_time)
|
||||
{
|
||||
/* Try to load CancelIoEx using GetProcAddress */
|
||||
InterlockedCompareExchangePointer((volatile void *)&fp_CancelIoEx,
|
||||
GetProcAddress(GetModuleHandle("kernel32"), "CancelIoEx"), NULL);
|
||||
first_time =0;
|
||||
}
|
||||
|
||||
if (fp_CancelIoEx)
|
||||
{
|
||||
return fp_CancelIoEx(handle, NULL)? 0 :-1;
|
||||
}
|
||||
|
||||
thread_handle= OpenThread(THREAD_SET_CONTEXT, FALSE, thread_id);
|
||||
if (thread_handle)
|
||||
{
|
||||
rc= QueueUserAPC(cancel_io_apc, thread_handle, (ULONG_PTR)handle);
|
||||
CloseHandle(thread_handle);
|
||||
}
|
||||
return rc;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int vio_socket_shutdown(Vio *vio, int how)
|
||||
{
|
||||
int ret= shutdown(mysql_socket_getfd(vio->mysql_socket), how);
|
||||
#ifdef _WIN32
|
||||
/* Cancel possible IO in progress (shutdown does not do that on Windows). */
|
||||
(void) cancel_io((HANDLE) mysql_socket_getfd(vio->mysql_socket),
|
||||
vio->thread_id);
|
||||
(void) CancelIoEx((HANDLE)mysql_socket_getfd(vio->mysql_socket), NULL);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue