[Bug 52461] New: The Legend of Heroes: Trails of Cold Steel III usually hangs on exit

WineHQ Bugzilla wine-bugs at winehq.org
Wed Jan 26 15:17:33 CST 2022


https://bugs.winehq.org/show_bug.cgi?id=52461

            Bug ID: 52461
           Summary: The Legend of Heroes: Trails of Cold Steel III usually
                    hangs on exit
           Product: Wine
           Version: 7.0
          Hardware: x86-64
                OS: Linux
            Status: NEW
          Keywords: patch
          Severity: normal
          Priority: P2
         Component: -unknown
          Assignee: wine-bugs at winehq.org
          Reporter: z.figura12 at gmail.com
      Distribution: ---

Created attachment 71749
  --> https://bugs.winehq.org/attachment.cgi?id=71749
patch mitigating the race

To reproduce it's sufficient to install the game from Steam, get into the main
menu, and select the "exit" option (page down with the S key then press Enter).
Closing the window also works. The hang doesn't happen every time but does
happen most times I tested.

This is an application bug. There are two threads—a main thread and a worker
thread. The main thread closes the handle to a mutex, goes off and does some
other things, and then tries to wait on the mutex handle again (with a timeout
of INFINITE). The worker thread basically does a timed wait, in a loop, on a
set of handles, except that instead of passing a timeout to
WaitForMultipleObjects(), it repeatedly creates a non-periodic auto-reset timer
object, sets it for 1 ms, and adds it to the wait array, and then closes the
handle.

Because of the way handles are allocated in Wine (i.e. "lowest slot first"),
and because of the specific pattern of handle usage of the program, what
usually happens is that when the mutex handle is closed, the value is reused
for a timer handle in the worker thread. While the worker thread is sleeping on
the timer, the main thread also waits on it, and when the timer fires, it only
wakes up the worker thread (which is almost always first in the queue). Closing
the handle of a waitable object in Windows doesn't interrupt other waits in
progress, and because the timer is non-periodic and auto-reset, the main thread
ends up waiting forever.

It turns out that Windows doesn't allocate handles like this. Rather, it seems
to use a free list, much like we use in other places in Wine: testing shows
that handles are always allocated in the reverse order that they are freed,
including across multiple threads, and regardless of how many handles were just
allocated.

Changing Wine to use a free list like this actually does mitigate the problem.
Because the worker thread closes its timer handle and then immediately creates
a new one, it will almost always get back the same handle value. The main
thread will most likely close its mutex handle while the worker thread is still
sleeping, with the effect that the mutex handle won't be reallocated and the
subsequent wait will return STATUS_INVALID_HANDLE.

In theory the potential for the race is still there, however; it's still
possible for the mutex handle to be closed after the timer handle is closed but
before a new timer is created, in which case the mutex handle value will be
reused for the timer value as it is currently. In practice I'm not sure if this
happens more than a negligible fraction of the time. I certainly couldn't
reproduce it after 6 or so tries, although that's not very many. Note that
there are also reports of it hanging on Windows [1]...

[1] https://steamcommunity.com/app/991270/discussions/0/2145343189632898904/

-- 
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.


More information about the wine-bugs mailing list