changeset 868:b64b931179e4

synch: add MXTRYLOCK There are times when having a try-lock is the best way to solve a problem. Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Wed, 31 Jan 2024 13:51:14 -0500
parents 3ededa985664
children 82994f2d7411
files include/jeffpc/synch.h mapfile-vers synch.c tests/.hgignore tests/CMakeLists.txt tests/test_mutex-trylock-held-class.c tests/test_mutex-trylock-null.c tests/test_mutex-trylock.c
diffstat 8 files changed, 203 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/include/jeffpc/synch.h	Mon Jan 29 22:40:35 2024 -0500
+++ b/include/jeffpc/synch.h	Wed Jan 31 13:51:14 2024 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2019,2023 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ * Copyright (c) 2011-2019,2023-2024 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -100,6 +100,14 @@
 				}; \
 				mxlock(&mx_ctx, (l)); \
 			} while (0)
+#define MXTRYLOCK(l)	({ \
+				struct lock_context mx_ctx = { \
+					.lockname = #l, \
+					.file = __FILE__, \
+					.line = __LINE__, \
+				}; \
+				mxtrylock(&mx_ctx, (l)); \
+			})
 #define MXUNLOCK(l)	do { \
 				struct lock_context mx_ctx = { \
 					.lockname = #l, \
@@ -213,6 +221,7 @@
 		   struct lock_class *lc);
 extern void mxdestroy(const struct lock_context *where, struct lock *m);
 extern void mxlock(const struct lock_context *where, struct lock *m);
+extern int mxtrylock(const struct lock_context *where, struct lock *m);
 extern void mxunlock(const struct lock_context *where, struct lock *m);
 
 extern void rwinit(const struct lock_context *where, struct rwlock *l,
--- a/mapfile-vers	Mon Jan 29 22:40:35 2024 -0500
+++ b/mapfile-vers	Wed Jan 31 13:51:14 2024 -0500
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2016-2023 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+# Copyright (c) 2016-2024 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
 #
 # Permission is hereby granted, free of charge, to any person obtaining a copy
 # of this software and associated documentation files (the "Software"), to deal
@@ -282,6 +282,7 @@
 		mxdestroy;
 		mxinit;
 		mxlock;
+		mxtrylock;
 		mxunlock;
 		rwdestroy;
 		rwinit;
--- a/synch.c	Mon Jan 29 22:40:35 2024 -0500
+++ b/synch.c	Wed Jan 31 13:51:14 2024 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2021,2023 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ * Copyright (c) 2011-2021,2023-2024 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -601,13 +601,17 @@
 	/* keep the synch type set to aid debugging */
 }
 
-static void verify_lock_lock(const struct lock_context *where, struct lock *l)
+static void verify_lock_lock(const struct lock_context *where, struct lock *l,
+			     bool trylock)
 {
 	if (!l)
-		print_invalid_call("MXLOCK", where);
+		print_invalid_call(trylock ? "MXTRYLOCK" : "MXLOCK", where);
 
-	check_magic(&l->info, "acquire", where, SYNCH_TYPE_MUTEX);
-	check_unheld_for_lock(&l->info, where, false);
+	check_magic(&l->info, trylock ? "try-acquire" : "acquire", where,
+		    SYNCH_TYPE_MUTEX);
+
+	if (!trylock)
+		check_unheld_for_lock(&l->info, where, false);
 }
 
 static void verify_lock_unlock(const struct lock_context *where, struct lock *l)
@@ -801,7 +805,7 @@
 {
 	int ret;
 
-	verify_lock_lock(where, l);
+	verify_lock_lock(where, l, false);
 
 	ret = pthread_mutex_lock(&l->lock);
 	if (ret)
@@ -810,6 +814,25 @@
 		      (ret == EDEADLK) ? "Deadlock" : strerror(ret));
 }
 
