Mercurial > libjeffpc
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)); +}