[folks] Add test cases for mutexing for async calls from the same thread.
- From: Jeremy Whiting <jpwhiting src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [folks] Add test cases for mutexing for async calls from the same thread.
- Date: Mon, 23 Jul 2012 17:26:52 +0000 (UTC)
commit 1492a80a0a05988b32b1d49895002a738c68ebfc
Author: Travis Reitter <travis reitter collabora co uk>
Date: Wed Jul 6 11:09:26 2011 -0700
Add test cases for mutexing for async calls from the same thread.
The standard Vala lock() call is a recursive lock, so it only prevents
concurrent access between different threads. But we rarely run in
distinct threads, so this test uses a more-complete solution that we can
depend upon (since the test will fail if its assumptions ever change in
Vala).
Helps: bgo#652637 - Don't hold locks across async calls
tests/folks/Makefile.am | 6 ++
tests/folks/async-locking.vala | 160 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 166 insertions(+), 0 deletions(-)
---
diff --git a/tests/folks/Makefile.am b/tests/folks/Makefile.am
index 62bd9df..470485e 100644
--- a/tests/folks/Makefile.am
+++ b/tests/folks/Makefile.am
@@ -45,6 +45,7 @@ AM_VALAFLAGS = \
# in order from least to most complex
noinst_PROGRAMS = \
abstract-field-details \
+ async-locking \
utils \
backend-loading \
aggregation \
@@ -79,6 +80,10 @@ abstract_field_details_SOURCES = \
abstract-field-details.vala \
$(NULL)
+async_locking_SOURCES = \
+ async-locking.vala \
+ $(NULL)
+
utils_SOURCES = \
utils.vala \
$(NULL)
@@ -106,6 +111,7 @@ MAINTAINERCLEANFILES = \
backend_loading_vala.stamp \
aggregation_vala.stamp \
abstract_field_details_vala.stamp \
+ async_locking_vala.stamp \
utils_vala.stamp \
avatar_cache_vala.stamp \
object_cache_vala.stamp \
diff --git a/tests/folks/async-locking.vala b/tests/folks/async-locking.vala
new file mode 100644
index 0000000..9957031
--- /dev/null
+++ b/tests/folks/async-locking.vala
@@ -0,0 +1,160 @@
+using Gee;
+using GLib;
+
+public class AsyncLockingTests : Folks.TestCase
+{
+ int _counter;
+ bool _counter_increment_pending;
+ int _calls_pending;
+ int _total_calls;
+ MainLoop _loop;
+
+ public AsyncLockingTests ()
+ {
+ base ("AsyncLocking");
+ this.add_test ("many concurrent funcs", this.test_many_concurrent_funcs);
+ this.add_test ("many concurrent funcs (safe)",
+ this.test_many_concurrent_funcs_safe);
+ }
+
+ public override void set_up ()
+ {
+ this._counter = 0;
+ this._calls_pending = 0;
+ this._counter_increment_pending = false;
+ }
+
+ public override void tear_down ()
+ {
+ }
+
+ public void test_many_concurrent_funcs ()
+ {
+ _loop = new GLib.MainLoop (null, false);
+ this._total_calls = 100;
+
+ Idle.add (() =>
+ {
+ for (var i = 0; i < this._total_calls; i++)
+ {
+ this._calls_pending++;
+ locked_increment.begin (increment_handler);
+ }
+
+ return false;
+ });
+
+ _loop.run ();
+ }
+
+ private void increment_handler (GLib.Object? source, GLib.AsyncResult result)
+ {
+ locked_increment.end (result);
+
+ /* calls expect the end state when they reach this point */
+ assert (this._counter >= 1);
+
+ this._calls_pending--;
+ if (this._calls_pending == 0)
+ {
+ print ("\n final counter value: %d " +
+ "(would be 1 if this weren't intentionally broken)\n",
+ this._counter);
+ this._loop.quit ();
+ }
+ }
+
+ private async void locked_increment ()
+ {
+ lock (this._counter)
+ {
+ if (this._counter < 1)
+ {
+ /* In this unsafe version, all the async calls will reach this
+ * point (despite the fact that they're all in the same thread).
+ * Uncomment the print() call below to verify. */
+
+ yield simulate_work ();
+
+ /* XXX: uncomment for debugging
+ print (" %3d -> %3d\n", this._counter, this._counter + 1);
+ */
+ this._counter++;
+ }
+ }
+ }
+
+ public void test_many_concurrent_funcs_safe ()
+ {
+ _loop = new GLib.MainLoop (null, false);
+ this._total_calls = 100;
+ this._calls_pending = 0;
+ this._counter_increment_pending = false;
+
+ Idle.add (() =>
+ {
+ for (var i = 0; i < this._total_calls; i++)
+ {
+ this._calls_pending++;
+ locked_increment_safe.begin (increment_handler_safe);
+ }
+
+ return false;
+ });
+
+ _loop.run ();
+ }
+
+ private void increment_handler_safe (GLib.Object? source,
+ GLib.AsyncResult result)
+ {
+ locked_increment_safe.end (result);
+
+ /* calls "ignored" while the first is in-flight still expect the end state
+ * when they reach this point */
+ assert (this._counter == 1);
+
+ this._calls_pending--;
+ if (this._calls_pending == 0)
+ {
+ print ("\n final counter value: %d\n", this._counter);
+ assert (this._counter == 1);
+
+ this._loop.quit ();
+ }
+ }
+
+ private async void locked_increment_safe ()
+ {
+ lock (this._counter)
+ {
+ if (this._counter < 1 && !this._counter_increment_pending)
+ {
+ this._counter_increment_pending = true;
+ yield simulate_work ();
+
+ /* XXX: uncomment for debugging
+ print (" %3d -> %3d\n", this._counter, this._counter + 1);
+ */
+ this._counter++;
+ }
+ }
+ }
+
+ private async void simulate_work ()
+ {
+ Thread.usleep (50 * 1000);
+ }
+}
+
+public int main (string[] args)
+{
+ Test.init (ref args);
+
+ TestSuite root = TestSuite.get_root ();
+ root.add_suite (new AsyncLockingTests ().get_suite ());
+
+ Test.run ();
+
+ return 0;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]