+int mxtrylock(const struct lock_context *where, struct lock *l)
+{
+	int ret;
+
+	verify_lock_lock(where, l, true);
+
+	ret = pthread_mutex_trylock(&l->lock);
+	if (ret && (ret != EBUSY))
+		panic("mutex trylock failed @ %s:%d: %s",
+		      where->file, where->line,
+		      strerror(ret));
+
+	/* on lock acquisition, sanity check everything */
+	if (!ret)
+		check_unheld_for_lock(&l->info, where, false);
+
+	return -ret;
+}
+
 void mxunlock(const struct lock_context *where, struct lock *l)
 {
 	int ret;
--- a/tests/.hgignore	Mon Jan 29 22:40:35 2024 -0500
+++ b/tests/.hgignore	Wed Jan 31 13:51:14 2024 -0500
@@ -33,6 +33,9 @@
 test_mutex-lock-held-nesting
 test_mutex-lock-memcpy
 test_mutex-lock-null
+test_mutex-trylock
+test_mutex-trylock-held-class
+test_mutex-trylock-null
 test_mutex-unlock-destroyed
 test_mutex-unlock-memcpy
 test_mutex-unlock-null
--- a/tests/CMakeLists.txt	Mon Jan 29 22:40:35 2024 -0500
+++ b/tests/CMakeLists.txt	Wed Jan 31 13:51:14 2024 -0500
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2016-2020,2022 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+# Copyright (c) 2016-2020,2022,2024 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
 #
 # Permission is hereby granted, free of charge, to any person obtaining a copy
 # of this software and associated documentation files (the "Software"), to deal
@@ -54,6 +54,8 @@
 build_test_bin_and_run(mutex-lock-held-nesting)
 build_test_bin_and_run(mutex-lock-memcpy)
 build_test_bin_and_run(mutex-lock-null)
+build_test_bin_and_run(mutex-trylock)
+build_test_bin_and_run(mutex-trylock-null)
 build_test_bin_and_run(mutex-unlock-destroyed)
 build_test_bin_and_run(mutex-unlock-memcpy)
 build_test_bin_and_run(mutex-unlock-null)
@@ -65,6 +67,7 @@
 build_test_bin_and_run(mutex-lock-held-multi-first)
 build_test_bin_and_run(mutex-lock-held-multi-second)
 build_test_bin_and_run(mutex-lock-held-multi-third)
+build_test_bin_and_run(mutex-trylock-held-class)
 build_test_bin_and_run(mutex-unlock-unheld)
 endif()
 build_test_bin_and_run(nvl)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test_mutex-trylock-held-class.c	Wed Jan 31 13:51:14 2024 -0500
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2024 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <jeffpc/synch.h>
+#include <jeffpc/thread.h>
+
+#include "test.c"
+
+void test(void)
+{
+	LOCK_CLASS(lc);
+	struct lock a;
+	struct lock b;
+	int ret;
+
+	MXINIT(&a, &lc);
+	MXINIT(&b, &lc);
+
+	MXLOCK(&a);
+
+	ret = MXTRYLOCK(&a);
+	if (ret != -EBUSY)
+		fail("MXTRYLOCK returned %d (%s), expected -EBUSY (%d)", ret,
+		     xstrerror(ret), -EBUSY);
+
+	/* generates a possible recursive locking detected warning */
+	/* TODO: can we catch it somehow? */
+	ret = MXTRYLOCK(&b);
+	if (ret)
+		fail("MXTRYLOCK returned %d (%s), expected 0", ret,
+		     xstrerror(ret));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test_mutex-trylock-null.c	Wed Jan 31 13:51:14 2024 -0500
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2024 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <jeffpc/synch.h>
+
+#include "test.c"
+
+void test(void)
+{
+	test_set_panic_string("lockdep: invalid call to MXTRYLOCK");
+
+	MXTRYLOCK(NULL);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test_mutex-trylock.c	Wed Jan 31 13:51:14 2024 -0500
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2024 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <jeffpc/synch.h>
+#include <jeffpc/thread.h>
+
+#include "test.c"
+
+static void *busy_lock_test(void *v)
+{
+	int ret;
+
+	fprintf(stderr, "Trying MXTRYLOCK from %p...", xthr_self());
+
+	ret = MXTRYLOCK(v);
+
+	fprintf(stderr, "%s (%d)\n", xstrerror(ret), ret);
+
+	if (ret != -EBUSY)
+		fail("MXTRYLOCK returned %d (%s), expected -EBUSY (%d)", ret,
+		     xstrerror(ret), -EBUSY);
+
+	return NULL;
+}
+
+void test(void)
+{
+	pthread_t thread;
+	LOCK_CLASS(lc);
+	struct lock a;
+	int ret;
+
+	MXINIT(&a, &lc);
+
+	/* lock that's free */
+	ret = MXTRYLOCK(&a);
+	if (ret)
+		fail("MXTRYLOCK returned %d (%s), expected 0", ret,
+		     xstrerror(ret));
+
+	/* lock that's busy - same thread */
+	busy_lock_test(&a);
+
+	/* lock that's busy - different thread */
+	ret = xthr_create(&thread, busy_lock_test, &a);
+	if (ret)
+		fail("xthr_create failed to create a thread: %s",
+		     xstrerror(ret));
+
+	ret = xthr_join(thread, NULL);
+	if (ret)
+		fail("xthr_join failed to join a thread: %s", xstrerror(ret));
+}