When we get an error from _umtx_wait_uint in the !_UMTX_OPTIMIZED case, relock the variable and do the decrement.
Signed-off-by: Warner Losh <[email protected]> --- bsd-user/freebsd/os-thread.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/bsd-user/freebsd/os-thread.c b/bsd-user/freebsd/os-thread.c index f90f13076f..a36c5ceccf 100644 --- a/bsd-user/freebsd/os-thread.c +++ b/bsd-user/freebsd/os-thread.c @@ -1264,6 +1264,7 @@ abi_long freebsd_rw_rdlock(abi_ulong target_addr, long fflag, size_t tsz, __get_user(blocked_readers, &target_urwlock->rw_blocked_readers); } + ret = 0; while (state & wrflags) { /* sleep/wait */ unlock_user_struct(target_urwlock, target_addr, 1); @@ -1274,7 +1275,11 @@ abi_long freebsd_rw_rdlock(abi_ulong target_addr, long fflag, size_t tsz, ret = _umtx_wait_uint(&target_urwlock->rw_state, tswap32(state), tsz, t, __func__); if (is_error(ret)) { - return ret; + if (!lock_user_struct(VERIFY_WRITE, target_urwlock, + target_addr, 0)) { + return ret; + } + goto rdlock_decrement; } if (!lock_user_struct(VERIFY_WRITE, target_urwlock, target_addr, 0)) { @@ -1284,6 +1289,7 @@ abi_long freebsd_rw_rdlock(abi_ulong target_addr, long fflag, size_t tsz, } /* decrease read waiter count */ +rdlock_decrement: __get_user(blocked_readers, &target_urwlock->rw_blocked_readers); while (!tcmpset_32(&target_urwlock->rw_blocked_readers, blocked_readers, (blocked_readers - 1))) { @@ -1297,6 +1303,9 @@ abi_long freebsd_rw_rdlock(abi_ulong target_addr, long fflag, size_t tsz, __get_user(state, &target_urwlock->rw_state); } } + if (is_error(ret)) { + return ret; + } } #endif /* _UMTX_OPTIMIZED */ } @@ -1365,6 +1374,7 @@ abi_long freebsd_rw_wrlock(abi_ulong target_addr, long fflag, size_t tsz, } /* sleep */ + ret = 0; while ((state & TARGET_URWLOCK_WRITE_OWNER) || (TARGET_URWLOCK_READER_COUNT(state) != 0)) { unlock_user_struct(target_urwlock, target_addr, 1); @@ -1375,7 +1385,11 @@ abi_long freebsd_rw_wrlock(abi_ulong target_addr, long fflag, size_t tsz, ret = _umtx_wait_uint(&target_urwlock->rw_state, tswap32(state), tsz, t, __func__); if (is_error(ret)) { - return ret; + if (!lock_user_struct(VERIFY_WRITE, target_urwlock, + target_addr, 0)) { + return ret; + } + goto wrlock_decrement; } if (!lock_user_struct(VERIFY_WRITE, target_urwlock, target_addr, 0)) { @@ -1385,6 +1399,7 @@ abi_long freebsd_rw_wrlock(abi_ulong target_addr, long fflag, size_t tsz, } /* decrease the write waiter count */ +wrlock_decrement: __get_user(blocked_writers, &target_urwlock->rw_blocked_writers); while (!tcmpset_32(&target_urwlock->rw_blocked_writers, blocked_writers, (blocked_writers - 1))) { @@ -1401,6 +1416,9 @@ abi_long freebsd_rw_wrlock(abi_ulong target_addr, long fflag, size_t tsz, } else { blocked_readers = 0; } + if (is_error(ret)) { + return ret; + } } #endif /* _UMTX_OPTIMIZED */ } -- 2.52.0
