winehq.org
Sign In
Sign Up
Sign In
Sign Up
Manage this list
×
Keyboard Shortcuts
Thread View
j
: Next unread message
k
: Previous unread message
j a
: Jump to all threads
j l
: Jump to MailingList overview
2025
February
January
2024
December
November
October
September
August
July
June
May
April
March
February
January
2023
December
November
October
September
August
July
June
May
April
March
February
January
2022
December
November
October
September
August
July
June
May
April
March
February
January
2021
December
November
October
September
August
July
June
May
April
March
February
January
2020
December
November
October
September
August
July
June
May
April
March
February
January
2019
December
November
October
September
August
July
June
May
April
March
February
January
2018
December
November
October
September
August
July
June
May
April
March
February
January
2017
December
November
October
September
August
July
June
May
April
March
February
January
2016
December
November
October
September
August
July
June
May
April
March
February
January
2015
December
November
October
September
August
July
June
May
April
March
February
January
2014
December
November
October
September
August
July
June
May
April
March
February
January
2013
December
November
October
September
August
July
June
May
April
March
February
January
2012
December
November
October
September
August
July
June
May
April
March
February
January
2011
December
November
October
September
August
July
June
May
April
March
February
January
2010
December
November
October
September
August
July
June
May
April
March
February
January
2009
December
November
October
September
August
July
June
May
April
March
February
January
2008
December
November
October
September
August
July
June
May
April
March
February
January
2007
December
November
October
September
August
July
June
May
April
March
February
January
2006
December
November
October
September
August
July
June
May
April
March
February
January
2005
December
November
October
September
August
July
June
May
April
March
February
January
2004
December
November
October
September
August
July
June
May
April
March
February
January
2003
December
November
October
September
August
July
June
May
April
March
February
January
2002
December
November
October
September
August
July
June
May
April
March
February
January
2001
December
November
October
September
August
July
June
May
April
March
February
List overview
wine-commits
February 2022
----- 2025 -----
February 2025
January 2025
----- 2024 -----
December 2024
November 2024
October 2024
September 2024
August 2024
July 2024
June 2024
May 2024
April 2024
March 2024
February 2024
January 2024
----- 2023 -----
December 2023
November 2023
October 2023
September 2023
August 2023
July 2023
June 2023
May 2023
April 2023
March 2023
February 2023
January 2023
----- 2022 -----
December 2022
November 2022
October 2022
September 2022
August 2022
July 2022
June 2022
May 2022
April 2022
March 2022
February 2022
January 2022
----- 2021 -----
December 2021
November 2021
October 2021
September 2021
August 2021
July 2021
June 2021
May 2021
April 2021
March 2021
February 2021
January 2021
----- 2020 -----
December 2020
November 2020
October 2020
September 2020
August 2020
July 2020
June 2020
May 2020
April 2020
March 2020
February 2020
January 2020
----- 2019 -----
December 2019
November 2019
October 2019
September 2019
August 2019
July 2019
June 2019
May 2019
April 2019
March 2019
February 2019
January 2019
----- 2018 -----
December 2018
November 2018
October 2018
September 2018
August 2018
July 2018
June 2018
May 2018
April 2018
March 2018
February 2018
January 2018
----- 2017 -----
December 2017
November 2017
October 2017
September 2017
August 2017
July 2017
June 2017
May 2017
April 2017
March 2017
February 2017
January 2017
----- 2016 -----
December 2016
November 2016
October 2016
September 2016
August 2016
July 2016
June 2016
May 2016
April 2016
March 2016
February 2016
January 2016
----- 2015 -----
December 2015
November 2015
October 2015
September 2015
August 2015
July 2015
June 2015
May 2015
April 2015
March 2015
February 2015
January 2015
----- 2014 -----
December 2014
November 2014
October 2014
September 2014
August 2014
July 2014
June 2014
May 2014
April 2014
March 2014
February 2014
January 2014
----- 2013 -----
December 2013
November 2013
October 2013
September 2013
August 2013
July 2013
June 2013
May 2013
April 2013
March 2013
February 2013
January 2013
----- 2012 -----
December 2012
November 2012
October 2012
September 2012
August 2012
July 2012
June 2012
May 2012
April 2012
March 2012
February 2012
January 2012
----- 2011 -----
December 2011
November 2011
October 2011
September 2011
August 2011
July 2011
June 2011
May 2011
April 2011
March 2011
February 2011
January 2011
----- 2010 -----
December 2010
November 2010
October 2010
September 2010
August 2010
July 2010
June 2010
May 2010
April 2010
March 2010
February 2010
January 2010
----- 2009 -----
December 2009
November 2009
October 2009
September 2009
August 2009
July 2009
June 2009
May 2009
April 2009
March 2009
February 2009
January 2009
----- 2008 -----
December 2008
November 2008
October 2008
September 2008
August 2008
July 2008
June 2008
May 2008
April 2008
March 2008
February 2008
January 2008
----- 2007 -----
December 2007
November 2007
October 2007
September 2007
August 2007
July 2007
June 2007
May 2007
April 2007
March 2007
February 2007
January 2007
----- 2006 -----
December 2006
November 2006
October 2006
September 2006
August 2006
July 2006
June 2006
May 2006
April 2006
March 2006
February 2006
January 2006
----- 2005 -----
December 2005
November 2005
October 2005
September 2005
August 2005
July 2005
June 2005
May 2005
April 2005
March 2005
February 2005
January 2005
----- 2004 -----
December 2004
November 2004
October 2004
September 2004
August 2004
July 2004
June 2004
May 2004
April 2004
March 2004
February 2004
January 2004
----- 2003 -----
December 2003
November 2003
October 2003
September 2003
August 2003
July 2003
June 2003
May 2003
April 2003
March 2003
February 2003
January 2003
----- 2002 -----
December 2002
November 2002
October 2002
September 2002
August 2002
July 2002
June 2002
May 2002
April 2002
March 2002
February 2002
January 2002
----- 2001 -----
December 2001
November 2001
October 2001
September 2001
August 2001
July 2001
June 2001
May 2001
April 2001
March 2001
February 2001
wine-commits@winehq.org
2 participants
1495 discussions
Start a n
N
ew thread
Jinoh Kang : server: Replace redundant recv_socket status fields with force_async boolean field.
by Alexandre Julliard
10 Feb '22
10 Feb '22
Module: wine Branch: master Commit: dea1499ac0129edafa36cc3af7c1e76068d3696a URL:
https://source.winehq.org/git/wine.git/?a=commit;h=dea1499ac0129edafa36cc3a…
Author: Jinoh Kang <jinoh.kang.kr(a)gmail.com> Date: Thu Feb 10 01:18:11 2022 +0900 server: Replace redundant recv_socket status fields with force_async boolean field. The 'status' field of recv_socket_request is always either STATUS_PENDING or STATUS_DEVICE_NOT_READY, and the 'total' field is always zero. Replace the 'status' field with 'force_async' boolean field, and get rid of the 'total' field entirely. Also, clean up the recv_socket handler code a bit. Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com> Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com> Signed-off-by: Alexandre Julliard <julliard(a)winehq.org> --- dlls/ntdll/unix/socket.c | 6 +----- include/wine/server_protocol.h | 6 +++--- server/protocol.def | 3 +-- server/request.h | 3 +-- server/sock.c | 40 ++++++++++++---------------------------- server/trace.c | 3 +-- 6 files changed, 19 insertions(+), 42 deletions(-) diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 5e3c724f807..23059e3cff8 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -737,13 +737,9 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi } } - status = force_async ? STATUS_PENDING : STATUS_DEVICE_NOT_READY; - information = 0; - SERVER_START_REQ( recv_socket ) { - req->status = status; - req->total = information; + req->force_async = force_async; req->async = server_async( handle, &async->io, event, apc, apc_user, iosb_client_ptr(io) ); req->oob = !!(unix_flags & MSG_OOB); status = wine_server_call( req ); diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index be63b290353..e32ef8e1479 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -1764,8 +1764,8 @@ struct recv_socket_request struct request_header __header; int oob; async_data_t async; - unsigned int status; - unsigned int total; + int force_async; + char __pad_60[4]; }; struct recv_socket_reply { @@ -6284,7 +6284,7 @@ union generic_reply /* ### protocol_version begin ### */ -#define SERVER_PROTOCOL_VERSION 745 +#define SERVER_PROTOCOL_VERSION 746 /* ### protocol_version end ### */ diff --git a/server/protocol.def b/server/protocol.def index 5c0f9a86822..9d90544fa41 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1451,8 +1451,7 @@ enum server_fd_type @REQ(recv_socket) int oob; /* are we receiving OOB data? */ async_data_t async; /* async I/O parameters */ - unsigned int status; /* status of initial call */ - unsigned int total; /* number of bytes already read */ + int force_async; /* Force asynchronous mode? */ @REPLY obj_handle_t wait; /* handle to wait on for blocking recv */ unsigned int options; /* device open options */ diff --git a/server/request.h b/server/request.h index 98e23bfd4ba..2c7889bd197 100644 --- a/server/request.h +++ b/server/request.h @@ -1044,8 +1044,7 @@ C_ASSERT( FIELD_OFFSET(struct unlock_file_request, count) == 24 ); C_ASSERT( sizeof(struct unlock_file_request) == 32 ); C_ASSERT( FIELD_OFFSET(struct recv_socket_request, oob) == 12 ); C_ASSERT( FIELD_OFFSET(struct recv_socket_request, async) == 16 ); -C_ASSERT( FIELD_OFFSET(struct recv_socket_request, status) == 56 ); -C_ASSERT( FIELD_OFFSET(struct recv_socket_request, total) == 60 ); +C_ASSERT( FIELD_OFFSET(struct recv_socket_request, force_async) == 56 ); C_ASSERT( sizeof(struct recv_socket_request) == 64 ); C_ASSERT( FIELD_OFFSET(struct recv_socket_reply, wait) == 8 ); C_ASSERT( FIELD_OFFSET(struct recv_socket_reply, options) == 12 ); diff --git a/server/sock.c b/server/sock.c index 38aaaac9e39..91f98556552 100644 --- a/server/sock.c +++ b/server/sock.c @@ -3396,7 +3396,7 @@ struct object *create_socket_device( struct object *root, const struct unicode_s DECL_HANDLER(recv_socket) { struct sock *sock = (struct sock *)get_handle_obj( current->process, req->async.handle, 0, &sock_ops ); - unsigned int status = req->status; + unsigned int status = STATUS_PENDING; timeout_t timeout = 0; struct async *async; struct fd *fd; @@ -3404,29 +3404,16 @@ DECL_HANDLER(recv_socket) if (!sock) return; fd = sock->fd; - /* recv() returned EWOULDBLOCK, i.e. no data available yet */ - if (status == STATUS_DEVICE_NOT_READY && !sock->nonblocking) - { - /* Set a timeout on the async if necessary. - * - * We want to do this *only* if the client gave us STATUS_DEVICE_NOT_READY. - * If the client gave us STATUS_PENDING, it expects the async to always - * block (it was triggered by WSARecv*() with a valid OVERLAPPED - * structure) and for the timeout not to be respected. */ - if (is_fd_overlapped( fd )) - timeout = (timeout_t)sock->rcvtimeo * -10000; - - status = STATUS_PENDING; - } - - if ((status == STATUS_PENDING || status == STATUS_DEVICE_NOT_READY) && sock->rd_shutdown) - status = STATUS_PIPE_DISCONNECTED; + if (!req->force_async && !sock->nonblocking && is_fd_overlapped( fd )) + timeout = (timeout_t)sock->rcvtimeo * -10000; - /* NOTE: If read_q is not empty, we cannot really tell if the already queued asyncs - * NOTE: will not consume all available data; if there's no data available, - * NOTE: the current request won't be immediately satiable. */ - if ((status == STATUS_PENDING || status == STATUS_DEVICE_NOT_READY) && !async_queued( &sock->read_q )) + if (sock->rd_shutdown) status = STATUS_PIPE_DISCONNECTED; + else if (!async_queued( &sock->read_q )) { + /* If read_q is not empty, we cannot really tell if the already queued + * asyncs will not consume all available data; if there's no data + * available, the current request won't be immediately satiable. + */ struct pollfd pollfd; pollfd.fd = get_unix_fd( sock->fd ); pollfd.events = req->oob ? POLLPRI : POLLIN; @@ -3440,17 +3427,14 @@ DECL_HANDLER(recv_socket) } } + if (status == STATUS_PENDING && !req->force_async && sock->nonblocking) + status = STATUS_DEVICE_NOT_READY; + sock->pending_events &= ~(req->oob ? AFD_POLL_OOB : AFD_POLL_READ); sock->reported_events &= ~(req->oob ? AFD_POLL_OOB : AFD_POLL_READ); if ((async = create_request_async( fd, get_fd_comp_flags( fd ), &req->async ))) { - if (status == STATUS_SUCCESS) - { - struct iosb *iosb = async_get_iosb( async ); - iosb->result = req->total; - release_object( iosb ); - } set_error( status ); if (timeout) diff --git a/server/trace.c b/server/trace.c index 1fb39d4f997..61e592b0ffa 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2032,8 +2032,7 @@ static void dump_recv_socket_request( const struct recv_socket_request *req ) { fprintf( stderr, " oob=%d", req->oob ); dump_async_data( ", async=", &req->async ); - fprintf( stderr, ", status=%08x", req->status ); - fprintf( stderr, ", total=%08x", req->total ); + fprintf( stderr, ", force_async=%d", req->force_async ); } static void dump_recv_socket_reply( const struct recv_socket_reply *req )
1
0
0
0
Jinoh Kang : ntdll: Don't call try_recv before server call in sock_recv.
by Alexandre Julliard
10 Feb '22
10 Feb '22
Module: wine Branch: master Commit: 160eb74fdf2d707b381734c064a17ff6f0ba28b6 URL:
https://source.winehq.org/git/wine.git/?a=commit;h=160eb74fdf2d707b381734c0…
Author: Jinoh Kang <jinoh.kang.kr(a)gmail.com> Date: Thu Feb 10 01:18:05 2022 +0900 ntdll: Don't call try_recv before server call in sock_recv. Otherwise, try_recv() call from sock_recv() may race against try_recv() call from async_recv_proc(), shuffling the packet order. Wine-Bug:
https://bugs.winehq.org/show_bug.cgi?id=52401
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com> Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com> Signed-off-by: Alexandre Julliard <julliard(a)winehq.org> --- dlls/ntdll/unix/socket.c | 12 ++---------- dlls/ws2_32/tests/sock.c | 8 ++++---- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 03a9a93d04a..5e3c724f807 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -737,16 +737,8 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi } } - status = try_recv( fd, async, &information ); - - if (status != STATUS_SUCCESS && status != STATUS_BUFFER_OVERFLOW && status != STATUS_DEVICE_NOT_READY) - { - release_fileio( &async->io ); - return status; - } - - if (status == STATUS_DEVICE_NOT_READY && force_async) - status = STATUS_PENDING; + status = force_async ? STATUS_PENDING : STATUS_DEVICE_NOT_READY; + information = 0; SERVER_START_REQ( recv_socket ) { diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index d208c17e43f..6b6267dc153 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -7813,8 +7813,8 @@ static void test_shutdown(void) WSASetLastError(0xdeadbeef); ret = recv(server, buffer, sizeof(buffer), 0); - todo_wine ok(ret == -1, "got %d\n", ret); - todo_wine ok(WSAGetLastError() == WSAESHUTDOWN, "got error %u\n", WSAGetLastError()); + ok(ret == -1, "got %d\n", ret); + ok(WSAGetLastError() == WSAESHUTDOWN, "got error %u\n", WSAGetLastError()); ret = send(server, "test", 5, 0); ok(ret == 5, "got %d\n", ret); @@ -7908,8 +7908,8 @@ static void test_shutdown(void) WSASetLastError(0xdeadbeef); ret = recv(server, buffer, sizeof(buffer), 0); - todo_wine ok(ret == -1, "got %d\n", ret); - todo_wine ok(WSAGetLastError() == WSAESHUTDOWN, "got error %u\n", WSAGetLastError()); + ok(ret == -1, "got %d\n", ret); + ok(WSAGetLastError() == WSAESHUTDOWN, "got error %u\n", WSAGetLastError()); WSASetLastError(0xdeadbeef); ret = send(server, "test", 5, 0);
1
0
0
0
Jinoh Kang : server: Attempt to complete I/O request immediately in recv_socket.
by Alexandre Julliard
10 Feb '22
10 Feb '22
Module: wine Branch: master Commit: e5ce4fa917a0e679e5a9444a5e277b141918fb29 URL:
https://source.winehq.org/git/wine.git/?a=commit;h=e5ce4fa917a0e679e5a9444a…
Author: Jinoh Kang <jinoh.kang.kr(a)gmail.com> Date: Thu Feb 10 01:17:59 2022 +0900 server: Attempt to complete I/O request immediately in recv_socket. Make recv_socket alert the async immediately if poll() call detects that there are incoming data in the socket, bypassing the wineserver's main polling loop. Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com> Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com> Signed-off-by: Alexandre Julliard <julliard(a)winehq.org> --- dlls/ntdll/unix/socket.c | 22 ++++++++++++++++++---- dlls/ntdll/unix/sync.c | 26 ++++++++++++++++++++++++++ dlls/ntdll/unix/unix_private.h | 1 + include/wine/server_protocol.h | 4 +++- server/protocol.def | 1 + server/request.h | 3 ++- server/sock.c | 21 ++++++++++++++++++++- server/trace.c | 1 + 8 files changed, 72 insertions(+), 7 deletions(-) diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 71dfcdd1114..03a9a93d04a 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -686,6 +686,7 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi NTSTATUS status; unsigned int i; ULONG options; + BOOL nonblocking, alerted; if (unix_flags & MSG_OOB) { @@ -756,16 +757,29 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi status = wine_server_call( req ); wait_handle = wine_server_ptr_handle( reply->wait ); options = reply->options; - if ((!NT_ERROR(status) || wait_handle) && status != STATUS_PENDING) + nonblocking = reply->nonblocking; + } + SERVER_END_REQ; + + alerted = status == STATUS_ALERTED; + if (alerted) + { + status = try_recv( fd, async, &information ); + if (status == STATUS_DEVICE_NOT_READY && (force_async || !nonblocking)) + status = STATUS_PENDING; + } + + if (status != STATUS_PENDING) + { + if (!NT_ERROR(status) || (wait_handle && !alerted)) { io->Status = status; io->Information = information; } + release_fileio( &async->io ); } - SERVER_END_REQ; - - if (status != STATUS_PENDING) release_fileio( &async->io ); + if (alerted) set_async_direct_result( &wait_handle, status, information ); if (wait_handle) status = wait_async( wait_handle, options & FILE_SYNCHRONOUS_IO_ALERT ); return status; } diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index 442243d8bcf..ee25dfd0099 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -2510,3 +2510,29 @@ NTSTATUS WINAPI NtWaitForAlertByThreadId( const void *address, const LARGE_INTEG } #endif + +/* Notify direct completion of async and close the wait handle if it is no longer needed. + * This function is a no-op (returns status as-is) if the supplied handle is NULL. + */ +void set_async_direct_result( HANDLE *optional_handle, NTSTATUS status, ULONG_PTR information ) +{ + NTSTATUS ret; + + if (!*optional_handle) return; + + SERVER_START_REQ( set_async_direct_result ) + { + req->handle = wine_server_obj_handle( *optional_handle ); + req->status = status; + req->information = information; + ret = wine_server_call( req ); + if (ret == STATUS_SUCCESS) + *optional_handle = wine_server_ptr_handle( reply->handle ); + } + SERVER_END_REQ; + + if (ret != STATUS_SUCCESS) + ERR( "cannot report I/O result back to server: %08x\n", ret ); + + return; +} diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index a79edabc37c..75f03706401 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -274,6 +274,7 @@ extern NTSTATUS get_device_info( int fd, struct _FILE_FS_DEVICE_INFORMATION *inf extern void init_files(void) DECLSPEC_HIDDEN; extern void init_cpu_info(void) DECLSPEC_HIDDEN; extern void add_completion( HANDLE handle, ULONG_PTR value, NTSTATUS status, ULONG info, BOOL async ) DECLSPEC_HIDDEN; +extern void set_async_direct_result( HANDLE *optional_handle, NTSTATUS status, ULONG_PTR information ); extern void dbg_init(void) DECLSPEC_HIDDEN; diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 5ed95a41c19..be63b290353 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -1772,6 +1772,8 @@ struct recv_socket_reply struct reply_header __header; obj_handle_t wait; unsigned int options; + int nonblocking; + char __pad_20[4]; }; @@ -6282,7 +6284,7 @@ union generic_reply /* ### protocol_version begin ### */ -#define SERVER_PROTOCOL_VERSION 744 +#define SERVER_PROTOCOL_VERSION 745 /* ### protocol_version end ### */ diff --git a/server/protocol.def b/server/protocol.def index ce05e9a38c6..5c0f9a86822 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1456,6 +1456,7 @@ enum server_fd_type @REPLY obj_handle_t wait; /* handle to wait on for blocking recv */ unsigned int options; /* device open options */ + int nonblocking; /* is socket non-blocking? */ @END diff --git a/server/request.h b/server/request.h index 128b70fe3cf..98e23bfd4ba 100644 --- a/server/request.h +++ b/server/request.h @@ -1049,7 +1049,8 @@ C_ASSERT( FIELD_OFFSET(struct recv_socket_request, total) == 60 ); C_ASSERT( sizeof(struct recv_socket_request) == 64 ); C_ASSERT( FIELD_OFFSET(struct recv_socket_reply, wait) == 8 ); C_ASSERT( FIELD_OFFSET(struct recv_socket_reply, options) == 12 ); -C_ASSERT( sizeof(struct recv_socket_reply) == 16 ); +C_ASSERT( FIELD_OFFSET(struct recv_socket_reply, nonblocking) == 16 ); +C_ASSERT( sizeof(struct recv_socket_reply) == 24 ); C_ASSERT( FIELD_OFFSET(struct send_socket_request, async) == 16 ); C_ASSERT( FIELD_OFFSET(struct send_socket_request, status) == 56 ); C_ASSERT( FIELD_OFFSET(struct send_socket_request, total) == 60 ); diff --git a/server/sock.c b/server/sock.c index 512b7c0f78e..38aaaac9e39 100644 --- a/server/sock.c +++ b/server/sock.c @@ -3422,6 +3422,24 @@ DECL_HANDLER(recv_socket) if ((status == STATUS_PENDING || status == STATUS_DEVICE_NOT_READY) && sock->rd_shutdown) status = STATUS_PIPE_DISCONNECTED; + /* NOTE: If read_q is not empty, we cannot really tell if the already queued asyncs + * NOTE: will not consume all available data; if there's no data available, + * NOTE: the current request won't be immediately satiable. */ + if ((status == STATUS_PENDING || status == STATUS_DEVICE_NOT_READY) && !async_queued( &sock->read_q )) + { + struct pollfd pollfd; + pollfd.fd = get_unix_fd( sock->fd ); + pollfd.events = req->oob ? POLLPRI : POLLIN; + pollfd.revents = 0; + if (poll(&pollfd, 1, 0) >= 0 && pollfd.revents) + { + /* Give the client opportunity to complete synchronously. + * If it turns out that the I/O request is not actually immediately satiable, + * the client may then choose to re-queue the async (with STATUS_PENDING). */ + status = STATUS_ALERTED; + } + } + sock->pending_events &= ~(req->oob ? AFD_POLL_OOB : AFD_POLL_READ); sock->reported_events &= ~(req->oob ? AFD_POLL_OOB : AFD_POLL_READ); @@ -3438,7 +3456,7 @@ DECL_HANDLER(recv_socket) if (timeout) async_set_timeout( async, timeout, STATUS_IO_TIMEOUT ); - if (status == STATUS_PENDING) + if (status == STATUS_PENDING || status == STATUS_ALERTED) queue_async( &sock->read_q, async ); /* always reselect; we changed reported_events above */ @@ -3446,6 +3464,7 @@ DECL_HANDLER(recv_socket) reply->wait = async_handoff( async, NULL, 0 ); reply->options = get_fd_options( fd ); + reply->nonblocking = sock->nonblocking; release_object( async ); } release_object( sock ); diff --git a/server/trace.c b/server/trace.c index 6b50b9d0d18..1fb39d4f997 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2040,6 +2040,7 @@ static void dump_recv_socket_reply( const struct recv_socket_reply *req ) { fprintf( stderr, " wait=%04x", req->wait ); fprintf( stderr, ", options=%08x", req->options ); + fprintf( stderr, ", nonblocking=%d", req->nonblocking ); } static void dump_send_socket_request( const struct send_socket_request *req )
1
0
0
0
Jinoh Kang : server: Allow calling async_handoff() with status code STATUS_ALERTED.
by Alexandre Julliard
10 Feb '22
10 Feb '22
Module: wine Branch: master Commit: 15483b1a126902a8c7b7b59800a376bc473e2ed3 URL:
https://source.winehq.org/git/wine.git/?a=commit;h=15483b1a126902a8c7b7b598…
Author: Jinoh Kang <jinoh.kang.kr(a)gmail.com> Date: Thu Feb 10 01:17:53 2022 +0900 server: Allow calling async_handoff() with status code STATUS_ALERTED. If the server detects that an I/O request could be completed immediately (e.g. the socket to read from already has incoming data), it can now return STATUS_ALERTED to allow opportunistic synchronous I/O. The Unix side will then attempt to perform I/O in nonblocking mode and report back the I/O status to the server via the new server request "set_async_direct_result". If the operation returns e.g. EAGAIN or EWOULDBLOCK, the client can opt to either abandon the request (by specifying an error status) or poll for it in the server as usual (by waiting on the wait handle). Without such mechanism in place, the client cannot safely perform immediately satiable I/O operations synchronously, since it can potentially conflict with other pending I/O operations that have already been queued. Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com> Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com> Signed-off-by: Alexandre Julliard <julliard(a)winehq.org> --- include/wine/server_protocol.h | 22 ++++++++++++- server/async.c | 72 ++++++++++++++++++++++++++++++++++++++++++ server/protocol.def | 10 ++++++ server/request.h | 8 +++++ server/trace.c | 15 +++++++++ 5 files changed, 126 insertions(+), 1 deletion(-) diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 97ab4e25834..5ed95a41c19 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -2905,6 +2905,23 @@ struct get_async_result_reply +struct set_async_direct_result_request +{ + struct request_header __header; + obj_handle_t handle; + apc_param_t information; + unsigned int status; + char __pad_28[4]; +}; +struct set_async_direct_result_reply +{ + struct reply_header __header; + obj_handle_t handle; + char __pad_12[4]; +}; + + + struct read_request { struct request_header __header; @@ -5547,6 +5564,7 @@ enum request REQ_register_async, REQ_cancel_async, REQ_get_async_result, + REQ_set_async_direct_result, REQ_read, REQ_write, REQ_ioctl, @@ -5828,6 +5846,7 @@ union generic_request struct register_async_request register_async_request; struct cancel_async_request cancel_async_request; struct get_async_result_request get_async_result_request; + struct set_async_direct_result_request set_async_direct_result_request; struct read_request read_request; struct write_request write_request; struct ioctl_request ioctl_request; @@ -6107,6 +6126,7 @@ union generic_reply struct register_async_reply register_async_reply; struct cancel_async_reply cancel_async_reply; struct get_async_result_reply get_async_result_reply; + struct set_async_direct_result_reply set_async_direct_result_reply; struct read_reply read_reply; struct write_reply write_reply; struct ioctl_reply ioctl_reply; @@ -6262,7 +6282,7 @@ union generic_reply /* ### protocol_version begin ### */ -#define SERVER_PROTOCOL_VERSION 743 +#define SERVER_PROTOCOL_VERSION 744 /* ### protocol_version end ### */ diff --git a/server/async.c b/server/async.c index 7aef28355f0..7d0ff10f7a4 100644 --- a/server/async.c +++ b/server/async.c @@ -338,6 +338,31 @@ obj_handle_t async_handoff( struct async *async, data_size_t *result, int force_ return async->wait_handle; } + if (get_error() == STATUS_ALERTED) + { + /* give the client opportunity to complete synchronously. after the + * client performs the I/O, it reports the result back to the server + * via the set_async_direct_result request. if it turns out that the + * I/O request is not actually immediately satiable, the client may + * then choose to re-queue the async by reporting STATUS_PENDING + * instead. + * + * since we're deferring the initial I/O (to the client), we mark the + * async as having unknown initial status (unknown_status = 1). note + * that we don't reuse async_set_unknown_status() here. this is because + * the one responsible for performing the I/O is not the device driver, + * but instead the client that requested the I/O in the first place. + * + * also, async_set_unknown_status() would set direct_result to zero + * forcing APC_ASYNC_IO to fire in async_terminate(), which is not + * useful due to subtle semantic differences between synchronous and + * asynchronous completion. + */ + async->unknown_status = 1; + async_terminate( async, STATUS_ALERTED ); + return async->wait_handle; + } + async->initial_status = get_error(); if (!async->pending && NT_ERROR( get_error() )) @@ -728,3 +753,50 @@ DECL_HANDLER(get_async_result) } set_error( iosb->status ); } + +/* notify direct completion of async and close the wait handle if not blocking */ +DECL_HANDLER(set_async_direct_result) +{ + struct async *async = (struct async *)get_handle_obj( current->process, req->handle, 0, &async_ops ); + unsigned int status = req->status; + + if (!async) return; + + if (!async->unknown_status || !async->terminated || !async->alerted) + { + set_error( STATUS_INVALID_PARAMETER ); + release_object( &async->obj ); + return; + } + + async_set_initial_status( async, status ); + + if (status == STATUS_PENDING) + { + async->direct_result = 0; + async->pending = 1; + } + + /* if the I/O has completed successfully, the client would have already + * set the IOSB. therefore, we can skip waiting on wait_handle and do + * async_set_result() directly. + */ + async_set_result( &async->obj, status, req->information ); + + /* close wait handle here to avoid extra server round trip, if the I/O + * either has completed, or is pending and not blocking. + */ + if (status != STATUS_PENDING || !async->blocking) + { + close_handle( async->thread->process, async->wait_handle ); + async->wait_handle = 0; + } + + /* report back to the client whether the wait handle has been closed. + * handle will be 0 if closed by us; otherwise the original value is + * retained + */ + reply->handle = async->wait_handle; + + release_object( &async->obj ); +} diff --git a/server/protocol.def b/server/protocol.def index 21b17c11551..ce05e9a38c6 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2163,6 +2163,16 @@ enum message_type @END +/* Notify direct completion of async and close the wait handle if not blocking */ +@REQ(set_async_direct_result) + obj_handle_t handle; /* wait handle */ + apc_param_t information; /* IO_STATUS_BLOCK Information */ + unsigned int status; /* completion status */ +@REPLY + obj_handle_t handle; /* wait handle, or NULL if closed */ +@END + + /* Perform a read on a file object */ @REQ(read) async_data_t async; /* async I/O parameters */ diff --git a/server/request.h b/server/request.h index f2d0e827d94..128b70fe3cf 100644 --- a/server/request.h +++ b/server/request.h @@ -242,6 +242,7 @@ DECL_HANDLER(set_serial_info); DECL_HANDLER(register_async); DECL_HANDLER(cancel_async); DECL_HANDLER(get_async_result); +DECL_HANDLER(set_async_direct_result); DECL_HANDLER(read); DECL_HANDLER(write); DECL_HANDLER(ioctl); @@ -522,6 +523,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_register_async, (req_handler)req_cancel_async, (req_handler)req_get_async_result, + (req_handler)req_set_async_direct_result, (req_handler)req_read, (req_handler)req_write, (req_handler)req_ioctl, @@ -1396,6 +1398,12 @@ C_ASSERT( sizeof(struct cancel_async_request) == 32 ); C_ASSERT( FIELD_OFFSET(struct get_async_result_request, user_arg) == 16 ); C_ASSERT( sizeof(struct get_async_result_request) == 24 ); C_ASSERT( sizeof(struct get_async_result_reply) == 8 ); +C_ASSERT( FIELD_OFFSET(struct set_async_direct_result_request, handle) == 12 ); +C_ASSERT( FIELD_OFFSET(struct set_async_direct_result_request, information) == 16 ); +C_ASSERT( FIELD_OFFSET(struct set_async_direct_result_request, status) == 24 ); +C_ASSERT( sizeof(struct set_async_direct_result_request) == 32 ); +C_ASSERT( FIELD_OFFSET(struct set_async_direct_result_reply, handle) == 8 ); +C_ASSERT( sizeof(struct set_async_direct_result_reply) == 16 ); C_ASSERT( FIELD_OFFSET(struct read_request, async) == 16 ); C_ASSERT( FIELD_OFFSET(struct read_request, pos) == 56 ); C_ASSERT( sizeof(struct read_request) == 64 ); diff --git a/server/trace.c b/server/trace.c index 32606d540f7..6b50b9d0d18 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2758,6 +2758,18 @@ static void dump_get_async_result_reply( const struct get_async_result_reply *re dump_varargs_bytes( " out_data=", cur_size ); } +static void dump_set_async_direct_result_request( const struct set_async_direct_result_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); + dump_uint64( ", information=", &req->information ); + fprintf( stderr, ", status=%08x", req->status ); +} + +static void dump_set_async_direct_result_reply( const struct set_async_direct_result_reply *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); +} + static void dump_read_request( const struct read_request *req ) { dump_async_data( " async=", &req->async ); @@ -4595,6 +4607,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_register_async_request, (dump_func)dump_cancel_async_request, (dump_func)dump_get_async_result_request, + (dump_func)dump_set_async_direct_result_request, (dump_func)dump_read_request, (dump_func)dump_write_request, (dump_func)dump_ioctl_request, @@ -4872,6 +4885,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { NULL, NULL, (dump_func)dump_get_async_result_reply, + (dump_func)dump_set_async_direct_result_reply, (dump_func)dump_read_reply, (dump_func)dump_write_reply, (dump_func)dump_ioctl_reply, @@ -5149,6 +5163,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "register_async", "cancel_async", "get_async_result", + "set_async_direct_result", "read", "write", "ioctl",
1
0
0
0
Rémi Bernon : mf/tests: Add some WMA decoder ProcessOutput tests.
by Alexandre Julliard
10 Feb '22
10 Feb '22
Module: wine Branch: master Commit: 6ed3af99b5526e32361f958f48e11fbee401f30e URL:
https://source.winehq.org/git/wine.git/?a=commit;h=6ed3af99b5526e32361f958f…
Author: Rémi Bernon <rbernon(a)codeweavers.com> Date: Wed Feb 9 11:18:04 2022 +0100 mf/tests: Add some WMA decoder ProcessOutput tests. Wine-Bug:
https://bugs.winehq.org/show_bug.cgi?id=51931
Wine-Bug:
https://bugs.winehq.org/show_bug.cgi?id=52391
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com> Signed-off-by: Alexandre Julliard <julliard(a)winehq.org> --- dlls/mf/tests/mf.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 09bc4ef1c1e..b9e2a3b940f 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -5815,6 +5815,7 @@ static void test_wma_decoder(void) MFT_REGISTER_TYPE_INFO output_type = {MFMediaType_Audio, MFAudioFormat_Float}; MFT_OUTPUT_STREAM_INFO output_info; MFT_INPUT_STREAM_INFO input_info; + MFT_OUTPUT_DATA_BUFFER output; const BYTE *wma_encoded_data; ULONG wma_encoded_data_len; IMFMediaType *media_type; @@ -5822,6 +5823,7 @@ static void test_wma_decoder(void) IMFSample *sample; HRSRC resource; GUID class_id; + DWORD status; ULONG i, ret; HRESULT hr; @@ -6006,8 +6008,145 @@ static void test_wma_decoder(void) todo_wine ok(ret == 1, "Release returned %u\n", ret); + /* As output_info.dwFlags doesn't have MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES + * IMFTransform_ProcessOutput needs a sample or returns MF_E_TRANSFORM_NEED_MORE_INPUT */ + + status = 0xdeadbeef; + memset(&output, 0, sizeof(output)); + hr = IMFTransform_ProcessOutput(transform, 0, 1, &output, &status); + todo_wine + ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#x\n", hr); + ok(output.dwStreamID == 0, "got dwStreamID %u\n", output.dwStreamID); + ok(!output.pSample, "got pSample %p\n", output.pSample); + todo_wine + ok(output.dwStatus == MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE || + broken(output.dwStatus == (MFT_OUTPUT_DATA_BUFFER_INCOMPLETE|MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE)) /* Win7 */, + "got dwStatus %#x\n", output.dwStatus); + ok(!output.pEvents, "got pEvents %p\n", output.pEvents); + todo_wine + ok(status == 0, "got status %#x\n", status); + + sample = create_sample(wma_encoded_data, wma_block_size); + hr = IMFTransform_ProcessInput(transform, 0, sample, 0); + todo_wine + ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#x\n", hr); + ret = IMFSample_Release(sample); + ok(ret == 0, "Release returned %u\n", ret); + + status = 0xdeadbeef; + memset(&output, 0, sizeof(output)); + hr = IMFTransform_ProcessOutput(transform, 0, 1, &output, &status); + todo_wine + ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#x\n", hr); + ok(!output.pSample, "got pSample %p\n", output.pSample); + todo_wine + ok(output.dwStatus == MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE || + broken(output.dwStatus == (MFT_OUTPUT_DATA_BUFFER_INCOMPLETE|MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE)) /* Win7 */, + "got dwStatus %#x\n", output.dwStatus); + todo_wine + ok(status == 0, "got status %#x\n", status); + + i = 1; + status = 0xdeadbeef; + output_info.cbSize = sizeof(wma_decoded_data); + sample = create_sample(NULL, output_info.cbSize); + memset(&output, 0, sizeof(output)); + output.pSample = sample; + hr = IMFTransform_ProcessOutput(transform, 0, 1, &output, &status); + while (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) + { + ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#x\n", hr); + ok(output.pSample == sample, "got pSample %p\n", output.pSample); + ok(output.dwStatus == 0, "got dwStatus %#x\n", output.dwStatus); + ok(status == 0, "got status %#x\n", status); + check_sample(sample, NULL, 0, NULL); + ret = IMFSample_Release(sample); + ok(ret == 0, "Release returned %u\n", ret); + + sample = create_sample(wma_encoded_data + i * wma_block_size, wma_block_size); + hr = IMFTransform_ProcessInput(transform, 0, sample, 0); + ok(hr == S_OK, "ProcessInput returned %#x\n", hr); + ret = IMFSample_Release(sample); + ok(ret == 1, "Release returned %u\n", ret); + i++; + + status = 0xdeadbeef; + sample = create_sample(NULL, output_info.cbSize); + memset(&output, 0, sizeof(output)); + output.pSample = sample; + hr = IMFTransform_ProcessOutput(transform, 0, 1, &output, &status); + } + + todo_wine + ok(hr == S_OK, "ProcessOutput returned %#x\n", hr); + ok(output.pSample == sample, "got pSample %p\n", output.pSample); + + i = 0; + while (hr == S_OK) + { + ok(output.pSample == sample, "got pSample %p\n", output.pSample); + ok(output.dwStatus == MFT_OUTPUT_DATA_BUFFER_INCOMPLETE || output.dwStatus == 0 || + broken(output.dwStatus == (MFT_OUTPUT_DATA_BUFFER_INCOMPLETE|7) || output.dwStatus == 7) /* Win7 */, + "got dwStatus %#x\n", output.dwStatus); + ok(status == 0, "got status %#x\n", status); + if (output.dwStatus == MFT_OUTPUT_DATA_BUFFER_INCOMPLETE || + broken(output.dwStatus == (MFT_OUTPUT_DATA_BUFFER_INCOMPLETE|7))) + { + check_sample(sample, wma_decoded_data, sizeof(wma_decoded_data), NULL); + i += sizeof(wma_decoded_data); + } + else + { + check_sample(sample, wma_decoded_data, sizeof(wma_decoded_data) / 2, NULL); + i += sizeof(wma_decoded_data) / 2; + } + ret = IMFSample_Release(sample); + ok(ret == 0, "Release returned %u\n", ret); + + status = 0xdeadbeef; + sample = create_sample(NULL, output_info.cbSize); + memset(&output, 0, sizeof(output)); + output.pSample = sample; + hr = IMFTransform_ProcessOutput(transform, 0, 1, &output, &status); + } + todo_wine + ok(i == 0xe000, "ProcessOutput produced %#x bytes\n", i); + + todo_wine + ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#x\n", hr); + ok(output.pSample == sample, "got pSample %p\n", output.pSample); + ok(output.dwStatus == 0, "got dwStatus %#x\n", output.dwStatus); + todo_wine + ok(status == 0, "got status %#x\n", status); + ret = IMFSample_Release(sample); + ok(ret == 0, "Release returned %u\n", ret); + + status = 0xdeadbeef; + sample = create_sample(NULL, output_info.cbSize); + memset(&output, 0, sizeof(output)); + output.pSample = sample; + hr = IMFTransform_ProcessOutput(transform, 0, 1, &output, &status); + todo_wine + ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#x\n", hr); + ok(output.pSample == sample, "got pSample %p\n", output.pSample); + ok(output.dwStatus == 0 || + broken(output.dwStatus == (MFT_OUTPUT_DATA_BUFFER_INCOMPLETE|7)) /* Win7 */, + "got dwStatus %#x\n", output.dwStatus); + todo_wine + ok(status == 0, "got status %#x\n", status); + check_sample(sample, NULL, 0, NULL); + ret = IMFSample_Release(sample); + ok(ret == 0, "Release returned %u\n", ret); + + sample = create_sample(wma_encoded_data, wma_block_size); + hr = IMFTransform_ProcessInput(transform, 0, sample, 0); + todo_wine + ok(hr == S_OK, "ProcessInput returned %#x\n", hr); + ret = IMFTransform_Release(transform); ok(ret == 0, "Release returned %u\n", ret); + ret = IMFSample_Release(sample); + ok(ret == 0, "Release returned %u\n", ret); failed: CoUninitialize();
1
0
0
0
Rémi Bernon : mf/tests: Add some WMA decoder ProcessInput tests.
by Alexandre Julliard
10 Feb '22
10 Feb '22
Module: wine Branch: master Commit: a45e4cbf9fc0410b30e8d441dbbac07305212f40 URL:
https://source.winehq.org/git/wine.git/?a=commit;h=a45e4cbf9fc0410b30e8d441…
Author: Rémi Bernon <rbernon(a)codeweavers.com> Date: Wed Feb 9 11:18:03 2022 +0100 mf/tests: Add some WMA decoder ProcessInput tests. Wine-Bug:
https://bugs.winehq.org/show_bug.cgi?id=51931
Wine-Bug:
https://bugs.winehq.org/show_bug.cgi?id=52391
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com> Signed-off-by: Alexandre Julliard <julliard(a)winehq.org> --- dlls/mf/tests/mf.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 229e2e30134..09bc4ef1c1e 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -5815,8 +5815,12 @@ static void test_wma_decoder(void) MFT_REGISTER_TYPE_INFO output_type = {MFMediaType_Audio, MFAudioFormat_Float}; MFT_OUTPUT_STREAM_INFO output_info; MFT_INPUT_STREAM_INFO input_info; + const BYTE *wma_encoded_data; + ULONG wma_encoded_data_len; IMFMediaType *media_type; IMFTransform *transform; + IMFSample *sample; + HRSRC resource; GUID class_id; ULONG i, ret; HRESULT hr; @@ -5972,6 +5976,36 @@ static void test_wma_decoder(void) ok(output_info.cbSize == sizeof(wma_decoded_data), "got cbSize %#x\n", output_info.cbSize); ok(output_info.cbAlignment == 1, "got cbAlignment %#x\n", output_info.cbAlignment); + /* resource is generated using test_wma_encoder output file */ + resource = FindResourceW(NULL, L"wmadata.bin", (const WCHAR *)RT_RCDATA); + ok(resource != 0, "FindResourceW failed, error %u\n", GetLastError()); + wma_encoded_data = LockResource(LoadResource(GetModuleHandleW(NULL), resource)); + wma_encoded_data_len = SizeofResource(GetModuleHandleW(NULL), resource); + ok(wma_encoded_data_len % wma_block_size == 0, "got wma encoded length %u\n", wma_encoded_data_len); + + sample = create_sample(wma_encoded_data, wma_block_size / 2); + hr = IMFTransform_ProcessInput(transform, 0, sample, 0); + todo_wine + ok(hr == S_OK, "ProcessInput returned %#x\n", hr); + ret = IMFSample_Release(sample); + ok(ret == 0, "Release returned %u\n", ret); + sample = create_sample(wma_encoded_data + wma_block_size, wma_block_size - wma_block_size / 2); + hr = IMFTransform_ProcessInput(transform, 0, sample, 0); + todo_wine + ok(hr == S_OK, "ProcessInput returned %#x\n", hr); + ret = IMFSample_Release(sample); + ok(ret == 0, "Release returned %u\n", ret); + sample = create_sample(wma_encoded_data, wma_block_size); + hr = IMFTransform_ProcessInput(transform, 0, sample, 0); + todo_wine + ok(hr == S_OK, "ProcessInput returned %#x\n", hr); + hr = IMFTransform_ProcessInput(transform, 0, sample, 0); + todo_wine + ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#x\n", hr); + ret = IMFSample_Release(sample); + todo_wine + ok(ret == 1, "Release returned %u\n", ret); + ret = IMFTransform_Release(transform); ok(ret == 0, "Release returned %u\n", ret);
1
0
0
0
Rémi Bernon : mf/tests: Add some WMA encoder ProcessOutput tests.
by Alexandre Julliard
10 Feb '22
10 Feb '22
Module: wine Branch: master Commit: c426c72b5c5ce23f16910fb21ca8572bb1f2889a URL:
https://source.winehq.org/git/wine.git/?a=commit;h=c426c72b5c5ce23f16910fb2…
Author: Rémi Bernon <rbernon(a)codeweavers.com> Date: Wed Feb 9 11:18:02 2022 +0100 mf/tests: Add some WMA encoder ProcessOutput tests. Wine-Bug:
https://bugs.winehq.org/show_bug.cgi?id=51931
Wine-Bug:
https://bugs.winehq.org/show_bug.cgi?id=52391
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com> Signed-off-by: Alexandre Julliard <julliard(a)winehq.org> --- dlls/mf/tests/Makefile.in | 2 ++ dlls/mf/tests/mf.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++ dlls/mf/tests/resource.rc | 24 +++++++++++++ dlls/mf/tests/wmadata.bin | Bin 0 -> 49071 bytes 4 files changed, 112 insertions(+) diff --git a/dlls/mf/tests/Makefile.in b/dlls/mf/tests/Makefile.in index da9882fb7db..53592a8758d 100644 --- a/dlls/mf/tests/Makefile.in +++ b/dlls/mf/tests/Makefile.in @@ -4,3 +4,5 @@ IMPORTS = mf mfplat mfuuid ole32 user32 propsys C_SRCS = \ mf.c + +RC_SRCS = resource.rc diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index ebf42bcad9c..229e2e30134 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -5492,6 +5492,32 @@ static IMFSample *create_sample(const BYTE *data, ULONG size) return sample; } +#define check_sample(a, b, c, d) check_sample_(__LINE__, a, b, c, d) +static void check_sample_(int line, IMFSample *sample, const void *expect_buf, ULONG expect_len, HANDLE output_file) +{ + IMFMediaBuffer *media_buffer; + DWORD length; + BYTE *buffer; + HRESULT hr; + ULONG ret; + + hr = IMFSample_ConvertToContiguousBuffer(sample, &media_buffer); + ok_(__FILE__, line)(hr == S_OK, "ConvertToContiguousBuffer returned %#x\n", hr); + hr = IMFMediaBuffer_Lock(media_buffer, &buffer, NULL, &length); + ok_(__FILE__, line)(hr == S_OK, "Lock returned %#x\n", hr); + ok_(__FILE__, line)(expect_len == length, "got length %u\n", length); + if (length && length == expect_len) + { + ok_(__FILE__, line)(!memcmp(expect_buf, buffer, expect_len), + "unexpected buffer data\n"); + } + if (output_file) WriteFile(output_file, buffer, length, &length, NULL); + hr = IMFMediaBuffer_Unlock(media_buffer); + ok_(__FILE__, line)(hr == S_OK, "Unlock returned %#x\n", hr); + ret = IMFMediaBuffer_Release(media_buffer); + ok_(__FILE__, line)(ret == 1, "Release returned %u\n", ret); +} + static const BYTE wma_codec_data[10] = {0, 0x44, 0, 0, 0x17, 0, 0, 0, 0, 0}; static const BYTE wma_decoded_data[0x4000] = {0}; static const ULONG wma_block_size = 1487; @@ -5552,10 +5578,17 @@ static void test_wma_encoder(void) MFT_REGISTER_TYPE_INFO input_type = {MFMediaType_Audio, MFAudioFormat_Float}; MFT_OUTPUT_STREAM_INFO output_info; MFT_INPUT_STREAM_INFO input_info; + MFT_OUTPUT_DATA_BUFFER output; + const BYTE *wma_encoded_data; + WCHAR output_path[MAX_PATH]; + ULONG wma_encoded_data_len; IMFMediaType *media_type; IMFTransform *transform; + HANDLE output_file; IMFSample *sample; + HRSRC resource; GUID class_id; + DWORD status; ULONG i, ret; HRESULT hr; @@ -5612,6 +5645,59 @@ static void test_wma_encoder(void) hr = IMFTransform_ProcessInput(transform, 0, sample, 0); ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#x\n", hr); + status = 0xdeadbeef; + sample = create_sample(NULL, output_info.cbSize); + memset(&output, 0, sizeof(output)); + output.pSample = sample; + + /* check wmadata.bin against current encoder output */ + resource = FindResourceW(NULL, L"wmadata.bin", (const WCHAR *)RT_RCDATA); + ok(resource != 0, "FindResourceW failed, error %u\n", GetLastError()); + wma_encoded_data = LockResource(LoadResource(GetModuleHandleW(NULL), resource)); + wma_encoded_data_len = SizeofResource(GetModuleHandleW(NULL), resource); + ok(wma_encoded_data_len % wma_block_size == 0, "got wma encoded length %u\n", wma_encoded_data_len); + + /* and generate a new one as well in a temporary directory */ + GetTempPathW(ARRAY_SIZE(output_path), output_path); + lstrcatW(output_path, L"wmadata.bin"); + output_file = CreateFileW(output_path, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + ok(output_file != INVALID_HANDLE_VALUE, "CreateFileW failed, error %u\n", GetLastError()); + + i = 0; + while (SUCCEEDED(hr = IMFTransform_ProcessOutput(transform, 0, 1, &output, &status))) + { + winetest_push_context("%u", i); + ok(hr == S_OK, "ProcessOutput returned %#x\n", hr); + ok(output.pSample == sample, "got pSample %p\n", output.pSample); + ok(output.dwStatus == MFT_OUTPUT_DATA_BUFFER_INCOMPLETE || + broken(output.dwStatus == (MFT_OUTPUT_DATA_BUFFER_INCOMPLETE|5)) /* win7 */, + "got dwStatus %#x\n", output.dwStatus); + ok(status == 0, "got status %#x\n", status); + ok(wma_encoded_data_len > i * wma_block_size, "got %u blocks\n", i); + check_sample(sample, wma_encoded_data + i * wma_block_size, wma_block_size, output_file); + winetest_pop_context(); + i++; + } + + trace("created %s\n", debugstr_w(output_path)); + CloseHandle(output_file); + + ret = IMFSample_Release(sample); + ok(ret == 0, "Release returned %u\n", ret); + + status = 0xdeadbeef; + sample = create_sample(NULL, output_info.cbSize); + memset(&output, 0, sizeof(output)); + output.pSample = sample; + hr = IMFTransform_ProcessOutput(transform, 0, 1, &output, &status); + ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#x\n", hr); + ok(output.pSample == sample, "got pSample %p\n", output.pSample); + ok(output.dwStatus == 0, "got dwStatus %#x\n", output.dwStatus); + ok(status == 0, "got status %#x\n", status); + check_sample(sample, NULL, 0, NULL); + ret = IMFSample_Release(sample); + ok(ret == 0, "Release returned %u\n", ret); + ret = IMFTransform_Release(transform); ok(ret == 0, "Release returned %u\n", ret); diff --git a/dlls/mf/tests/resource.rc b/dlls/mf/tests/resource.rc new file mode 100644 index 00000000000..931139c741a --- /dev/null +++ b/dlls/mf/tests/resource.rc @@ -0,0 +1,24 @@ +/* + * Resources for mf test suite. + * + * Copyright 2022 Rémi Bernon for CodeWeavers + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "windef.h" + +/* @makedep: wmadata.bin */ +wmadata.bin RCDATA wmadata.bin diff --git a/dlls/mf/tests/wmadata.bin b/dlls/mf/tests/wmadata.bin new file mode 100644 index 00000000000..6c70c14ce32 Binary files /dev/null and b/dlls/mf/tests/wmadata.bin differ
1
0
0
0
Jacek Caban : server: Store all 64 bits of window id.
by Alexandre Julliard
10 Feb '22
10 Feb '22
Module: wine Branch: master Commit: f139b06651ff259198b3ae765edc81483bf12e8f URL:
https://source.winehq.org/git/wine.git/?a=commit;h=f139b06651ff259198b3ae76…
Author: Jacek Caban <jacek(a)codeweavers.com> Date: Thu Feb 10 15:02:08 2022 +0100 server: Store all 64 bits of window id. Signed-off-by: Jacek Caban <jacek(a)codeweavers.com> Signed-off-by: Alexandre Julliard <julliard(a)winehq.org> --- dlls/user32/win.c | 2 +- include/wine/server_protocol.h | 11 +++++------ server/protocol.def | 7 +++---- server/request.h | 7 +++---- server/trace.c | 7 +++---- server/window.c | 4 ++-- 6 files changed, 17 insertions(+), 21 deletions(-) diff --git a/dlls/user32/win.c b/dlls/user32/win.c index 82975cadd23..70c3521ab1d 100644 --- a/dlls/user32/win.c +++ b/dlls/user32/win.c @@ -2715,7 +2715,7 @@ LONG_PTR WIN_SetWindowLong( HWND hwnd, INT offset, UINT size, LONG_PTR newval, B break; case GWLP_ID: req->flags = SET_WIN_ID; - req->id = newval; + req->extra_value = newval; break; case GWLP_HINSTANCE: req->flags = SET_WIN_INSTANCE; diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 050b682d489..97ab4e25834 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -3112,12 +3112,12 @@ struct set_window_info_request user_handle_t handle; unsigned int style; unsigned int ex_style; - unsigned int id; + data_size_t extra_size; mod_handle_t instance; lparam_t user_data; - int extra_offset; - data_size_t extra_size; lparam_t extra_value; + int extra_offset; + char __pad_60[4]; }; struct set_window_info_reply { @@ -3127,8 +3127,7 @@ struct set_window_info_reply mod_handle_t old_instance; lparam_t old_user_data; lparam_t old_extra_value; - unsigned int old_id; - char __pad_44[4]; + lparam_t old_id; }; #define SET_WIN_STYLE 0x01 #define SET_WIN_EXSTYLE 0x02 @@ -6263,7 +6262,7 @@ union generic_reply /* ### protocol_version begin ### */ -#define SERVER_PROTOCOL_VERSION 742 +#define SERVER_PROTOCOL_VERSION 743 /* ### protocol_version end ### */ diff --git a/server/protocol.def b/server/protocol.def index 02e73047f9b..21b17c11551 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2303,19 +2303,18 @@ enum message_type user_handle_t handle; /* handle to the window */ unsigned int style; /* window style */ unsigned int ex_style; /* window extended style */ - unsigned int id; /* window id */ + data_size_t extra_size; /* size to set in extra bytes */ mod_handle_t instance; /* creator instance */ lparam_t user_data; /* user-specific data */ + lparam_t extra_value; /* value to set in extra bytes or window id */ int extra_offset; /* offset to set in extra bytes */ - data_size_t extra_size; /* size to set in extra bytes */ - lparam_t extra_value; /* value to set in extra bytes */ @REPLY unsigned int old_style; /* old window style */ unsigned int old_ex_style; /* old window extended style */ mod_handle_t old_instance; /* old creator instance */ lparam_t old_user_data; /* old user-specific data */ lparam_t old_extra_value; /* old value in extra bytes */ - unsigned int old_id; /* old window id */ + lparam_t old_id; /* old window id */ @END #define SET_WIN_STYLE 0x01 #define SET_WIN_EXSTYLE 0x02 diff --git a/server/request.h b/server/request.h index 98b3300de2c..f2d0e827d94 100644 --- a/server/request.h +++ b/server/request.h @@ -1479,12 +1479,11 @@ C_ASSERT( FIELD_OFFSET(struct set_window_info_request, is_unicode) == 14 ); C_ASSERT( FIELD_OFFSET(struct set_window_info_request, handle) == 16 ); C_ASSERT( FIELD_OFFSET(struct set_window_info_request, style) == 20 ); C_ASSERT( FIELD_OFFSET(struct set_window_info_request, ex_style) == 24 ); -C_ASSERT( FIELD_OFFSET(struct set_window_info_request, id) == 28 ); +C_ASSERT( FIELD_OFFSET(struct set_window_info_request, extra_size) == 28 ); C_ASSERT( FIELD_OFFSET(struct set_window_info_request, instance) == 32 ); C_ASSERT( FIELD_OFFSET(struct set_window_info_request, user_data) == 40 ); -C_ASSERT( FIELD_OFFSET(struct set_window_info_request, extra_offset) == 48 ); -C_ASSERT( FIELD_OFFSET(struct set_window_info_request, extra_size) == 52 ); -C_ASSERT( FIELD_OFFSET(struct set_window_info_request, extra_value) == 56 ); +C_ASSERT( FIELD_OFFSET(struct set_window_info_request, extra_value) == 48 ); +C_ASSERT( FIELD_OFFSET(struct set_window_info_request, extra_offset) == 56 ); C_ASSERT( sizeof(struct set_window_info_request) == 64 ); C_ASSERT( FIELD_OFFSET(struct set_window_info_reply, old_style) == 8 ); C_ASSERT( FIELD_OFFSET(struct set_window_info_reply, old_ex_style) == 12 ); diff --git a/server/trace.c b/server/trace.c index 429b39a5cdd..32606d540f7 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2907,12 +2907,11 @@ static void dump_set_window_info_request( const struct set_window_info_request * fprintf( stderr, ", handle=%08x", req->handle ); fprintf( stderr, ", style=%08x", req->style ); fprintf( stderr, ", ex_style=%08x", req->ex_style ); - fprintf( stderr, ", id=%08x", req->id ); + fprintf( stderr, ", extra_size=%u", req->extra_size ); dump_uint64( ", instance=", &req->instance ); dump_uint64( ", user_data=", &req->user_data ); - fprintf( stderr, ", extra_offset=%d", req->extra_offset ); - fprintf( stderr, ", extra_size=%u", req->extra_size ); dump_uint64( ", extra_value=", &req->extra_value ); + fprintf( stderr, ", extra_offset=%d", req->extra_offset ); } static void dump_set_window_info_reply( const struct set_window_info_reply *req ) @@ -2922,7 +2921,7 @@ static void dump_set_window_info_reply( const struct set_window_info_reply *req dump_uint64( ", old_instance=", &req->old_instance ); dump_uint64( ", old_user_data=", &req->old_user_data ); dump_uint64( ", old_extra_value=", &req->old_extra_value ); - fprintf( stderr, ", old_id=%08x", req->old_id ); + dump_uint64( ", old_id=", &req->old_id ); } static void dump_set_parent_request( const struct set_parent_request *req ) diff --git a/server/window.c b/server/window.c index 6b889df9149..7675cd1103d 100644 --- a/server/window.c +++ b/server/window.c @@ -74,7 +74,7 @@ struct window struct region *update_region; /* update region (relative to window rect) */ unsigned int style; /* window style */ unsigned int ex_style; /* window extended style */ - unsigned int id; /* window id */ + lparam_t id; /* window id */ mod_handle_t instance; /* creator instance */ unsigned int is_unicode : 1; /* ANSI or unicode */ unsigned int is_linked : 1; /* is it linked into the parent z-order list? */ @@ -2245,7 +2245,7 @@ DECL_HANDLER(set_window_info) else win->ex_style = (req->ex_style & ~WS_EX_TOPMOST) | (win->ex_style & WS_EX_TOPMOST); if (!(win->ex_style & WS_EX_LAYERED)) win->is_layered = 0; } - if (req->flags & SET_WIN_ID) win->id = req->id; + if (req->flags & SET_WIN_ID) win->id = req->extra_value; if (req->flags & SET_WIN_INSTANCE) win->instance = req->instance; if (req->flags & SET_WIN_UNICODE) win->is_unicode = req->is_unicode; if (req->flags & SET_WIN_USERDATA) win->user_data = req->user_data;
1
0
0
0
Jacek Caban : user32: Destroy window server objects from destroy_thread_windows.
by Alexandre Julliard
10 Feb '22
10 Feb '22
Module: wine Branch: master Commit: 6366694d2b12c3227f303f6b2edd6b4efe106bb3 URL:
https://source.winehq.org/git/wine.git/?a=commit;h=6366694d2b12c3227f303f6b…
Author: Jacek Caban <jacek(a)codeweavers.com> Date: Thu Feb 10 14:59:09 2022 +0100 user32: Destroy window server objects from destroy_thread_windows. Signed-off-by: Jacek Caban <jacek(a)codeweavers.com> Signed-off-by: Alexandre Julliard <julliard(a)winehq.org> --- dlls/user32/win.c | 9 +++++++++ server/window.c | 9 +++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/dlls/user32/win.c b/dlls/user32/win.c index 5e42872ed7a..82975cadd23 100644 --- a/dlls/user32/win.c +++ b/dlls/user32/win.c @@ -1216,6 +1216,15 @@ void destroy_thread_windows(void) win->obj.handle = *free_list_ptr; free_list_ptr = (WND **)&win->obj.handle; } + if (free_list) + { + SERVER_START_REQ( destroy_window ) + { + req->handle = 0; /* destroy all thread windows */ + wine_server_call( req ); + } + SERVER_END_REQ; + } USER_Unlock(); while ((win = free_list)) diff --git a/server/window.c b/server/window.c index ec8ee2f1790..6b889df9149 100644 --- a/server/window.c +++ b/server/window.c @@ -2101,8 +2101,13 @@ DECL_HANDLER(set_parent) /* destroy a window */ DECL_HANDLER(destroy_window) { - struct window *win = get_window( req->handle ); - if (win) + struct window *win; + + if (!req->handle) + { + destroy_thread_windows( current ); + } + else if ((win = get_window( req->handle ))) { if (!is_desktop_window(win)) free_window_handle( win ); else if (win->thread == current) detach_window_thread( win );
1
0
0
0
Jacek Caban : user32: Use a single lock in destroy_thread_windows.
by Alexandre Julliard
10 Feb '22
10 Feb '22
Module: wine Branch: master Commit: 72ec1c18a0513fba7218bdeee68aa7d09ce745a2 URL:
https://source.winehq.org/git/wine.git/?a=commit;h=72ec1c18a0513fba7218bdee…
Author: Jacek Caban <jacek(a)codeweavers.com> Date: Thu Feb 10 14:57:00 2022 +0100 user32: Use a single lock in destroy_thread_windows. Signed-off-by: Jacek Caban <jacek(a)codeweavers.com> Signed-off-by: Alexandre Julliard <julliard(a)winehq.org> --- dlls/user32/win.c | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/dlls/user32/win.c b/dlls/user32/win.c index 9e483b4c494..5e42872ed7a 100644 --- a/dlls/user32/win.c +++ b/dlls/user32/win.c @@ -1185,7 +1185,6 @@ static WND *next_thread_window( HWND *hwnd ) WND *win; WORD index = *hwnd ? USER_HANDLE_TO_INDEX( *hwnd ) + 1 : 0; - USER_Lock(); while (index < NB_USER_HANDLES) { if (!(ptr = user_handles[index++])) continue; @@ -1195,7 +1194,6 @@ static WND *next_thread_window( HWND *hwnd ) *hwnd = ptr->handle; return win; } - USER_Unlock(); return NULL; } @@ -1207,29 +1205,33 @@ static WND *next_thread_window( HWND *hwnd ) */ void destroy_thread_windows(void) { - WND *wndPtr; + WND *win, *free_list = NULL, **free_list_ptr = &free_list; HWND hwnd = 0; - HMENU menu, sys_menu; - struct window_surface *surface; - while ((wndPtr = next_thread_window( &hwnd ))) + USER_Lock(); + while ((win = next_thread_window( &hwnd ))) + { + free_dce( win->dce, win->obj.handle ); + InterlockedCompareExchangePointer( &user_handles[USER_HANDLE_TO_INDEX(hwnd)], NULL, win ); + win->obj.handle = *free_list_ptr; + free_list_ptr = (WND **)&win->obj.handle; + } + USER_Unlock(); + + while ((win = free_list)) { - /* destroy the client-side storage */ + free_list = win->obj.handle; + TRACE( "destroying %p\n", win ); - menu = ((wndPtr->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD) ? (HMENU)wndPtr->wIDmenu : 0; - sys_menu = wndPtr->hSysMenu; - free_dce( wndPtr->dce, hwnd ); - surface = wndPtr->surface; - InterlockedCompareExchangePointer( &user_handles[USER_HANDLE_TO_INDEX(hwnd)], NULL, wndPtr ); - WIN_ReleasePtr( wndPtr ); - HeapFree( GetProcessHeap(), 0, wndPtr ); - if (menu) DestroyMenu( menu ); - if (sys_menu) DestroyMenu( sys_menu ); - if (surface) + if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD && win->wIDmenu) + DestroyMenu( UlongToHandle(win->wIDmenu) ); + if (win->hSysMenu) DestroyMenu( win->hSysMenu ); + if (win->surface) { - register_window_surface( surface, NULL ); - window_surface_release( surface ); + register_window_surface( win->surface, NULL ); + window_surface_release( win->surface ); } + HeapFree( GetProcessHeap(), 0, win ); } }
1
0
0
0
← Newer
1
...
94
95
96
97
98
99
100
...
150
Older →
Jump to page:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
Results per page:
10
25
50
100
200