mp_thread: add win32 implementation

This commit is contained in:
Kacper Michajłow 2023-10-22 02:43:15 +02:00 committed by Dudemanguy
parent 55ed50ba90
commit 56d35da180
13 changed files with 252 additions and 37 deletions

View File

@ -1,4 +1,3 @@
#include <pthread.h>
#include <stdatomic.h>
#include <time.h>
#include <unistd.h>

View File

@ -17,7 +17,6 @@
#include "config.h"
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

View File

@ -248,7 +248,6 @@ sources = files(
'osdep/io.c',
'osdep/semaphore_osx.c',
'osdep/subprocess.c',
'osdep/threads.c',
'osdep/timer.c',
## tree_allocator
@ -354,31 +353,18 @@ if features['cplugins'] and not win32
link_flags += '-rdynamic'
endif
# Note: this include is only used for windows pthreads and
# must be accompanied immediately by the following flags.
# This currently works because these are the last flags set
# in the build for windows. Adding any new flags after this
# will probably break something.
includedir = []
win32_pthreads = get_option('win32-internal-pthreads').require(
win32 and not posix,
error_message: 'the os is not win32!',
)
features += {'win32-internal-pthreads': win32_pthreads.allowed()}
if features['win32-internal-pthreads']
flags += ['-isystem', '-I', '-DIN_WINPTHREAD']
includedir += include_directories('osdep/win32/include')
sources += files('osdep/win32/pthread.c')
else
# pthreads is intentionally left undefined in win32 branch to find incorrect
# uses of it immediately
win32_threads = get_option('win32-threads').require(win32)
features += {'win32-threads': win32_threads.allowed()}
if not features['win32-threads']
pthreads = dependency('threads')
sources += files('osdep/threads.c')
dependencies += pthreads
endif
pthread_debug = get_option('pthread-debug').require(
win32_pthreads.disabled(),
error_message: 'win32-internal-pthreads was found!',
win32_threads.disabled(),
error_message: 'win32-threads was found!',
)
features += {'pthread-debug': pthread_debug.allowed()}
if features['pthread-debug']
@ -1721,8 +1707,8 @@ client_api_version = major + '.' + minor + '.0'
libmpv = library('mpv', sources, dependencies: dependencies, gnu_symbol_visibility: 'hidden',
link_args: cc.get_supported_link_arguments(['-Wl,-Bsymbolic']),
version: client_api_version, include_directories: includedir,
install: get_option('libmpv'), build_by_default: get_option('libmpv'))
version: client_api_version, install: get_option('libmpv'),
build_by_default: get_option('libmpv'))
if get_option('libmpv')
@ -1768,7 +1754,7 @@ if get_option('cplayer')
install_data('etc/mpv-symbolic.svg', install_dir: join_paths(hicolor_dir, 'symbolic', 'apps'))
mpv = executable('mpv', objects: libmpv.extract_all_objects(recursive: true), dependencies: dependencies,
win_subsystem: 'windows,6.0', include_directories: includedir, install: true)
win_subsystem: 'windows,6.0', install: true)
endif
if get_option('tests')

View File

