|
|
|
|
Quelle thread.c
Sprache: C
|
|
/*****************************************************************************
* thread.c : Win32 back-end for LibVLC
*****************************************************************************
* Copyright (C) 1999-2016 VLC authors and VideoLAN
*
* Authors: Jean-Marc Dressler <polux@via.ecp.fr>
* Samuel Hocevar <sam@zoy.org>
* Gildas Bazin <gbazin@netcourrier.com>
* Clément Sténac
* Rémi Denis-Courmont
* Pierre Ynard
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#define _DECL_DLLMAIN
#include <windows.h>
#include <process.h>
#include <vlc_common.h>
#include <vlc_threads.h>
#include <vlc_charset.h>
#include "../libvlc.h"
#include <stdarg.h>
#include <stdatomic.h>
#include <assert.h>
#include <limits.h>
#include <errno.h>
#include <time.h>
#include <vlc_atomic.h>
#if WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
#include <mmsystem.h>
#endif
#ifndef NTDDI_WIN10_RS1
#define NTDDI_WIN10_RS1 0x0A000002
#endif
#if NTDDI_VERSION >= NTDDI_WIN10_RS1 && defined(__MINGW64_VERSION_MAJOR) && __MINGW64_VERSION_MAJOR < 10
WINBASEAPI HRESULT WINAPI SetThreadDescription(HANDLE,PCWSTR);
#endif
/*** Static mutex and condition variable ***/
static SRWLOCK super_lock = SRWLOCK_INIT;
/*** Threads ***/
static thread_local struct vlc_thread *current_thread_ctx = NULL;
struct vlc_thread
{
HANDLE id;
bool killable;
atomic_uint killed;
vlc_cleanup_t *cleaners;
void *(*entry) (void *);
void *data;
};
/*** Thread-specific variables (TLS) ***/
struct vlc_threadvar
{
DWORD id;
void (*destroy) (void *);
struct vlc_threadvar *prev;
struct vlc_threadvar *next;
} *vlc_threadvar_last = NULL;
int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
{
struct vlc_threadvar *var = malloc (sizeof (*var));
if (unlikely(var == NULL))
return errno;
var->id = TlsAlloc();
if (var->id == TLS_OUT_OF_INDEXES)
{
free (var);
return EAGAIN;
}
var->destroy = destr;
var->next = NULL;
*p_tls = var;
if (destr != NULL)
{
AcquireSRWLockExclusive(&super_lock);
var->prev = vlc_threadvar_last;
if (var->prev)
var->prev->next = var;
vlc_threadvar_last = var;
ReleaseSRWLockExclusive(&super_lock);
}
return 0;
}
void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
{
struct vlc_threadvar *var = *p_tls;
if (var->destroy != NULL)
{
AcquireSRWLockExclusive(&super_lock);
if (var->prev != NULL)
var->prev->next = var->next;
if (var->next != NULL)
var->next->prev = var->prev;
else
vlc_threadvar_last = var->prev;
ReleaseSRWLockExclusive(&super_lock);
}
TlsFree (var->id);
free (var);
}
int vlc_threadvar_set (vlc_threadvar_t key, void *value)
{
int saved = GetLastError ();
if (!TlsSetValue(key->id, value))
return ENOMEM;
SetLastError(saved);
return 0;
}
void *vlc_threadvar_get (vlc_threadvar_t key)
{
int saved = GetLastError ();
void *value = TlsGetValue (key->id);
SetLastError(saved);
return value;
}
static void vlc_threadvars_cleanup(void)
{
const struct vlc_threadvar *key;
retry:
AcquireSRWLockShared(&super_lock);
for (key = vlc_threadvar_last; key != NULL; key = key->prev)
{
void *value = TlsGetValue(key->id);
if (value != NULL)
{
ReleaseSRWLockShared(&super_lock);
TlsSetValue(key->id, NULL);
assert(key->destroy != NULL);
key->destroy(value);
goto retry;
}
}
ReleaseSRWLockShared(&super_lock);
}
/*** Futeces^WAddress waits ***/
typedef HRESULT (WINAPI *SetThreadDescription_ptr)(HANDLE, PCWSTR);
static SetThreadDescription_ptr SetThreadDescription_;
#if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
typedef BOOL (WINAPI *WaitOnAddress_ptr)(VOID volatile *, PVOID, SIZE_T, DWORD);
static WaitOnAddress_ptr WaitOnAddress_;
#define WaitOnAddress (*WaitOnAddress_)
typedef VOID (WINAPI *WakeByAddressAll_ptr)(PVOID);
static WakeByAddressAll_ptr WakeByAddressAll_;
#define WakeByAddressAll (*WakeByAddressAll_)
typedef VOID (WINAPI *WakeByAddressSingle_ptr)(PVOID);
static WakeByAddressSingle_ptr WakeByAddressSingle_;
#define WakeByAddressSingle (*WakeByAddressSingle_)
static struct wait_addr_bucket
{
SRWLOCK lock;
CONDITION_VARIABLE wait;
} wait_addr_buckets[32];
static struct wait_addr_bucket *wait_addr_get_bucket(void volatile *addr)
{
uintptr_t u = (uintptr_t)addr;
return wait_addr_buckets + ((u >> 3) % ARRAY_SIZE(wait_addr_buckets));
}
static BOOL WINAPI WaitOnAddressFallback(void volatile *addr, void *value,
SIZE_T size, DWORD ms)
{
struct wait_addr_bucket *bucket = wait_addr_get_bucket(addr);
uint64_t futex, val = 0;
BOOL ret = FALSE;
AcquireSRWLockExclusive(&bucket->lock);
switch (size)
{
case 1:
futex = atomic_load_explicit((atomic_char *)addr,
memory_order_relaxed);
val = *(const char *)value;
break;
case 2:
futex = atomic_load_explicit((atomic_short *)addr,
memory_order_relaxed);
val = *(const short *)value;
break;
case 4:
futex = atomic_load_explicit((atomic_int *)addr,
memory_order_relaxed);
val = *(const int *)value;
break;
case 8:
futex = atomic_load_explicit((atomic_llong *)addr,
memory_order_relaxed);
val = *(const long long *)value;
break;
default:
vlc_assert_unreachable();
}
if (futex == val)
ret = SleepConditionVariableSRW(&bucket->wait, &bucket->lock, ms, 0);
ReleaseSRWLockExclusive(&bucket->lock);
return ret;
}
static void WINAPI WakeByAddressFallback(void *addr)
{
struct wait_addr_bucket *bucket = wait_addr_get_bucket(addr);
/* Acquire the bucket critical section (only) to enforce proper sequencing.
* The critical section does not protect any actual memory object. */
AcquireSRWLockExclusive(&bucket->lock);
/* No other threads can hold the lock for this bucket while it is held
* here. Thus any other thread either:
* - is already sleeping in SleepConditionVariableCS(), and to be woken up
* by the following WakeAllConditionVariable(), or
* - has yet to retrieve the value at the wait address (with the
* 'switch (size)' block). */
ReleaseSRWLockExclusive(&bucket->lock);
/* At this point, other threads can retrieve the value at the wait address.
* But the value will have already been changed by our call site, thus
* (futex == val) will be false, and the threads will not go to sleep. */
/* Wake up any thread that was already sleeping. Since there are more than
* one wait address per bucket, all threads must be woken up :-/ */
WakeAllConditionVariable(&bucket->wait);
}
static void vlc_wait_addr_init(void)
{
for (size_t i = 0; i < ARRAY_SIZE(wait_addr_buckets); i++)
{
struct wait_addr_bucket *bucket = wait_addr_buckets + i;
InitializeSRWLock(&bucket->lock);
InitializeConditionVariable(&bucket->wait);
}
WaitOnAddress_ = WaitOnAddressFallback;
WakeByAddressAll_ = WakeByAddressFallback;
WakeByAddressSingle_ = WakeByAddressFallback;
}
#endif
void vlc_atomic_wait(void *addr, unsigned val)
{
WaitOnAddress(addr, &val, sizeof (val), INFINITE);
}
int vlc_atomic_timedwait(void *addr, unsigned val, vlc_tick_t deadline)
{
vlc_tick_t delay;
for(;;)
{
delay = deadline - vlc_tick_now();
if (delay < 0)
return ETIMEDOUT; // deadline passed
DWORD ms;
int64_t idelay = MS_FROM_VLC_TICK(delay);
static_assert(sizeof(unsigned long) <= sizeof(DWORD), "unknown max DWORD");
if (unlikely(idelay > ULONG_MAX))
ms = ULONG_MAX;
else
ms = idelay;
if (WaitOnAddress(addr, &val, sizeof (val), ms))
return 0;
if (GetLastError() == ERROR_TIMEOUT)
return ETIMEDOUT;
}
}
void vlc_atomic_notify_one(void *addr)
{
WakeByAddressSingle(addr);
}
void vlc_atomic_notify_all(void *addr)
{
WakeByAddressAll(addr);
}
/*** Threads ***/
static
unsigned
__stdcall ThreadEntry (void *p)
{
struct vlc_thread *th = p;
current_thread_ctx = th;
th->killable = true;
th->data = th->entry (th->data);
assert(th->data != VLC_THREAD_CANCELED); // don't hijack our internal values
current_thread_ctx = NULL;
return 0;
}
int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),
void *data)
{
struct vlc_thread *th = malloc (sizeof (*th));
if (unlikely(th == NULL))
return ENOMEM;
th->entry = entry;
th->data = data;
th->killable = false; /* not until vlc_entry() ! */
atomic_init(&th->killed, false);
th->cleaners = NULL;
HANDLE h;
/* When using the MSVCRT C library you have to use the _beginthreadex
* function instead of CreateThread, otherwise you'll end up with
* memory leaks and the signal functions not working (see Microsoft
* Knowledge Base, article 104641) */
h = (HANDLE)(uintptr_t) _beginthreadex (NULL, 0, ThreadEntry, th, 0, NULL);
if (h == 0)
{
int err = errno;
free (th);
return err;
}
th->id = h;
if (p_handle != NULL)
*p_handle = th;
return 0;
}
void vlc_join (vlc_thread_t th, void **result)
{
DWORD ret;
do
{
ret = WaitForSingleObject(th->id, INFINITE);
assert(ret != WAIT_ABANDONED_0);
}
while (ret == WAIT_FAILED);
if (result != NULL)
*result = th->data;
CloseHandle (th->id);
free(th);
}
unsigned long vlc_thread_id (void)
{
return GetCurrentThreadId ();
}
void (vlc_thread_set_name)(const char *name)
{
if (SetThreadDescription_)
{
wchar_t *wname = ToWide(name);
SetThreadDescription_(GetCurrentThread(), wname);
free(wname);
}
}
/*** Thread cancellation ***/
/* APC procedure for thread cancellation */
static void CALLBACK vlc_cancel_self (ULONG_PTR self)
{
(void) self;
}
void vlc_cancel (vlc_thread_t th)
{
atomic_store_explicit(&th->killed, true, memory_order_release);
vlc_atomic_notify_one(&th->killed);
QueueUserAPC (vlc_cancel_self, th->id, (uintptr_t)th);
}
int vlc_savecancel (void)
{
struct vlc_thread *th = current_thread_ctx;
if (th == NULL)
return false; /* Main thread - cannot be cancelled anyway */
bool state = th->killable;
th->killable = false;
return state;
}
void vlc_restorecancel (int state)
{
struct vlc_thread *th = current_thread_ctx;
assert (state == false || state == true);
if (th == NULL)
return; /* Main thread - cannot be cancelled anyway */
assert (!th->killable);
th->killable = !!state;
}
_Noreturn static void vlc_docancel(struct vlc_thread *th)
{
th->killable = false; /* Do not re-enter cancellation cleanup */
for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
p->proc (p->data);
th->data = VLC_THREAD_CANCELED;
_endthreadex(0);
}
void vlc_testcancel (void)
{
struct vlc_thread *th = current_thread_ctx;
if (th == NULL)
return; /* Main thread - cannot be cancelled anyway */
if (!th->killable)
return;
if (!atomic_load_explicit(&th->killed, memory_order_relaxed))
return;
vlc_docancel(th);
}
void vlc_control_cancel (vlc_cleanup_t *cleaner)
{
/* NOTE: This function only modifies thread-specific data, so there is no
* need to lock anything. */
struct vlc_thread *th = current_thread_ctx;
if (th == NULL)
return; /* Main thread - cannot be cancelled anyway */
if (cleaner != NULL)
{
/* cleaner is a pointer to the caller stack, no need to allocate
* and copy anything. As a nice side effect, this cannot fail. */
cleaner->next = th->cleaners;
th->cleaners = cleaner;
}
else
{
th->cleaners = th->cleaners->next;
}
}
/*** Clock ***/
static union
{
struct
{
LARGE_INTEGER freq;
} perf;
} clk;
static vlc_tick_t mdate_perf (void)
{
/* We don't need the real date, just the value of a high precision timer */
LARGE_INTEGER counter;
if (unlikely(!QueryPerformanceCounter(&counter)))
abort ();
/* Convert to from (1/freq) to microsecond resolution */
/* We need to split the division to avoid 63-bits overflow */
return vlc_tick_from_frac(counter.QuadPart, clk.perf.freq.QuadPart);
}
static vlc_tick_t mdate_perf_100ns(void)
{
/* We don't need the real date, just the value of a high precision timer */
LARGE_INTEGER counter;
if (unlikely(!QueryPerformanceCounter(&counter)))
abort ();
return VLC_TICK_FROM_MSFTIME(counter.QuadPart);
}
static vlc_tick_t mdate_wall (void)
{
FILETIME ts;
ULARGE_INTEGER s;
#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
GetSystemTimePreciseAsFileTime (&ts);
#else // _WIN32_WINNT < _WIN32_WINNT_WIN8
GetSystemTimeAsFileTime (&ts);
#endif // _WIN32_WINNT < _WIN32_WINNT_WIN8
s.LowPart = ts.dwLowDateTime;
s.HighPart = ts.dwHighDateTime;
/* hundreds of nanoseconds */
static_assert ((10000000 % CLOCK_FREQ) == 0, "Broken frequencies ratio");
return VLC_TICK_FROM_MSFTIME(s.QuadPart);
}
#if WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
static vlc_tick_t mdate_multimedia(void)
{
DWORD ts = timeGetTime ();
/* milliseconds */
static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
return VLC_TICK_FROM_MS(ts);
}
#endif
static vlc_tick_t (*mdate_selected) (void) = mdate_wall;
vlc_tick_t vlc_tick_now (void)
{
return mdate_selected ();
}
void (vlc_tick_wait)(vlc_tick_t deadline)
{
vlc_tick_t delay;
struct vlc_thread *th = current_thread_ctx;
if (likely(th != NULL) && th->killable)
{
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
do
{
if (atomic_load_explicit(&th->killed, memory_order_acquire))
vlc_docancel(th);
}
while (vlc_atomic_timedwait(&th->killed, false, deadline) == 0);
return;
#else
if (atomic_load_explicit(&th->killed, memory_order_relaxed))
vlc_docancel(th);
#endif
}
while ((delay = (deadline - vlc_tick_now())) > 0)
{
int64_t idelay = MS_FROM_VLC_TICK(delay + VLC_TICK_FROM_MS(1)-1);
DWORD delay_ms;
static_assert(sizeof(unsigned long) <= sizeof(DWORD), "unknown max DWORD");
if (unlikely(idelay > ULONG_MAX))
delay_ms = ULONG_MAX;
else
delay_ms = idelay;
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
Sleep(delay_ms);
#else
SleepEx(delay_ms, TRUE);
if (likely(th != NULL) && th->killable)
{
if (atomic_load_explicit(&th->killed, memory_order_relaxed))
vlc_docancel(th);
}
#endif
if (delay_ms != ULONG_MAX)
break;
}
}
void (vlc_tick_sleep)(vlc_tick_t delay)
{
vlc_tick_wait (vlc_tick_now () + delay);
}
static void SelectClockSource(libvlc_int_t *obj)
{
// speed comparison / granularity / counts during sleep
// multimedia:102000 / 5 ms / no
// perf: 155612 / ~100 ns / yes
// wall: 181593 / 100 ns / yes
char *str = var_InheritString(obj, "clock-source");
const char *name = str != NULL ? str : "perf";
#if WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
if (!strcmp (name, "multimedia"))
{
TIMECAPS caps;
msg_Dbg (obj, "using multimedia timers as clock source");
if (timeGetDevCaps (&caps, sizeof (caps)) != MMSYSERR_NOERROR)
abort();
msg_Dbg (obj, " min period: %u ms, max period: %u ms",
caps.wPeriodMin, caps.wPeriodMax);
mdate_selected = mdate_multimedia;
timeBeginPeriod(__MAX(5, caps.wPeriodMin));
}
else
#endif
if (!strcmp (name, "perf"))
{
msg_Dbg (obj, "using performance counters as clock source");
if (!QueryPerformanceFrequency (&clk.perf.freq))
abort ();
msg_Dbg (obj, " frequency: %llu Hz", clk.perf.freq.QuadPart);
if (clk.perf.freq.QuadPart == 10000000)
mdate_selected = mdate_perf_100ns;
else
mdate_selected = mdate_perf;
}
else
{
msg_Err (obj, "invalid clock source \"%s\"", name);
abort ();
}
free (str);
}
/*** CPU ***/
unsigned vlc_GetCPUCount (void)
{
SYSTEM_INFO systemInfo;
GetNativeSystemInfo(&systemInfo);
return systemInfo.dwNumberOfProcessors;
}
/*** Initialization ***/
void vlc_threads_setup(libvlc_int_t *vlc)
{
SelectClockSource(vlc);
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
/* Raise default priority of the current process */
#ifndef ABOVE_NORMAL_PRIORITY_CLASS
# define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
#endif
if (var_InheritBool(vlc, "high-priority"))
{
if (SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS)
|| SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS))
msg_Dbg(vlc, "raised process priority");
else
msg_Dbg(vlc, "could not raise process priority");
}
#endif
}
#define LOOKUP(s) (((s##_) = (s##_ptr)GetProcAddress(h, #s)) != NULL)
int __stdcall DllMain (void *hinstDll, DWORD fdwReason, void *lpvReserved)
{
(void) hinstDll;
(void) lpvReserved;
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
{
#if NTDDI_VERSION >= NTDDI_WIN10_RS1
SetThreadDescription_ = SetThreadDescription;
#else // !NTDDI_WIN10_RS1
HMODULE h;
h = GetModuleHandle(TEXT("kernelbase.dll"));
if (h == NULL)
h = GetModuleHandle(TEXT("api-ms-win-core-processthreads-l1-1-3.dll"));
if (h != NULL)
LOOKUP(SetThreadDescription);
else
SetThreadDescription_ = NULL;
#endif // !NTDDI_WIN10_RS1
#if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
h = GetModuleHandle(TEXT("api-ms-win-core-synch-l1-2-0.dll"));
if (h == NULL || !LOOKUP(WaitOnAddress)
|| !LOOKUP(WakeByAddressAll) || !LOOKUP(WakeByAddressSingle))
{
vlc_wait_addr_init();
}
#endif
break;
}
case DLL_THREAD_DETACH:
vlc_threadvars_cleanup();
break;
}
return TRUE;
}
¤ Dauer der Verarbeitung: 0.7 Sekunden
¤
*© Formatika GbR, Deutschland
|
|
2026-11-01
|
|
|
|
|
Neuigkeiten |
| Aktuelles |
| Motto des Tages |
|
Open Source Software |
|
|
|
Jenseits des Üblichen ....
|
|
Besucherstatistik |
|
|
| Statistik |
| #Sources=1104842 |
| #Domains=499163 |
|
|