Wine-Devel
By thread
wine-devel@list.winehq.org
By month
Messages by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- 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
June 2022
- 68 participants
- 3274 messages
[PATCH 0/1] MR274: winedbg: Fix typos in manpage.
by Jinoh Kang (@iamahuman)
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/274
June 18, 2022
Re: [PATCH 0/8] MR272: d3dx10: Add D3DX10CreateThreadPump implementation.
by Piotr Caban (@piotr)
On Sat Jun 18 16:12:19 2022 +0000, Nikolay Sivov wrote:
> Could we use threadpool API for this?
While it would be possible it doesn't fit well this use case. The thread pump implementation uses permanent threads with dedicated threads for loading and processing tasks.
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/272#note_2316
June 18, 2022
Re: How to identify the primary battery (for Wine)
by Alex Henrie
On Fri, Jun 17, 2022 at 5:31 AM Benjamin Berg <benjamin(a)sipsolutions.net> wrote:
>
> On Tue, 2022-06-14 at 09:17 -0600, Alex Henrie wrote:
> > On Tue, Jun 14, 2022 at 8:17 AM Sebastian Reichel
> > <sebastian.reichel(a)collabora.com> wrote:
> > > On Tue, Jun 14, 2022 at 2:05 AM Benjamin Berg <benjamin(a)sipsolutions.net> wrote:
> > >
> > > > Wouldn't it make sense for Wine to use the UPower provided
> > > > DisplayDevice that can be queried through DBus?
> > >
> > > UPower does the required data aggregation for the 'DisplayDevice'.
> > > I don't know enough about the Wine codebase to recommend for or
> > > against using UPower.
> >
> > I also don't know if D-Bus would be a good choice here. It would
> > certainly be a bigger change than the patch that I've proposed. What
> > are the advantages and disadvantages of querying the battery through
> > D-Bus, besides it doing battery aggregation for us?
>
> Well, depends on what you need, it will:
> 1. Calculate a energy/power values if the HW reports charge/current
> 2. Try to generate a proper state (charging/discharging, etc.) if the
> hardware does not provide it
> 3. Aggregate multiple batteries
>
> Actually, I think using UPower likely fixes bugs:
> * You are currently not supporting modern hardware that reports
> energy/power values (rather than charge/current).
> * You are only reading one battery
> * You are not estimating a rate if the HW does not provide one
> (and not smoothing it which might be desirable).
>
> And, well, it should be easy. You can just query properties on a fixed
> DBus path. And if it fails, just assume you don't have a battery.
Thanks for the information! It looks like we're sticking with reading
from /sys directly for now, but it's good to know what we can get over
D-Bus for if we have more problems in the future.
-Alex
June 18, 2022
[PATCH v2 1/1] ntdll/tests: Add more tests for \Device\NamedPipe\ root directory.
by Jinoh Kang
From: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
---
dlls/ntdll/tests/om.c | 9 +++++
dlls/ntdll/tests/pipe.c | 90 ++++++++++++++++++++++++++++++++++++++++-
2 files changed, 98 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/tests/om.c b/dlls/ntdll/tests/om.c
index 2e03abd13ed..f151d55d800 100644
--- a/dlls/ntdll/tests/om.c
+++ b/dlls/ntdll/tests/om.c
@@ -1881,6 +1881,15 @@ static void test_query_object(void)
pNtClose( handle );
+ handle = CreateFileA( "\\\\.\\pipe\\", 0, 0, NULL, OPEN_EXISTING, 0, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFile failed (%lu)\n", GetLastError() );
+
+ test_object_name( handle, L"\\Device\\NamedPipe\\", TRUE );
+ test_object_type( handle, L"File" );
+ test_file_info( handle );
+
+ pNtClose( handle );
+
RtlInitUnicodeString( &path, L"\\REGISTRY\\Machine" );
status = pNtCreateKey( &handle, KEY_READ, &attr, 0, 0, 0, 0 );
ok( status == STATUS_SUCCESS, "NtCreateKey failed status %lx\n", status );
diff --git a/dlls/ntdll/tests/pipe.c b/dlls/ntdll/tests/pipe.c
index dc9737e73d8..0ad09daaa82 100644
--- a/dlls/ntdll/tests/pipe.c
+++ b/dlls/ntdll/tests/pipe.c
@@ -51,6 +51,13 @@ typedef struct {
ULONG NamedPipeEnd;
} FILE_PIPE_LOCAL_INFORMATION;
+typedef struct _FILE_PIPE_WAIT_FOR_BUFFER {
+ LARGE_INTEGER Timeout;
+ ULONG NameLength;
+ BOOLEAN TimeoutSpecified;
+ WCHAR Name[1];
+} FILE_PIPE_WAIT_FOR_BUFFER, *PFILE_PIPE_WAIT_FOR_BUFFER;
+
#ifndef FILE_SYNCHRONOUS_IO_ALERT
#define FILE_SYNCHRONOUS_IO_ALERT 0x10
#endif
@@ -62,6 +69,10 @@ typedef struct {
#ifndef FSCTL_PIPE_LISTEN
#define FSCTL_PIPE_LISTEN CTL_CODE(FILE_DEVICE_NAMED_PIPE, 2, METHOD_BUFFERED, FILE_ANY_ACCESS)
#endif
+
+#ifndef FSCTL_PIPE_WAIT
+#define FSCTL_PIPE_WAIT CTL_CODE(FILE_DEVICE_NAMED_PIPE, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#endif
#endif
static NTSTATUS (WINAPI *pNtFsControlFile) (HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, PVOID apc_context, PIO_STATUS_BLOCK io, ULONG code, PVOID in_buffer, ULONG in_size, PVOID out_buffer, ULONG out_size);
@@ -177,6 +188,48 @@ static NTSTATUS listen_pipe(HANDLE hPipe, HANDLE hEvent, PIO_STATUS_BLOCK iosb,
return pNtFsControlFile(hPipe, hEvent, use_apc ? &ioapc: NULL, use_apc ? &dummy: NULL, iosb, FSCTL_PIPE_LISTEN, 0, 0, 0, 0);
}
+static NTSTATUS wait_pipe(HANDLE handle, PUNICODE_STRING name, const LARGE_INTEGER* timeout)
+{
+ HANDLE event;
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES attr;
+ IO_STATUS_BLOCK iosb;
+ FILE_PIPE_WAIT_FOR_BUFFER *pipe_wait;
+ ULONG pipe_wait_size;
+
+ pipe_wait_size = offsetof(FILE_PIPE_WAIT_FOR_BUFFER, Name[0]) + name->Length;
+ pipe_wait = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pipe_wait_size);
+ if (!pipe_wait) return STATUS_NO_MEMORY;
+
+ pipe_wait->TimeoutSpecified = !!timeout;
+ pipe_wait->NameLength = name->Length;
+ if (timeout) pipe_wait->Timeout = *timeout;
+ memcpy(pipe_wait->Name, name->Buffer, name->Length);
+
+ InitializeObjectAttributes(&attr, NULL, 0, 0, NULL);
+ status = NtCreateEvent(&event, GENERIC_ALL, &attr, NotificationEvent, FALSE);
+ if (status != STATUS_SUCCESS)
+ {
+ ok(0, "NtCreateEvent failure: %#lx\n", status);
+ HeapFree(GetProcessHeap(), 0, pipe_wait);
+ return status;
+ }
+
+ memset(&iosb, 0, sizeof(iosb));
+ iosb.Status = STATUS_PENDING;
+ status = pNtFsControlFile(handle, event, NULL, NULL, &iosb, FSCTL_PIPE_WAIT,
+ pipe_wait, pipe_wait_size, NULL, 0);
+ if (status == STATUS_PENDING)
+ {
+ WaitForSingleObject(event, INFINITE);
+ status = iosb.Status;
+ }
+
+ NtClose(event);
+ HeapFree(GetProcessHeap(), 0, pipe_wait);
+ return status;
+}
+
static void test_create_invalid(void)
{
IO_STATUS_BLOCK iosb;
@@ -2420,6 +2473,7 @@ static void test_security_info(void)
static void test_empty_name(void)
{
+ static const LARGE_INTEGER zero_timeout = {{ 0 }};
HANDLE hdirectory, hpipe, hpipe2, hwrite, hwrite2, handle;
OBJECT_TYPE_INFORMATION *type_info;
OBJECT_NAME_INFORMATION *name_info;
@@ -2450,6 +2504,10 @@ static void test_empty_name(void)
FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0 );
ok(!status, "Got unexpected status %#lx.\n", status);
+ pRtlInitUnicodeString(&name, L"nonexistent_pipe");
+ status = wait_pipe(hdirectory, &name, &zero_timeout);
+ todo_wine ok(status == STATUS_ILLEGAL_FUNCTION, "unexpected status for FSCTL_PIPE_WAIT on \\Device\\NamedPipe: %#lx\n", status);
+
name.Buffer = NULL;
name.Length = 0;
name.MaximumLength = 0;
@@ -2462,6 +2520,17 @@ static void test_empty_name(void)
todo_wine ok(status == STATUS_OBJECT_NAME_INVALID, "Got unexpected status %#lx.\n", status);
if (!status)
CloseHandle(hpipe);
+
+ pRtlInitUnicodeString(&name, L"test3\\pipe");
+ attr.RootDirectory = hdirectory;
+ attr.ObjectName = &name;
+ timeout.QuadPart = -(LONG64)10000000;
+ status = pNtCreateNamedPipeFile(&hpipe, GENERIC_READ|GENERIC_WRITE, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout);
+ ok(status == STATUS_OBJECT_NAME_INVALID, "unexpected status from NtCreateNamedPipeFile: %#lx\n", status);
+ if (!status)
+ CloseHandle(hpipe);
+
CloseHandle(hdirectory);
pRtlInitUnicodeString(&name, L"\\Device\\NamedPipe\\");
@@ -2475,6 +2544,10 @@ static void test_empty_name(void)
FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0 );
ok(!status, "Got unexpected status %#lx.\n", status);
+ pRtlInitUnicodeString(&name, L"nonexistent_pipe");
+ status = wait_pipe(hdirectory, &name, &zero_timeout);
+ ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "unexpected status for FSCTL_PIPE_WAIT on \\Device\\NamedPipe\\: %#lx\n", status);
+
name.Buffer = NULL;
name.Length = 0;
name.MaximumLength = 0;
@@ -2565,9 +2638,24 @@ static void test_empty_name(void)
CloseHandle(hwrite);
CloseHandle(hpipe);
- CloseHandle(hdirectory);
CloseHandle(hpipe2);
CloseHandle(hwrite2);
+
+ pRtlInitUnicodeString(&name, L"test3\\pipe");
+ attr.RootDirectory = hdirectory;
+ attr.ObjectName = &name;
+ timeout.QuadPart = -(LONG64)10000000;
+ status = pNtCreateNamedPipeFile(&hpipe, GENERIC_READ|GENERIC_WRITE, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout);
+ todo_wine ok(!status, "unexpected failure from NtCreateNamedPipeFile: %#lx\n", status);
+
+ handle = CreateFileA("\\\\.\\pipe\\test3\\pipe", GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, 0, 0 );
+ todo_wine ok(handle != INVALID_HANDLE_VALUE, "Failed to open NamedPipe (%lu)\n", GetLastError());
+
+ CloseHandle(handle);
+ CloseHandle(hpipe);
+ CloseHandle(hdirectory);
}
START_TEST(pipe)
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/273
June 18, 2022
[PATCH v2 0/1] MR273: ntdll/tests: Add more tests for \Device\NamedPipe\ root directory.
by Jinoh Kang (@iamahuman)
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
--
v2: ntdll/tests: Add more tests for \Device\NamedPipe\ root directory.
https://gitlab.winehq.org/wine/wine/-/merge_requests/273
June 18, 2022
[PATCH 1/1] ntdll/tests: Add more tests for \Device\NamedPipe\ root directory.
by Jinoh Kang
From: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
---
dlls/ntdll/tests/om.c | 9 +++++
dlls/ntdll/tests/pipe.c | 90 ++++++++++++++++++++++++++++++++++++++++-
2 files changed, 98 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/tests/om.c b/dlls/ntdll/tests/om.c
index 2e03abd13ed..13ade1dbb28 100644
--- a/dlls/ntdll/tests/om.c
+++ b/dlls/ntdll/tests/om.c
@@ -1881,6 +1881,15 @@ static void test_query_object(void)
pNtClose( handle );
+ handle = CreateFileA( "\\\\.\\pipe\\", 0, 0, NULL, OPEN_EXISTING, 0, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFile failed (%d)\n", GetLastError() );
+
+ test_object_name( handle, L"\\Device\\NamedPipe\\", TRUE );
+ test_object_type( handle, L"File" );
+ test_file_info( handle );
+
+ pNtClose( handle );
+
RtlInitUnicodeString( &path, L"\\REGISTRY\\Machine" );
status = pNtCreateKey( &handle, KEY_READ, &attr, 0, 0, 0, 0 );
ok( status == STATUS_SUCCESS, "NtCreateKey failed status %lx\n", status );
diff --git a/dlls/ntdll/tests/pipe.c b/dlls/ntdll/tests/pipe.c
index dc9737e73d8..9b8b24bf1df 100644
--- a/dlls/ntdll/tests/pipe.c
+++ b/dlls/ntdll/tests/pipe.c
@@ -51,6 +51,13 @@ typedef struct {
ULONG NamedPipeEnd;
} FILE_PIPE_LOCAL_INFORMATION;
+typedef struct _FILE_PIPE_WAIT_FOR_BUFFER {
+ LARGE_INTEGER Timeout;
+ ULONG NameLength;
+ BOOLEAN TimeoutSpecified;
+ WCHAR Name[1];
+} FILE_PIPE_WAIT_FOR_BUFFER, *PFILE_PIPE_WAIT_FOR_BUFFER;
+
#ifndef FILE_SYNCHRONOUS_IO_ALERT
#define FILE_SYNCHRONOUS_IO_ALERT 0x10
#endif
@@ -62,6 +69,10 @@ typedef struct {
#ifndef FSCTL_PIPE_LISTEN
#define FSCTL_PIPE_LISTEN CTL_CODE(FILE_DEVICE_NAMED_PIPE, 2, METHOD_BUFFERED, FILE_ANY_ACCESS)
#endif
+
+#ifndef FSCTL_PIPE_WAIT
+#define FSCTL_PIPE_WAIT CTL_CODE(FILE_DEVICE_NAMED_PIPE, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#endif
#endif
static NTSTATUS (WINAPI *pNtFsControlFile) (HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, PVOID apc_context, PIO_STATUS_BLOCK io, ULONG code, PVOID in_buffer, ULONG in_size, PVOID out_buffer, ULONG out_size);
@@ -177,6 +188,48 @@ static NTSTATUS listen_pipe(HANDLE hPipe, HANDLE hEvent, PIO_STATUS_BLOCK iosb,
return pNtFsControlFile(hPipe, hEvent, use_apc ? &ioapc: NULL, use_apc ? &dummy: NULL, iosb, FSCTL_PIPE_LISTEN, 0, 0, 0, 0);
}
+static NTSTATUS wait_pipe(HANDLE handle, PUNICODE_STRING name, const LARGE_INTEGER* timeout)
+{
+ HANDLE event;
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES attr;
+ IO_STATUS_BLOCK iosb;
+ FILE_PIPE_WAIT_FOR_BUFFER *pipe_wait;
+ ULONG pipe_wait_size;
+
+ pipe_wait_size = offsetof(FILE_PIPE_WAIT_FOR_BUFFER, Name[0]) + name->Length;
+ pipe_wait = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pipe_wait_size);
+ if (!pipe_wait) return STATUS_NO_MEMORY;
+
+ pipe_wait->TimeoutSpecified = !!timeout;
+ pipe_wait->NameLength = name->Length;
+ if (timeout) pipe_wait->Timeout = *timeout;
+ memcpy(pipe_wait->Name, name->Buffer, name->Length);
+
+ InitializeObjectAttributes(&attr, NULL, 0, 0, NULL);
+ status = NtCreateEvent(&event, GENERIC_ALL, &attr, NotificationEvent, FALSE);
+ if (status != STATUS_SUCCESS)
+ {
+ ok(0, "NtCreateEvent failure: %08x\n", status);
+ HeapFree(GetProcessHeap(), 0, pipe_wait);
+ return status;
+ }
+
+ memset(&iosb, 0, sizeof(iosb));
+ iosb.Status = STATUS_PENDING;
+ status = pNtFsControlFile(handle, event, NULL, NULL, &iosb, FSCTL_PIPE_WAIT,
+ pipe_wait, pipe_wait_size, NULL, 0);
+ if (status == STATUS_PENDING)
+ {
+ WaitForSingleObject(event, INFINITE);
+ status = iosb.Status;
+ }
+
+ NtClose(event);
+ HeapFree(GetProcessHeap(), 0, pipe_wait);
+ return status;
+}
+
static void test_create_invalid(void)
{
IO_STATUS_BLOCK iosb;
@@ -2420,6 +2473,7 @@ static void test_security_info(void)
static void test_empty_name(void)
{
+ static const LARGE_INTEGER zero_timeout = {{ 0 }};
HANDLE hdirectory, hpipe, hpipe2, hwrite, hwrite2, handle;
OBJECT_TYPE_INFORMATION *type_info;
OBJECT_NAME_INFORMATION *name_info;
@@ -2450,6 +2504,10 @@ static void test_empty_name(void)
FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0 );
ok(!status, "Got unexpected status %#lx.\n", status);
+ pRtlInitUnicodeString(&name, L"nonexistent_pipe");
+ status = wait_pipe(hdirectory, &name, &zero_timeout);
+ todo_wine ok(status == STATUS_ILLEGAL_FUNCTION, "unexpected status for FSCTL_PIPE_WAIT on \\Device\\NamedPipe: %08x\n", status);
+
name.Buffer = NULL;
name.Length = 0;
name.MaximumLength = 0;
@@ -2462,6 +2520,17 @@ static void test_empty_name(void)
todo_wine ok(status == STATUS_OBJECT_NAME_INVALID, "Got unexpected status %#lx.\n", status);
if (!status)
CloseHandle(hpipe);
+
+ pRtlInitUnicodeString(&name, L"test3\\pipe");
+ attr.RootDirectory = hdirectory;
+ attr.ObjectName = &name;
+ timeout.QuadPart = -(LONG64)10000000;
+ status = pNtCreateNamedPipeFile(&hpipe, GENERIC_READ|GENERIC_WRITE, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout);
+ ok(status == STATUS_OBJECT_NAME_INVALID, "unexpected status from NtCreateNamedPipeFile: %08x\n", status);
+ if (!status)
+ CloseHandle(hpipe);
+
CloseHandle(hdirectory);
pRtlInitUnicodeString(&name, L"\\Device\\NamedPipe\\");
@@ -2475,6 +2544,10 @@ static void test_empty_name(void)
FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0 );
ok(!status, "Got unexpected status %#lx.\n", status);
+ pRtlInitUnicodeString(&name, L"nonexistent_pipe");
+ status = wait_pipe(hdirectory, &name, &zero_timeout);
+ ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "unexpected status for FSCTL_PIPE_WAIT on \\Device\\NamedPipe\\: %08x\n", status);
+
name.Buffer = NULL;
name.Length = 0;
name.MaximumLength = 0;
@@ -2565,9 +2638,24 @@ static void test_empty_name(void)
CloseHandle(hwrite);
CloseHandle(hpipe);
- CloseHandle(hdirectory);
CloseHandle(hpipe2);
CloseHandle(hwrite2);
+
+ pRtlInitUnicodeString(&name, L"test3\\pipe");
+ attr.RootDirectory = hdirectory;
+ attr.ObjectName = &name;
+ timeout.QuadPart = -(LONG64)10000000;
+ status = pNtCreateNamedPipeFile(&hpipe, GENERIC_READ|GENERIC_WRITE, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout);
+ todo_wine ok(!status, "unexpected failure from NtCreateNamedPipeFile: %08x\n", status);
+
+ handle = CreateFileA("\\\\.\\pipe\\test3\\pipe", GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, 0, 0 );
+ todo_wine ok(handle != INVALID_HANDLE_VALUE, "Failed to open NamedPipe (%u)\n", GetLastError());
+
+ CloseHandle(handle);
+ CloseHandle(hpipe);
+ CloseHandle(hdirectory);
}
START_TEST(pipe)
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/273
June 18, 2022
[PATCH 0/1] MR273: ntdll/tests: Add more tests for \Device\NamedPipe\ root directory.
by Jinoh Kang (@iamahuman)
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/273
June 18, 2022
Re: [PATCH 0/8] MR272: d3dx10: Add D3DX10CreateThreadPump implementation.
by Nikolay Sivov (@nsivov)
Could we use threadpool API for this?
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/272#note_2313
June 18, 2022
Re: [PATCH 8/8] d3dx10/tests: Add D3DX10CreateThreadPump tests.
by Marvin
Hi,
It looks like your patch introduced the new failures shown below.
Please investigate and fix them before resubmitting your patch.
If they are not new, fixing them anyway would help a lot. Otherwise
please ask for the known failures list to be updated.
The full results can be found at:
https://testbot.winehq.org/JobDetails.pl?Key=117237
Your paranoid android.
=== debian11 (32 bit report) ===
d3dx10_43:
Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x70bb0040).
=== debian11 (32 bit Arabic:Morocco report) ===
d3dx10_43:
Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x70bb0040).
=== debian11 (32 bit German report) ===
d3dx10_43:
Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x70bb0040).
=== debian11 (32 bit French report) ===
d3dx10_43:
Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x70bb0040).
=== debian11 (32 bit Hebrew:Israel report) ===
d3dx10_43:
Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x70bb0040).
=== debian11 (32 bit Hindi:India report) ===
d3dx10_43:
Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x70bb0040).
=== debian11 (32 bit Japanese:Japan report) ===
d3dx10_43:
Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x70bb0040).
=== debian11 (32 bit Chinese:China report) ===
d3dx10_43:
Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x70bb0040).
=== debian11 (32 bit WoW report) ===
d3dx10_43:
Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x70bb0040).
June 18, 2022
[PATCH 8/8] d3dx10/tests: Add D3DX10CreateThreadPump tests.
by Piotr Caban
From: Piotr Caban <piotr(a)codeweavers.com>
Signed-off-by: Piotr Caban <piotr(a)codeweavers.com>
---
dlls/d3dx10_43/tests/d3dx10.c | 355 ++++++++++++++++++++++++++++++++++
1 file changed, 355 insertions(+)
diff --git a/dlls/d3dx10_43/tests/d3dx10.c b/dlls/d3dx10_43/tests/d3dx10.c
index 1c28a62b700..d249bf78141 100644
--- a/dlls/d3dx10_43/tests/d3dx10.c
+++ b/dlls/d3dx10_43/tests/d3dx10.c
@@ -2065,6 +2065,360 @@ static void test_D3DX10CreateAsyncTextureProcessor(void)
ID3D10Device_Release(device);
}
+struct data_object
+{
+ ID3DX10DataLoader ID3DX10DataLoader_iface;
+ ID3DX10DataProcessor ID3DX10DataProcessor_iface;
+
+ HANDLE load_started;
+ HANDLE load_done;
+ HANDLE decompress_done;
+ HRESULT load_ret;
+
+ DWORD create_tid;
+ DWORD io_tid;
+ DWORD process_tid;
+};
+
+static struct data_object* data_object_from_ID3DX10DataLoader(ID3DX10DataLoader *iface)
+{
+ return CONTAINING_RECORD(iface, struct data_object, ID3DX10DataLoader_iface);
+}
+
+static LONG data_loader_load_count;
+static WINAPI HRESULT data_loader_Load(ID3DX10DataLoader *iface)
+{
+ struct data_object *data_object = data_object_from_ID3DX10DataLoader(iface);
+ DWORD ret;
+
+ ok(InterlockedDecrement(&data_loader_load_count) >= 0, "unexpected call\n");
+
+ data_object->io_tid = GetCurrentThreadId();
+ ok(data_object->io_tid != data_object->create_tid, "Load called in main thread.\n");
+
+ SetEvent(data_object->load_started);
+ ret = WaitForSingleObject(data_object->load_done, INFINITE);
+ ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %x\n", ret);
+ return data_object->load_ret;
+}
+
+static LONG data_loader_decompress_count;
+static WINAPI HRESULT data_loader_Decompress(ID3DX10DataLoader *iface, void **data, SIZE_T *bytes)
+{
+ struct data_object *data_object = data_object_from_ID3DX10DataLoader(iface);
+ DWORD ret;
+
+ ok(InterlockedDecrement(&data_loader_decompress_count) >= 0, "unexpected call\n");
+ ok(data != NULL, "data == NULL\n");
+ ok(bytes != NULL, "bytes == NULL\n");
+
+ data_object->process_tid = GetCurrentThreadId();
+ ok(data_object->process_tid != data_object->create_tid, "Decompress called in main thread.\n");
+ ok(data_object->process_tid != data_object->io_tid, "Decompress called in IO thread.\n");
+
+ *data = (void*)0xdeadbeef;
+ *bytes = 0xdead;
+ ret = WaitForSingleObject(data_object->decompress_done, INFINITE);
+ ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %x\n", ret);
+ return S_OK;
+}
+
+static LONG data_loader_destroy_count;
+static WINAPI HRESULT data_loader_Destroy(ID3DX10DataLoader *iface)
+{
+ ok(InterlockedDecrement(&data_loader_destroy_count) >= 0, "unexpected call\n");
+ return S_OK;
+}
+
+static ID3DX10DataLoaderVtbl D3DX10DataLoaderVtbl =
+{
+ data_loader_Load,
+ data_loader_Decompress,
+ data_loader_Destroy
+};
+
+static struct data_object* data_object_from_ID3DX10DataProcessor(ID3DX10DataProcessor *iface)
+{
+ return CONTAINING_RECORD(iface, struct data_object, ID3DX10DataProcessor_iface);
+}
+
+static LONG data_processor_process_count;
+static HRESULT WINAPI data_processor_Process(ID3DX10DataProcessor *iface, void *data, SIZE_T bytes)
+{
+ struct data_object *data_object = data_object_from_ID3DX10DataProcessor(iface);
+
+ ok(InterlockedDecrement(&data_processor_process_count) >= 0, "unexpected call\n");
+ ok(GetCurrentThreadId() == data_object->process_tid, "Process called in different thread.\n");
+
+ ok(data == (void*)0xdeadbeef, "data = %p\n", data);
+ ok(bytes == 0xdead, "bytes = %lu\n", bytes);
+ return S_OK;
+}
+
+static LONG data_processor_create_count;
+static HRESULT WINAPI data_processor_CreateDeviceObject(ID3DX10DataProcessor *iface, void **object)
+{
+ struct data_object *data_object = data_object_from_ID3DX10DataProcessor(iface);
+
+ ok(InterlockedDecrement(&data_processor_create_count) >= 0, "unexpected call\n");
+ ok(GetCurrentThreadId() == data_object->create_tid, "CreateDeviceObject not called in main thread.\n");
+
+ *object = (void*)0xdeadf00d;
+ return S_OK;
+}
+
+static LONG data_processor_destroy_count;
+static HRESULT WINAPI data_processor_Destroy(ID3DX10DataProcessor *iface)
+{
+ struct data_object *data_object = data_object_from_ID3DX10DataProcessor(iface);
+
+ ok(InterlockedDecrement(&data_processor_destroy_count) >= 0, "unexpected call\n");
+
+ CloseHandle(data_object->load_started);
+ CloseHandle(data_object->load_done);
+ CloseHandle(data_object->decompress_done);
+ free(data_object);
+ return S_OK;
+}
+
+static ID3DX10DataProcessorVtbl D3DX10DataProcessorVtbl =
+{
+ data_processor_Process,
+ data_processor_CreateDeviceObject,
+ data_processor_Destroy
+};
+
+static struct data_object* create_data_object(HRESULT load_ret)
+{
+ struct data_object *data_object = malloc(sizeof(*data_object));
+
+ data_object->ID3DX10DataLoader_iface.lpVtbl = &D3DX10DataLoaderVtbl;
+ data_object->ID3DX10DataProcessor_iface.lpVtbl = &D3DX10DataProcessorVtbl;
+
+ data_object->load_started = CreateEventW(NULL, FALSE, FALSE, NULL);
+ ok(data_object->load_started != NULL, "CreateEvent failed (%u)\n", GetLastError());
+ data_object->load_done = CreateEventW(NULL, FALSE, FALSE, NULL);
+ ok(data_object->load_done != NULL, "CreateEvent failed (%u)\n", GetLastError());
+ data_object->decompress_done = CreateEventW(NULL, FALSE, FALSE, NULL);
+ ok(data_object->decompress_done != NULL, "CreateEvent failed (%u)\n", GetLastError());
+ data_object->load_ret = load_ret;
+
+ data_object->create_tid = GetCurrentThreadId();
+ return data_object;
+}
+
+static void test_D3DX10CreateThreadPump(void)
+{
+ UINT io_count, process_count, device_count, count;
+ struct data_object *data_object[2];
+ ID3DX10DataProcessor *processor;
+ D3DX10_IMAGE_INFO image_info;
+ ID3DX10DataLoader *loader;
+ HRESULT hr, work_item_hr;
+ ID3D10Resource *resource;
+ ID3DX10ThreadPump *pump;
+ ID3D10Device *device;
+ SYSTEM_INFO info;
+ void *object;
+ DWORD ret;
+ int i;
+
+ hr = D3DX10CreateThreadPump(1024, 0, &pump);
+ ok(hr == E_FAIL, "Got unexpected hr %#x.\n", hr);
+ hr = D3DX10CreateThreadPump(0, 1024, &pump);
+ ok(hr == E_FAIL, "Got unexpected hr %#x.\n", hr);
+
+ GetSystemInfo(&info);
+ if (info.dwNumberOfProcessors > 1)
+ hr = D3DX10CreateThreadPump(0, 0, &pump);
+ else
+ hr = D3DX10CreateThreadPump(0, 2, &pump);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+
+ count = ID3DX10ThreadPump_GetWorkItemCount(pump);
+ ok(!count, "GetWorkItemCount returned %u.\n", count);
+ hr = ID3DX10ThreadPump_GetQueueStatus(pump, &io_count, &process_count, &device_count);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ ok(!io_count, "Got unexpected io_count = %u.\n", io_count);
+ ok(!process_count, "Got unexpected process_count = %u.\n", process_count);
+ ok(!device_count, "Got unexpected device_count = %u.\n", device_count);
+
+ data_object[0] = create_data_object(E_NOTIMPL);
+ data_object[1] = create_data_object(S_OK);
+
+ data_loader_load_count = 1;
+ hr = ID3DX10ThreadPump_AddWorkItem(pump, &data_object[0]->ID3DX10DataLoader_iface,
+ &data_object[0]->ID3DX10DataProcessor_iface, &work_item_hr, NULL);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ ret = WaitForSingleObject(data_object[0]->load_started, INFINITE);
+ ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %x.\n", ret);
+ ok(!data_loader_load_count, "Got unexpected data_loader_load_count %d.\n", data_loader_load_count);
+ count = ID3DX10ThreadPump_GetWorkItemCount(pump);
+ ok(count == 1, "GetWorkItemCount returned %u.\n", count);
+ hr = ID3DX10ThreadPump_GetQueueStatus(pump, &io_count, &process_count, &device_count);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ ok(!io_count, "Got unexpected io_count = %u.\n", io_count);
+ ok(!process_count, "Got unexpected process_count = %u.\n", process_count);
+ ok(!device_count, "Got unexpected device_count = %u.\n", device_count);
+
+ hr = ID3DX10ThreadPump_AddWorkItem(pump, &data_object[1]->ID3DX10DataLoader_iface,
+ &data_object[1]->ID3DX10DataProcessor_iface, NULL, &object);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ ret = WaitForSingleObject(data_object[0]->load_started, 50);
+ ok(ret == WAIT_TIMEOUT, "WaitForSingleObject returned %x.\n", ret);
+ count = ID3DX10ThreadPump_GetWorkItemCount(pump);
+ ok(count == 2, "GetWorkItemCount returned %u.\n", count);
+ hr = ID3DX10ThreadPump_GetQueueStatus(pump, &io_count, &process_count, &device_count);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ ok(io_count == 1, "Got unexpected io_count = %u.\n", io_count);
+ ok(!process_count, "Got unexpected process_count = %u.\n", process_count);
+ ok(!device_count, "Got unexpected device_count = %u.\n", device_count);
+
+ data_loader_load_count = 1;
+ data_loader_destroy_count = 1;
+ data_processor_destroy_count = 1;
+ SetEvent(data_object[0]->load_done);
+ ret = WaitForSingleObject(data_object[1]->load_started, INFINITE);
+ ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %x.\n", ret);
+ ok(work_item_hr == E_NOTIMPL, "Got unexpected work_item_hr = %#x.\n", work_item_hr);
+ ok(!data_loader_destroy_count, "Got unexpected data_loader_destroy_count %d.\n", data_loader_destroy_count);
+ ok(!data_processor_destroy_count, "Got unexpected data_processor_destroy_count %d.\n",
+ data_processor_destroy_count);
+ ok(!data_loader_load_count, "Got unexpected data_loader_load_count %d.\n", data_loader_load_count);
+
+ data_loader_decompress_count = 1;
+ data_processor_process_count = 1;
+ SetEvent(data_object[1]->load_done);
+ SetEvent(data_object[1]->decompress_done);
+
+ data_processor_create_count = 1;
+ data_loader_destroy_count = 1;
+ data_processor_destroy_count = 1;
+ hr = ID3DX10ThreadPump_WaitForAllItems(pump);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ ok(object == (void*)0xdeadf00d, "Got unexpected object = %p.\n", object);
+ ok(!data_loader_decompress_count, "Got unexpected data_loader_decompress_count %d.\n", data_loader_decompress_count);
+ ok(!data_processor_process_count, "Got unexpected data_processor_process_count %d.\n", data_processor_process_count);
+ ok(!data_processor_create_count, "Got unexpected data_processor_create_count %d.\n", data_processor_create_count);
+ ok(!data_loader_destroy_count, "Got unexpected data_loader_destroy_count %d.\n", data_loader_destroy_count);
+ ok(!data_processor_destroy_count, "Got unexpected data_processor_destroy_count %d.\n", data_processor_destroy_count);
+
+ data_object[0] = create_data_object(S_OK);
+ data_object[1] = create_data_object(S_OK);
+ SetEvent(data_object[0]->load_done);
+ SetEvent(data_object[1]->load_done);
+ SetEvent(data_object[1]->decompress_done);
+
+ data_loader_load_count = 2;
+ data_loader_decompress_count = 2;
+ data_processor_process_count = 1;
+ hr = ID3DX10ThreadPump_AddWorkItem(pump, &data_object[0]->ID3DX10DataLoader_iface,
+ &data_object[0]->ID3DX10DataProcessor_iface, NULL, &object);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ hr = ID3DX10ThreadPump_AddWorkItem(pump, &data_object[1]->ID3DX10DataLoader_iface,
+ &data_object[1]->ID3DX10DataProcessor_iface, NULL, &object);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ while(1)
+ {
+ hr = ID3DX10ThreadPump_GetQueueStatus(pump, &io_count, &process_count, &device_count);
+ if (hr != S_OK || device_count)
+ break;
+ Sleep(1);
+ }
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ ok(!io_count, "Got unexpected io_count = %u.\n", io_count);
+ ok(!process_count, "Got unexpected process_count = %u.\n", process_count);
+ ok(device_count == 1, "Got unexpected device_count = %u.\n", device_count);
+ ok(!data_loader_load_count, "Got unexpected data_loader_load_count %d.\n", data_loader_load_count);
+ ok(!data_loader_decompress_count, "Got unexpected data_loader_decompress_count %d.\n", data_loader_decompress_count);
+ ok(!data_processor_process_count, "Got unexpected data_processor_process_count %d.\n", data_processor_process_count);
+
+ hr = ID3DX10ThreadPump_ProcessDeviceWorkItems(pump, 0);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ hr = ID3DX10ThreadPump_GetQueueStatus(pump, &io_count, &process_count, &device_count);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ ok(!io_count, "Got unexpected io_count = %u.\n", io_count);
+ ok(!process_count, "Got unexpected process_count = %u.\n", process_count);
+ ok(device_count == 1, "Got unexpected device_count = %u.\n", device_count);
+
+ data_processor_create_count = 1;
+ data_loader_destroy_count = 1;
+ data_processor_destroy_count = 1;
+ hr = ID3DX10ThreadPump_ProcessDeviceWorkItems(pump, 1);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ hr = ID3DX10ThreadPump_GetQueueStatus(pump, &io_count, &process_count, &device_count);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ ok(!io_count, "Got unexpected io_count = %u.\n", io_count);
+ ok(!process_count, "Got unexpected process_count = %u.\n", process_count);
+ ok(!device_count, "Got unexpected device_count = %u.\n", device_count);
+ ok(!data_processor_create_count, "Got unexpected data_processor_create_count %d.\n", data_processor_create_count);
+ ok(!data_loader_destroy_count, "Got unexpected data_loader_destroy_count %d.\n", data_loader_destroy_count);
+ ok(!data_processor_destroy_count, "Got unexpected data_processor_destroy_count %d.\n", data_processor_destroy_count);
+
+ data_processor_process_count = 1;
+ data_loader_destroy_count = 1;
+ data_processor_destroy_count = 1;
+ SetEvent(data_object[0]->decompress_done);
+ hr = ID3DX10ThreadPump_PurgeAllItems(pump);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ data_processor_process_count = 0;
+ ok(!data_loader_destroy_count, "Got unexpected data_loader_destroy_count %d.\n", data_loader_destroy_count);
+ ok(!data_processor_destroy_count, "Got unexpected data_processor_destroy_count %d.\n", data_processor_destroy_count);
+
+ device = create_device();
+ if (!device)
+ {
+ skip("Failed to create device, skipping tests.\n");
+ ID3DX10ThreadPump_Release(pump);
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(test_image); ++i)
+ {
+ winetest_push_context("Test %u", i);
+
+ hr = D3DX10CreateAsyncMemoryLoader(test_image[i].data, test_image[i].size, &loader);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ hr = D3DX10CreateAsyncTextureInfoProcessor(&image_info, &processor);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ hr = ID3DX10ThreadPump_AddWorkItem(pump, loader, processor, &work_item_hr, NULL);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ hr = ID3DX10ThreadPump_WaitForAllItems(pump);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ ok(work_item_hr == S_OK || (work_item_hr == E_FAIL
+ && test_image[i].expected_info.ImageFileFormat == D3DX10_IFF_WMP),
+ "Got unexpected hr %#x.\n", work_item_hr);
+ if (work_item_hr == S_OK)
+ check_image_info(&image_info, test_image + i, __LINE__);
+
+ hr = D3DX10CreateAsyncMemoryLoader(test_image[i].data, test_image[i].size, &loader);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ hr = D3DX10CreateAsyncTextureProcessor(device, NULL, &processor);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ hr = ID3DX10ThreadPump_AddWorkItem(pump, loader, processor, &work_item_hr, (void **)&resource);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ hr = ID3DX10ThreadPump_WaitForAllItems(pump);
+ ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+ todo_wine_if(test_image[i].expected_info.MiscFlags & D3D10_RESOURCE_MISC_TEXTURECUBE)
+ ok(work_item_hr == S_OK || (work_item_hr == E_FAIL
+ && test_image[i].expected_info.ImageFileFormat == D3DX10_IFF_WMP),
+ "Got unexpected hr %#x.\n", work_item_hr);
+ if (work_item_hr == S_OK)
+ {
+ check_resource_info(resource, test_image + i, __LINE__);
+ check_resource_data(resource, test_image + i, __LINE__);
+ ID3D10Resource_Release(resource);
+ }
+
+ winetest_pop_context();
+ }
+
+ ID3D10Device_Release(device);
+
+ ret = ID3DX10ThreadPump_Release(pump);
+ ok(!ret, "Got unexpected refcount %u.\n", ret);
+}
+
static void test_get_image_info(void)
{
static const WCHAR test_resource_name[] = L"resource.data";
@@ -3572,6 +3926,7 @@ START_TEST(d3dx10)
test_D3DX10CreateAsyncResourceLoader();
test_D3DX10CreateAsyncTextureInfoProcessor();
test_D3DX10CreateAsyncTextureProcessor();
+ test_D3DX10CreateThreadPump();
test_get_image_info();
test_create_texture();
test_font();
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/272
June 18, 2022