task_get_flags(), task_set_flags(), task_get_progress() and
task_set_progress() each took the process-wide property_lock, so every
handler polling for cancellation and every progress update queued
behind every other task's, and behind the progress report formatting a
title under the same lock. The queue itself took it again to cancel,
to settle a task and to test for a main-thread or detachable one.
retro_task_t::flags and ::progress are retro_atomic_int_t now. Reads
are an acquire load, progress a release store, and flag updates an
atomic fetch-or / fetch-and, so concurrent updates of different bits
cannot lose one another. Only a backend whose read-modify-write is not
atomic (RETRO_ATOMIC_LOCK_FREE undefined) still takes property_lock,
and only around the flag update.
Title and error stay under property_lock: their buffers are freed and
replaced, which an atomic pointer would not make safe. The snapshot
and the progress report read the flags before the strings, so a task
seen finished is seen with the error it set before finishing.
Code that touched the two fields on a task already pushed goes through
the accessors: the progress callbacks, the cloud sync handler, the
emscripten HTTP handler and the cheevos badge tasks. Setting them on a
task not yet pushed is unchanged.
task_queue_snapshot_test covers both properties: bits flipped from two
threads with a third held, and a finished task always snapshotting
with its error. retro_task_threaded_gather drops from 8 lock
acquisitions to 6 and the budget reference moves with it.