We use flags in both locked paths and in public ones (to check if a
source is destroyed or running), but those checks are not using atomic
logic, thus they lead to races in various tests.
Fix them by atomically change and read the values.
And this fixes various tests in thread sanitizer.
In g_source_ref() we can just ensure that the value we've referenced was
valid, instead of checking it before, since if get to such state the
GSource is broken anyways, but at least we can avoid doing an unneeded
atomic read.
Reason why we don't bother resetting the reference in case of failure.
If going from 1 -> 0 references failed, then we should restart the unref
process, by potentially re-entering in the dispose function again if
instead something else re-references us and we're going to drop the last
one again.
During g_source_unref() we might end up calling dispose from
multiple threads, and due to the ref/unref dance we were doing we could
end up initiating a GSource finalization while another thread was about
to revive the source.
This was because we were unreffing a source in a thread, and potentially
re-reffing it, at the same time, but it was not guaranteed that the
final decrement and test couldn't be followed by a further re-ref.
To avoid this, do not do any ref/unref/re-ref/re-unref dance while we're
about to dispose, but instead follow a bit more the g_object_unref()
logic, and start the disposal phase only if we're about to drop the last
reference, and only after the potential disposal call is done, we do an
actual ref count decrement, which may lead to the finalization or not.
We don't bother following the same logic at later point, since after
disposal we should really have just one thread running and revival of a
finalizing GSource isn't supported anyways.
With this logic we can also avoid doing unneeded context locking when
we've enough references on a GSource that disposal is unlikely to
happen.
Closes: #3612
It pulls in windows.h causing conflict for code that assume windows.h is not
previously included.
Thanks to Luca Bacci for proposing this fix, and @Osyotr for pointing
out this issue!
Fixes issue #3613.
This improves test coverage, adding coverage for some lines which I
spotted were not covered while testing the preceding commits.
It doesn’t directly test the preceding commits, though.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
For long input strings, it would have been possible for `i` to overflow.
Avoid that problem by using the `tz_length` instead, so that we count up
rather than down.
This commit introduces no functional changes (outside of changing
undefined behaviour), and can be verified using the identity
`i === length - tz_length`.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
It’s guaranteed to be in (0, length] by the calculations above.
This avoids the possibility of integer overflow through `gssize` not
being as big as `size_t`.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
This one is much harder to trigger than the one in the previous commit,
but mixing `gssize` and `gsize` always runs the risk of the former
overflowing for very (very very) long input strings.
Avoid that possibility by not using the sign of the `tz_offset` to
indicate its validity, and instead using the return value of the
function.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
This will only happen with invalid (or maliciously invalid) potential
ISO8601 strings, but `g_date_time_new_from_iso8601()` needs to be robust
against that.
Prevent `length` overflowing by correctly defining it as a `size_t`.
Similarly for `date_length`, but additionally track its validity in a
boolean rather than as its sign.
Spotted by chamalsl as #YWH-PGM9867-43.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
It is easy to overlook that unreffing a GVolumeMonitor doesn't
disconnect signal handlers, this can lead to segfaults from dangling
user data pointers.
The documentation for g_spawn_async_with_pipes_and_fds() states:
> If an error occurs, child_pid, stdin_pipe_out, stdout_pipe_out, and
> stderr_pipe_out will not be filled with valid values.
Before 2dc3a6f0c80e5a8f786369eee0c45bfe19b55f4f, the `child_pid`
argument was `self->pid`, and GObject zero-initializes structs. So
the pid field was properly initialized to zero.
After 2dc3a6f0c80e5a8f786369eee0c45bfe19b55f4f, however, the out
variable is now declared inside initable_init(), and it's unitialized.
So if g_spawn_async_with_pipes_and_fds() errors out, `pid` will have
trash value in it, and the following assertion will fail:
```
g_assert (success == (pid != 0));
```
Fix that by initializing the `pid` variable to zero. Add a test to
exercise the fail code path, and prevent errors like this in the
future.
The process PID is initialized by the initable vfunc, while
g_subprocess_exited sets it again, when we're protecting it via a lock.
The status is set when the process exits instead, again while locking.
This makes the thread sanitizer unhappy (even if it shouldn't really be
a race for the PID init case), but still locking during initialization is
not a bad thing to do.
At the same time g_subprocess_wait() and friends were using the pid and status
values without any protection, so let's ensure this is not the case anymore.
WARNING: ThreadSanitizer: data race (pid=8213)
Write of size 4 at 0x7b200000084c by thread T1:
#0 g_subprocess_exited ../gio/gsubprocess.c:284
#1 g_child_watch_dispatch ../glib/gmain.c:5963
#2 g_main_dispatch ../glib/gmain.c:3373
#3 g_main_context_dispatch_unlocked ../glib/gmain.c:4224
#4 g_main_context_iterate_unlocked ../glib/gmain.c:4289
#5 g_main_context_iteration ../glib/gmain.c:4354
#6 glib_worker_main ../glib/gmain.c:6553
#7 g_thread_proxy ../glib/gthread.c:892
Previous read of size 4 at 0x7b200000084c by main thread:
#0 g_subprocess_wait ../gio/gsubprocess.c:908
#1 g_subprocess_wait_check ../gio/gsubprocess.c:939
#2 end_element ../gio/glib-compile-resources.c:342
#3 emit_end_element ../glib/gmarkup.c:1045
#4 g_markup_parse_context_parse ../glib/gmarkup.c:1603
#5 parse_resource_file ../gio/glib-compile-resources.c:578
#6 main ../gio/glib-compile-resources.c:967
Location is heap block of size 120 at 0x7b2000000800 allocated by main
thread:
#0 calloc <null>
#1 g_malloc0 ../glib/gmem.c:133
#2 g_type_create_instance ../gobject/gtype.c:1933
#3 g_object_new_internal ../gobject/gobject.c:2621
#4 g_object_new_valist ../gobject/gobject.c:2960
#5 g_initable_new_valist ../gio/ginitable.c:245
#6 g_initable_new ../gio/ginitable.c:163
#7 g_subprocess_newv ../gio/gsubprocess.c:619
#8 g_subprocess_new ../gio/gsubprocess.c:590
#9 end_element ../gio/glib-compile-resources.c:334
#10 emit_end_element ../glib/gmarkup.c:1045
#11 g_markup_parse_context_parse ../glib/gmarkup.c:1603
#12 parse_resource_file ../gio/glib-compile-resources.c:578
#13 main ../gio/glib-compile-resources.c:967
Thread T1 'gmain' (tid=8228, running) created by main thread at:
#0 pthread_create <null>
#1 g_system_thread_new ../glib/gthread-posix.c:762
#2 g_thread_new_internal ../glib/gthread.c:996
#3 g_thread_new ../glib/gthread.c:949
#4 g_get_worker_context ../glib/gmain.c:6580
#5 initable_init ../gio/gsubprocess.c:443
#6 g_initable_init ../gio/ginitable.c:129
#7 g_initable_new_valist ../gio/ginitable.c:249
#8 g_initable_new ../gio/ginitable.c:163
#9 g_subprocess_newv ../gio/gsubprocess.c:619
#10 g_subprocess_new ../gio/gsubprocess.c:590
#11 end_element ../gio/glib-compile-resources.c:334
#12 emit_end_element ../glib/gmarkup.c:1045
#13 g_markup_parse_context_parse ../glib/gmarkup.c:1603
#14 parse_resource_file ../gio/glib-compile-resources.c:578
#15 main ../gio/glib-compile-resources.c:967
SUMMARY: ThreadSanitizer: data race ../gio/gsubprocess.c:284 in
g_subprocess_exited
======================================
WARNING: ThreadSanitizer: data race (pid=15959)
Read of size 4 at 0x7b200000084c by main thread:
#0 g_subprocess_wait ../gio/gsubprocess.c:913
#1 g_subprocess_wait_check ../gio/gsubprocess.c:944
#2 test_cat_utf8 ../gio/tests/gsubprocess.c:489
#3 test_case_run ../glib/gtestutils.c:3115
#4 g_test_run_suite_internal ../glib/gtestutils.c:3210
#5 g_test_run_suite_internal ../glib/gtestutils.c:3229
#6 g_test_run_suite ../glib/gtestutils.c:3310
#7 g_test_run ../glib/gtestutils.c:2379
#8 main ../gio/tests/gsubprocess.c:2266
Previous write of size 4 at 0x7b200000084c by thread T1:
#0 g_subprocess_exited ../gio/gsubprocess.c:284
#1 g_child_watch_dispatch ../glib/gmain.c:5963
#2 g_main_dispatch ../glib/gmain.c:3373
#3 g_main_context_dispatch_unlocked ../glib/gmain.c:4224
#4 g_main_context_iterate_unlocked ../glib/gmain.c:4289
#5 g_main_context_iteration ../glib/gmain.c:4354
#6 glib_worker_main ../glib/gmain.c:6553
#7 g_thread_proxy ../glib/gthread.c:892
This is a minor performance improvement, since the pspec list for the
class now only has to be modified once, rather than twice.
It also means we now have the `GParamSpec` pointers to hand in a `props`
array, which will be used in the upcoming commits.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
This lets the compiler tell us if we’ve accidentally missed a property
implementation from `get_property()` or `set_property()`.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
As per previous commit we used atomic logic to handle the cancellation,
but that lead to a slightly different behavior because the file monitor
was then marked as cancelled before the vfunc implementation was called.
Use similar behavior now (by still relying on the atomic logic), by
marking the state as about-to-cancel as soon as we're starting the
cancellation (preventing other threads to cancel it), and eventually
fully marking it as cancelled.
The cancelled state may be set and read by different threads, so ensure
that it's stored and managed in an atomic way.
We could in fact end up check for `g_file_monitor_is_cancelled()` in a
thread and `g_file_monitor_cancel()` or `g_file_monitor_emit_event` in
in another one.