Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-fonts
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Aleksandr Isakov
wine-fonts
Commits
6bdb914a
Commit
6bdb914a
authored
Nov 17, 2021
by
Zebediah Figura
Committed by
Alexandre Julliard
Nov 18, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ntdll: Reimplement Win32 futexes on top of thread-ID alerts.
Signed-off-by:
Zebediah Figura
<
zfigura@codeweavers.com
>
Signed-off-by:
Alexandre Julliard
<
julliard@winehq.org
>
parent
842ecb45
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
173 additions
and
173 deletions
+173
-173
sync.c
dlls/ntdll/sync.c
+172
-3
loader.c
dlls/ntdll/unix/loader.c
+0
-3
sync.c
dlls/ntdll/unix/sync.c
+0
-162
unixlib.h
dlls/ntdll/unixlib.h
+1
-5
No files found.
dlls/ntdll/sync.c
View file @
6bdb914a
...
...
@@ -34,11 +34,18 @@
#include "windef.h"
#include "winternl.h"
#include "wine/debug.h"
#include "wine/list.h"
#include "ntdll_misc.h"
WINE_DEFAULT_DEBUG_CHANNEL
(
sync
);
WINE_DECLARE_DEBUG_CHANNEL
(
relay
);
static
const
char
*
debugstr_timeout
(
const
LARGE_INTEGER
*
timeout
)
{
if
(
!
timeout
)
return
"(infinite)"
;
return
wine_dbgstr_longlong
(
timeout
->
QuadPart
);
}
/******************************************************************
* RtlRunOnceInitialize (NTDLL.@)
*/
...
...
@@ -863,13 +870,113 @@ NTSTATUS WINAPI RtlSleepConditionVariableSRW( RTL_CONDITION_VARIABLE *variable,
return
status
;
}
/* RtlWaitOnAddress() and RtlWakeAddress*(), hereafter referred to as "Win32
* futexes", offer futex-like semantics with a variable set of address sizes,
* but are limited to a single process. They are also fair—the documentation
* specifies this, and tests bear it out.
*
* On Windows they are implemented using NtAlertThreadByThreadId and
* NtWaitForAlertByThreadId, which manipulate a single flag (similar to an
* auto-reset event) per thread. This can be tested by attempting to wake a
* thread waiting in RtlWaitOnAddress() via NtAlertThreadByThreadId.
*/
struct
futex_entry
{
struct
list
entry
;
const
void
*
addr
;
DWORD
tid
;
};
struct
futex_queue
{
struct
list
queue
;
LONG
lock
;
};
static
struct
futex_queue
futex_queues
[
256
];
static
struct
futex_queue
*
get_futex_queue
(
const
void
*
addr
)
{
ULONG_PTR
val
=
(
ULONG_PTR
)
addr
;
return
&
futex_queues
[(
val
>>
4
)
%
ARRAY_SIZE
(
futex_queues
)];
}
static
void
spin_lock
(
LONG
*
lock
)
{
while
(
InterlockedCompareExchange
(
lock
,
-
1
,
0
))
YieldProcessor
();
}
static
void
spin_unlock
(
LONG
*
lock
)
{
InterlockedExchange
(
lock
,
0
);
}
static
BOOL
compare_addr
(
const
void
*
addr
,
const
void
*
cmp
,
SIZE_T
size
)
{
switch
(
size
)
{
case
1
:
return
(
*
(
const
UCHAR
*
)
addr
==
*
(
const
UCHAR
*
)
cmp
);
case
2
:
return
(
*
(
const
USHORT
*
)
addr
==
*
(
const
USHORT
*
)
cmp
);
case
4
:
return
(
*
(
const
ULONG
*
)
addr
==
*
(
const
ULONG
*
)
cmp
);
case
8
:
return
(
*
(
const
ULONG64
*
)
addr
==
*
(
const
ULONG64
*
)
cmp
);
}
return
FALSE
;
}
/***********************************************************************
* RtlWaitOnAddress (NTDLL.@)
*/
NTSTATUS
WINAPI
RtlWaitOnAddress
(
const
void
*
addr
,
const
void
*
cmp
,
SIZE_T
size
,
const
LARGE_INTEGER
*
timeout
)
{
return
unix_funcs
->
RtlWaitOnAddress
(
addr
,
cmp
,
size
,
timeout
);
struct
futex_queue
*
queue
=
get_futex_queue
(
addr
);
struct
futex_entry
entry
;
NTSTATUS
ret
;
TRACE
(
"addr %p cmp %p size %#Ix timeout %s
\n
"
,
addr
,
cmp
,
size
,
debugstr_timeout
(
timeout
));
if
(
size
!=
1
&&
size
!=
2
&&
size
!=
4
&&
size
!=
8
)
return
STATUS_INVALID_PARAMETER
;
entry
.
addr
=
addr
;
entry
.
tid
=
GetCurrentThreadId
();
spin_lock
(
&
queue
->
lock
);
/* Do the comparison inside of the spinlock, to reduce spurious wakeups. */
if
(
!
compare_addr
(
addr
,
cmp
,
size
))
{
spin_unlock
(
&
queue
->
lock
);
return
STATUS_SUCCESS
;
}
if
(
!
queue
->
queue
.
next
)
list_init
(
&
queue
->
queue
);
list_add_tail
(
&
queue
->
queue
,
&
entry
.
entry
);
spin_unlock
(
&
queue
->
lock
);
ret
=
NtWaitForAlertByThreadId
(
NULL
,
timeout
);
spin_lock
(
&
queue
->
lock
);
/* We may have already been removed by a call to RtlWakeAddressSingle(). */
if
(
entry
.
addr
)
list_remove
(
&
entry
.
entry
);
spin_unlock
(
&
queue
->
lock
);
TRACE
(
"returning %#x
\n
"
,
ret
);
if
(
ret
==
STATUS_ALERTED
)
ret
=
STATUS_SUCCESS
;
return
ret
;
}
/***********************************************************************
...
...
@@ -877,7 +984,37 @@ NTSTATUS WINAPI RtlWaitOnAddress( const void *addr, const void *cmp, SIZE_T size
*/
void
WINAPI
RtlWakeAddressAll
(
const
void
*
addr
)
{
return
unix_funcs
->
RtlWakeAddressAll
(
addr
);
struct
futex_queue
*
queue
=
get_futex_queue
(
addr
);
unsigned
int
count
=
0
,
i
;
struct
futex_entry
*
entry
;
DWORD
tids
[
256
];
TRACE
(
"%p
\n
"
,
addr
);
if
(
!
addr
)
return
;
spin_lock
(
&
queue
->
lock
);
if
(
!
queue
->
queue
.
next
)
list_init
(
&
queue
->
queue
);
LIST_FOR_EACH_ENTRY
(
entry
,
&
queue
->
queue
,
struct
futex_entry
,
entry
)
{
if
(
entry
->
addr
==
addr
)
{
/* Try to buffer wakes, so that we don't make a system call while
* holding a spinlock. */
if
(
count
<
ARRAY_SIZE
(
tids
))
tids
[
count
++
]
=
entry
->
tid
;
else
NtAlertThreadByThreadId
(
(
HANDLE
)(
DWORD_PTR
)
entry
->
tid
);
}
}
spin_unlock
(
&
queue
->
lock
);
for
(
i
=
0
;
i
<
count
;
++
i
)
NtAlertThreadByThreadId
(
(
HANDLE
)(
DWORD_PTR
)
tids
[
i
]
);
}
/***********************************************************************
...
...
@@ -885,5 +1022,37 @@ void WINAPI RtlWakeAddressAll( const void *addr )
*/
void
WINAPI
RtlWakeAddressSingle
(
const
void
*
addr
)
{
return
unix_funcs
->
RtlWakeAddressSingle
(
addr
);
struct
futex_queue
*
queue
=
get_futex_queue
(
addr
);
struct
futex_entry
*
entry
;
DWORD
tid
=
0
;
TRACE
(
"%p
\n
"
,
addr
);
if
(
!
addr
)
return
;
spin_lock
(
&
queue
->
lock
);
if
(
!
queue
->
queue
.
next
)
list_init
(
&
queue
->
queue
);
LIST_FOR_EACH_ENTRY
(
entry
,
&
queue
->
queue
,
struct
futex_entry
,
entry
)
{
if
(
entry
->
addr
==
addr
)
{
/* Try to buffer wakes, so that we don't make a system call while
* holding a spinlock. */
tid
=
entry
->
tid
;
/* Remove this entry from the queue, so that a simultaneous call to
* RtlWakeAddressSingle() will not also wake it—two simultaneous
* calls must wake at least two waiters if they exist. */
entry
->
addr
=
NULL
;
list_remove
(
&
entry
->
entry
);
break
;
}
}
spin_unlock
(
&
queue
->
lock
);
if
(
tid
)
NtAlertThreadByThreadId
(
(
HANDLE
)(
DWORD_PTR
)
tid
);
}
dlls/ntdll/unix/loader.c
View file @
6bdb914a
...
...
@@ -2145,9 +2145,6 @@ static struct unix_funcs unix_funcs =
NtCurrentTeb
,
#endif
RtlGetSystemTimePrecise
,
RtlWaitOnAddress
,
RtlWakeAddressAll
,
RtlWakeAddressSingle
,
fast_RtlpWaitForCriticalSection
,
fast_RtlpUnWaitCriticalSection
,
fast_RtlDeleteCriticalSection
,
...
...
dlls/ntdll/unix/sync.c
View file @
6bdb914a
...
...
@@ -73,10 +73,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(sync);
HANDLE
keyed_event
=
0
;
static
const
LARGE_INTEGER
zero_timeout
;
static
pthread_mutex_t
addr_mutex
=
PTHREAD_MUTEX_INITIALIZER
;
static
const
char
*
debugstr_timeout
(
const
LARGE_INTEGER
*
timeout
)
{
if
(
!
timeout
)
return
"(infinite)"
;
...
...
@@ -186,24 +182,6 @@ static void timespec_from_timeout( struct timespec *timespec, const LARGE_INTEGE
#endif
static
BOOL
compare_addr
(
const
void
*
addr
,
const
void
*
cmp
,
SIZE_T
size
)
{
switch
(
size
)
{
case
1
:
return
(
*
(
const
UCHAR
*
)
addr
==
*
(
const
UCHAR
*
)
cmp
);
case
2
:
return
(
*
(
const
USHORT
*
)
addr
==
*
(
const
USHORT
*
)
cmp
);
case
4
:
return
(
*
(
const
ULONG
*
)
addr
==
*
(
const
ULONG
*
)
cmp
);
case
8
:
return
(
*
(
const
ULONG64
*
)
addr
==
*
(
const
ULONG64
*
)
cmp
);
}
return
FALSE
;
}
/* create a struct security_descriptor and contained information in one contiguous piece of memory */
NTSTATUS
alloc_object_attributes
(
const
OBJECT_ATTRIBUTES
*
attr
,
struct
object_attributes
**
ret
,
data_size_t
*
ret_len
)
...
...
@@ -2977,71 +2955,6 @@ NTSTATUS CDECL fast_RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable,
return
STATUS_SUCCESS
;
}
/* We can't map addresses to futex directly, because an application can wait on
* 8 bytes, and we can't pass all 8 as the compare value to futex(). Instead we
* map all addresses to a small fixed table of futexes. This may result in
* spurious wakes, but the application is already expected to handle those. */
static
int
addr_futex_table
[
256
];
static
inline
int
*
hash_addr
(
const
void
*
addr
)
{
ULONG_PTR
val
=
(
ULONG_PTR
)
addr
;
return
&
addr_futex_table
[(
val
>>
2
)
&
255
];
}
static
inline
NTSTATUS
fast_wait_addr
(
const
void
*
addr
,
const
void
*
cmp
,
SIZE_T
size
,
const
LARGE_INTEGER
*
timeout
)
{
int
*
futex
;
int
val
;
struct
timespec
timespec
;
int
ret
;
if
(
!
use_futexes
())
return
STATUS_NOT_IMPLEMENTED
;
futex
=
hash_addr
(
addr
);
/* We must read the previous value of the futex before checking the value
* of the address being waited on. That way, if we receive a wake between
* now and waiting on the futex, we know that val will have changed.
* Use an atomic load so that memory accesses are ordered between this read
* and the increment below. */
val
=
InterlockedCompareExchange
(
futex
,
0
,
0
);
if
(
!
compare_addr
(
addr
,
cmp
,
size
))
return
STATUS_SUCCESS
;
if
(
timeout
)
{
timespec_from_timeout
(
&
timespec
,
timeout
);
ret
=
futex_wait
(
futex
,
val
,
&
timespec
);
}
else
ret
=
futex_wait
(
futex
,
val
,
NULL
);
if
(
ret
==
-
1
&&
errno
==
ETIMEDOUT
)
return
STATUS_TIMEOUT
;
return
STATUS_SUCCESS
;
}
static
inline
NTSTATUS
fast_wake_addr
(
const
void
*
addr
)
{
int
*
futex
;
if
(
!
use_futexes
())
return
STATUS_NOT_IMPLEMENTED
;
futex
=
hash_addr
(
addr
);
InterlockedIncrement
(
futex
);
futex_wake
(
futex
,
INT_MAX
);
return
STATUS_SUCCESS
;
}
#else
NTSTATUS
CDECL
fast_RtlTryAcquireSRWLockExclusive
(
RTL_SRWLOCK
*
lock
)
...
...
@@ -3084,79 +2997,4 @@ NTSTATUS CDECL fast_wait_cv( RTL_CONDITION_VARIABLE *variable, const void *value
return
STATUS_NOT_IMPLEMENTED
;
}
static
inline
NTSTATUS
fast_wait_addr
(
const
void
*
addr
,
const
void
*
cmp
,
SIZE_T
size
,
const
LARGE_INTEGER
*
timeout
)
{
return
STATUS_NOT_IMPLEMENTED
;
}
static
inline
NTSTATUS
fast_wake_addr
(
const
void
*
addr
)
{
return
STATUS_NOT_IMPLEMENTED
;
}
#endif
/***********************************************************************
* RtlWaitOnAddress (NTDLL.@)
*/
NTSTATUS
WINAPI
RtlWaitOnAddress
(
const
void
*
addr
,
const
void
*
cmp
,
SIZE_T
size
,
const
LARGE_INTEGER
*
timeout
)
{
select_op_t
select_op
;
NTSTATUS
ret
;
timeout_t
abs_timeout
=
timeout
?
timeout
->
QuadPart
:
TIMEOUT_INFINITE
;
if
(
size
!=
1
&&
size
!=
2
&&
size
!=
4
&&
size
!=
8
)
return
STATUS_INVALID_PARAMETER
;
if
((
ret
=
fast_wait_addr
(
addr
,
cmp
,
size
,
timeout
))
!=
STATUS_NOT_IMPLEMENTED
)
return
ret
;
mutex_lock
(
&
addr_mutex
);
if
(
!
compare_addr
(
addr
,
cmp
,
size
))
{
mutex_unlock
(
&
addr_mutex
);
return
STATUS_SUCCESS
;
}
if
(
abs_timeout
<
0
)
{
LARGE_INTEGER
now
;
NtQueryPerformanceCounter
(
&
now
,
NULL
);
abs_timeout
-=
now
.
QuadPart
;
}
select_op
.
keyed_event
.
op
=
SELECT_KEYED_EVENT_WAIT
;
select_op
.
keyed_event
.
handle
=
wine_server_obj_handle
(
keyed_event
);
select_op
.
keyed_event
.
key
=
wine_server_client_ptr
(
addr
);
return
server_select
(
&
select_op
,
sizeof
(
select_op
.
keyed_event
),
SELECT_INTERRUPTIBLE
,
abs_timeout
,
NULL
,
&
addr_mutex
,
NULL
);
}
/***********************************************************************
* RtlWakeAddressAll (NTDLL.@)
*/
void
WINAPI
RtlWakeAddressAll
(
const
void
*
addr
)
{
if
(
fast_wake_addr
(
addr
)
!=
STATUS_NOT_IMPLEMENTED
)
return
;
mutex_lock
(
&
addr_mutex
);
while
(
NtReleaseKeyedEvent
(
0
,
addr
,
0
,
&
zero_timeout
)
==
STATUS_SUCCESS
)
{}
mutex_unlock
(
&
addr_mutex
);
}
/***********************************************************************
* RtlWakeAddressSingle (NTDLL.@)
*/
void
WINAPI
RtlWakeAddressSingle
(
const
void
*
addr
)
{
if
(
fast_wake_addr
(
addr
)
!=
STATUS_NOT_IMPLEMENTED
)
return
;
mutex_lock
(
&
addr_mutex
);
NtReleaseKeyedEvent
(
0
,
addr
,
0
,
&
zero_timeout
);
mutex_unlock
(
&
addr_mutex
);
}
dlls/ntdll/unixlib.h
View file @
6bdb914a
...
...
@@ -26,7 +26,7 @@
struct
_DISPATCHER_CONTEXT
;
/* increment this when you change the function table */
#define NTDLL_UNIXLIB_VERSION 12
8
#define NTDLL_UNIXLIB_VERSION 12
9
struct
unix_funcs
{
...
...
@@ -37,10 +37,6 @@ struct unix_funcs
/* other Win32 API functions */
LONGLONG
(
WINAPI
*
RtlGetSystemTimePrecise
)(
void
);
NTSTATUS
(
WINAPI
*
RtlWaitOnAddress
)(
const
void
*
addr
,
const
void
*
cmp
,
SIZE_T
size
,
const
LARGE_INTEGER
*
timeout
);
void
(
WINAPI
*
RtlWakeAddressAll
)(
const
void
*
addr
);
void
(
WINAPI
*
RtlWakeAddressSingle
)(
const
void
*
addr
);
/* fast locks */
NTSTATUS
(
CDECL
*
fast_RtlpWaitForCriticalSection
)(
RTL_CRITICAL_SECTION
*
crit
,
int
timeout
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment