Every std::mutex, lock_guard, unique_lock, scoped_lock,
condition_variable, call_once and semaphore left in Source/Core is
replaced by a lock-free design:
- Input: the device list, control bindings, stick calibration and
default device are published snapshots read under a quiesce; a
rebind requested while another thread is rebinding is handed to
that thread instead of waited for.
- WiimoteReal: the pool thread owns the slots and runs commands from
an MPSC queue; the active Wiimotes are atomic slots.
- EXI/SI/IOS devices: socket and packet traffic goes through SPSC
rings or MPSC handoffs owned by one thread; USB scanning publishes
its device list, and libusb transfers drain through a CallGate.
- Memory cards: the flush thread applies change records queued by
the CPU thread.
- Custom assets: loads are scheduled as a published batch with an
atomic cursor, results come back over an MPSC stack, and busy
assets are skipped and retried.
- D3D state and GL program caches use insert-only atomic maps
(Common/AtomicHashMap.h).
- Logging appends with O_APPEND / FILE_APPEND_DATA; FatFs reports
busy instead of waiting; per-socket WSAStartup/WSACleanup.
Deleted as unused by the libretro core: the profiler, the FIFO
recorder, the volume verifier, the touch InputOverrider and the
GameCube adapter's libusb path.
No std::thread, std::async, std::future or std::promise is left. Each
thread is an sthread with a plain entry point: Common::ThreadEntry<&C::F>
turns a member function into one, Common::JoinThread joins and clears a
handle, and Common::StartDetachedThread covers the fire-and-forget users.
Common/ParallelFor.{h,c} (C89) runs fn(ctx, i) for i in [0, count) on a
persistent pool of one worker per extra core plus the caller. Jobs are
claimed with an atomic counter, workers park on an eventcount between
batches, and the batch hand-off is a seq_cst users/active pair, so there
is no lock anywhere. A caller that finds the pool busy runs its batch
serially instead of waiting. retro_deinit joins the pool.
- VolumeWii::HashGroup spawned 64 threads per 2 MiB group and
EncryptGroup another one per core; both are now parallel_for batches
on the pool. The H1/H2 tail is a few small hashes and runs inline.
- VolumeVerifier spawned up to five std::async threads per chunk; the
chunk's tasks now go to one persistent worker that fans them out with
parallel_for, still overlapping the next read. The Redump lookup is an
sthread joined in Finish or the destructor.
- The dual-core "GPU thread" in Core only set a thread_local on itself
and exited, since the frontend runs the GPU loop. It is gone, along
with the non-libretro branches of Core.cpp.
- The emu thread is started through Core::StartEmuThread.
- CPU core counts come from cpu_features_get_core_amount().
We were registering the hotplug callback when starting the thread, but
deregistering it when shutting down GCAdapter as a whole. Because the
thread can start and stop potentially many times before GCAdapter shuts
down, this led to callbacks being registered when we already have
callbacks. On Android, this was making Dolphin's Kotlin code for
registering the callback throw an IllegalStateException.
To fix this, deregister the callback when stopping the thread.
Make `s_is_adapter_wanted` and the elements of `s_config_rumble_enabled`
atomic.
The CPU thread reads `s_is_adapter_wanted` and `s_config_rumble_enabled`
in `Output`, while the host thread writes to them in `RefreshConfig`.
The simplest way to trigger this race is to close the `Settings` window
while playing a game with the adapter active.
Accomplished using `run-clang-tidy` with `performance-move-const-arg,performance-unnecessary-value-param,modernize-pass-by-value`.
Changed arguments to const references, removed them where inappropriate (e.g. sink parameters). Same with std::move.
Manually reviewed each change to make sure that it makes sense, and do something more appropriate if possible.
Yellow squiggly lines begone!
Done automatically on .cpp files through `run-clang-tidy`, with manual corrections to the mistakes.
If an import is directly used, but is technically unnecessary since it's recursively imported by something else, it is *not* removed.
The tool doesn't touch .h files, so I did some of them by hand while fixing errors due to old recursive imports.
Not everything is removed, but the cleanup should be substantial enough.
Because this done on Linux, code that isn't used on it is mostly untouched.
(Hopefully no open PR is depending on these imports...)
This lets us get rid of the Reset call in ProcessInputPayload, which was
causing us some threading headaches (see 74ed5e5532). Instead we handle
disconnection in the same way as the libusb implementation does.
We can register a BroadcastReceiver to have Android tell us when a GC
adapter gets connected instead of having a loop where we continuously
call SleepCurrentThread(1000) and poll the current status. When waiting
for a GC adapter to connect, this both reduces power usage and improves
responsiveness.
Note that I made openAdapter get the UsbDevice that's been stored by the
hotplug code instead of having openAdapter find the UsbDevice on its own
like before. This is only because I want to ensure that the UsbDevice
being tracked for disconnection is the same as the UsbDevice actually
being used, in case the user has multiple adapters connected.
The read thread could call Reset, which in turn tried to join the read
thread, leading to a SIGABRT. This manifested as Dolphin consistently
crashing when disconnecting a GC adapter and having a chance of crashing
a few seconds after connecting a GC adapter.