@ -34,7 +34,7 @@ option('uchardet', type: 'feature', value: 'auto', description: 'uchardet suppor
option('uwp', type: 'feature', value: 'disabled', description: 'Universal Windows Platform')
option('vapoursynth', type: 'feature', value: 'auto', description: 'VapourSynth filter bridge')
option('vector', type: 'feature', value: 'auto', description: 'GCC vector instructions')
option('win32-internal-pthreads', type: 'feature', value: 'auto', description: 'internal pthread wrapper for win32 (Vista+)')
option('win32-threads', type: 'feature', value: 'auto', description: 'win32 threads')
option('zimg', type: 'feature', value: 'auto', description: 'libzimg support (high quality software scaler)')
option('zlib', type: 'feature', value: 'auto', description: 'zlib')

222
osdep/threads-win32.h Normal file
View File

@ -0,0 +1,222 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <errno.h>
#include <process.h>
#include <windows.h>
#include "common/common.h"
#include "timer.h"
typedef struct {
char use_cs;
union {
CRITICAL_SECTION cs;
SRWLOCK srw;
};
} mp_mutex;
typedef CONDITION_VARIABLE mp_cond;
typedef INIT_ONCE mp_once;
typedef mp_mutex mp_static_mutex;
typedef HANDLE mp_thread;
typedef DWORD mp_thread_id;
#define MP_STATIC_COND_INITIALIZER CONDITION_VARIABLE_INIT
#define MP_STATIC_MUTEX_INITIALIZER (mp_mutex){ .srw = SRWLOCK_INIT }
#define MP_STATIC_ONCE_INITIALIZER INIT_ONCE_STATIC_INIT
static inline int mp_mutex_init_type_internal(mp_mutex *mutex, enum mp_mutex_type mtype)
{
mutex->use_cs = mtype == MP_MUTEX_RECURSIVE;
if (mutex->use_cs)
return !InitializeCriticalSectionEx(&mutex->cs, 0, 0);
InitializeSRWLock(&mutex->srw);
return 0;
}
#define mp_mutex_init_type(mutex, mtype) \
assert(!mp_mutex_init_type_internal(mutex, mtype))
static inline int mp_mutex_destroy(mp_mutex *mutex)
{
if (mutex->use_cs)
DeleteCriticalSection(&mutex->cs);
return 0;
}
static inline int mp_mutex_lock(mp_mutex *mutex)
{
if (mutex->use_cs) {
EnterCriticalSection(&mutex->cs);
} else {
AcquireSRWLockExclusive(&mutex->srw);
}
return 0;
}
static inline int mp_mutex_trylock(mp_mutex *mutex)
{
if (mutex->use_cs)
return !TryEnterCriticalSection(&mutex->cs);
return !TryAcquireSRWLockExclusive(&mutex->srw);
}
static inline int mp_mutex_unlock(mp_mutex *mutex)
{
if (mutex->use_cs) {
LeaveCriticalSection(&mutex->cs);
} else {
ReleaseSRWLockExclusive(&mutex->srw);
}
return 0;
}
static inline int mp_cond_init(mp_cond *cond)
{
InitializeConditionVariable(cond);
return 0;
}
static inline int mp_cond_destroy(mp_cond *cond)
{
// condition variables are not destroyed
(void) cond;
return 0;
}
static inline int mp_cond_broadcast(mp_cond *cond)
{
WakeAllConditionVariable(cond);
return 0;
}
static inline int mp_cond_signal(mp_cond *cond)
{
WakeConditionVariable(cond);
return 0;
}
static inline int mp_cond_timedwait(mp_cond *cond, mp_mutex *mutex, int64_t timeout)
{
timeout = MPCLAMP(timeout, 0, MP_TIME_MS_TO_NS(INFINITE)) / MP_TIME_MS_TO_NS(1);
int ret = 0;
int hrt = mp_start_hires_timers(timeout);
BOOL bRet;
if (mutex->use_cs) {
bRet = SleepConditionVariableCS(cond, &mutex->cs, timeout);
} else {
bRet = SleepConditionVariableSRW(cond, &mutex->srw, timeout, 0);
}
if (bRet == FALSE)
ret = GetLastError() == ERROR_TIMEOUT ? ETIMEDOUT : EINVAL;
mp_end_hires_timers(hrt);
return ret;
}
static inline int mp_cond_wait(mp_cond *cond, mp_mutex *mutex)
{
return mp_cond_timedwait(cond, mutex, MP_TIME_MS_TO_NS(INFINITE));
}
static inline int mp_cond_timedwait_until(mp_cond *cond, mp_mutex *mutex, int64_t until)
{
return mp_cond_timedwait(cond, mutex, until - mp_time_ns());
}
static inline int mp_exec_once(mp_once *once, void (*init_routine)(void))
{
BOOL pending;
if (!InitOnceBeginInitialize(once, 0, &pending, NULL))
abort();
if (pending) {
init_routine();
InitOnceComplete(once, 0, NULL);
}
return 0;
}
#define MP_THREAD_VOID unsigned __stdcall
#define MP_THREAD_RETURN() return 0
static inline int mp_thread_create(mp_thread *thread,
MP_THREAD_VOID (*fun)(void *),
void *__restrict arg)
{
*thread = (HANDLE) _beginthreadex(NULL, 0, fun, arg, 0, NULL);
return *thread ? 0 : -1;
}
static inline int mp_thread_join(mp_thread thread)
{
DWORD ret = WaitForSingleObject(thread, INFINITE);
if (ret != WAIT_OBJECT_0)
return ret == WAIT_ABANDONED ? EINVAL : EDEADLK;
CloseHandle(thread);
return 0;
}
static inline int mp_thread_join_id(mp_thread_id id)
{
mp_thread thread = OpenThread(SYNCHRONIZE, FALSE, id);
if (!thread)
return ESRCH;
int ret = mp_thread_join(thread);
if (ret)
CloseHandle(thread);
return ret;
}
static inline int mp_thread_detach(mp_thread thread)
{
return CloseHandle(thread) ? 0 : EINVAL;
}
#define mp_thread_current_id GetCurrentThreadId
#define mp_thread_id_equal(a, b) ((a) == (b))
#define mp_thread_get_id(thread) GetThreadId(thread)
wchar_t *mp_from_utf8(void *talloc_ctx, const char *s);
static inline void mp_thread_set_name(const char *name)
{
#if !HAVE_UWP
HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
if (!kernel32)
return;
HRESULT (WINAPI *pSetThreadDescription)(HANDLE, PCWSTR) =
(void *) GetProcAddress(kernel32, "SetThreadDescription");
if (!pSetThreadDescription)
return;
wchar_t *wname = mp_from_utf8(NULL, name);
pSetThreadDescription(GetCurrentThread(), wname);
talloc_free(wname);
#endif
}
static inline int64_t mp_thread_cpu_time_ns(mp_thread_id thread)
{
(void) thread;
return 0;
}

View File

@ -17,7 +17,6 @@
#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include "common/common.h"
#include "config.h"
@ -46,7 +45,7 @@ void mpthread_set_name(const char *name)
strncpy(tname, name, sizeof(tname) - 1);
pthread_setname_np(pthread_self(), tname);
}
#elif HAVE_WIN32_INTERNAL_PTHREADS || HAVE_BSD_THREAD_NAME
#elif HAVE_BSD_THREAD_NAME
pthread_set_name_np(pthread_self(), name);
#elif HAVE_OSX_THREAD_NAME
pthread_setname_np(name);

View File

@ -4,6 +4,8 @@
#include <pthread.h>
#include <inttypes.h>
#include "config.h"
// Helper to reduce boiler plate.
int mpthread_mutex_init_recursive(pthread_mutex_t *mutex);
@ -81,6 +83,10 @@ enum mp_mutex_type {
#define mp_mutex_init_type(mutex, mtype) \
assert(!mp_mutex_init_type_internal(mutex, mtype))
#if HAVE_WIN32_THREADS
#include "threads-win32.h"
#else
#include "threads-posix.h"
#endif
#endif

View File

@ -67,6 +67,8 @@ int64_t mp_time_ns_add(int64_t time_ns, double timeout_sec)
return time_ns + ti;
}
#if !HAVE_WIN32_THREADS
struct timespec mp_time_ns_to_realtime(int64_t time_ns)
{
struct timespec ts = {0};
@ -91,3 +93,5 @@ struct timespec mp_rel_time_to_timespec(double timeout_sec)
{
return mp_time_ns_to_realtime(mp_time_ns_add(mp_time_ns(), timeout_sec));
}
#endif

View File

@ -15,7 +15,6 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <pthread.h>
#include <float.h>
#include <stdlib.h>
#include <inttypes.h>

View File

@ -15,7 +15,6 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

View File

@ -15,7 +15,6 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <pthread.h>
#include <archive.h>
#include <archive_entry.h>

View File

@ -40,11 +40,12 @@ if win32
test_utils_files += 'osdep/windows_utils.c'
endif
if features['pthread-debug']
test_utils_files += 'osdep/threads.c'
endif
# The zimg code requires using threads.
if features['win32-internal-pthreads']
test_utils_args += '-DWIN32_TESTS'
test_utils_files += ['osdep/win32/pthread.c']
else
if not features['win32-threads']
test_utils_deps += pthreads
endif
@ -59,7 +60,6 @@ test_utils = static_library('test-utils', 'test_utils.c', include_directories: i
# For getting imgfmts and stuff.
img_utils_files = [
'misc/thread_pool.c',
'osdep/threads.c',
'video/csputils.c',
'video/fmt-conversion.c',
'video/img_format.c',

View File

@ -1,6 +1,7 @@
#include "common/common.h"
#include "osdep/timer.h"
#include "test_utils.h"
#include "config.h"
#include <time.h>
#include <sys/time.h>
@ -37,6 +38,7 @@ int main(void)
assert_int_equal(mp_time_ns_add(test2, 20.44), INT64_MAX);
}
#if !HAVE_WIN32_THREADS
/* conversion */
{
struct timeval tv;
@ -49,6 +51,7 @@ int main(void)
ts = mp_rel_time_to_timespec(0.0);
assert_true(llabs(tv.tv_sec - ts.tv_sec) <= 1);
}
#endif
return 0;
}