diff --git a/hphp/runtime/ext/simplelock/ext_simplelock.cpp b/hphp/runtime/ext/simplelock/ext_simplelock.cpp index 49caad6568f70f..7cbaec8d2e4078 100644 --- a/hphp/runtime/ext/simplelock/ext_simplelock.cpp +++ b/hphp/runtime/ext/simplelock/ext_simplelock.cpp @@ -134,6 +134,11 @@ void HHVM_FUNCTION(unlock_mutex, const String& name) { unlock(l); } +bool HHVM_FUNCTION(is_held, const String& name) { + auto const l = get_lock(name.toCppString()); + return l->load(std::memory_order_relaxed) != UNLOCKED; +} + struct SimpleLockExtension final : Extension { SimpleLockExtension() : Extension("simplelock", "1.0", "sandbox_infra") {} @@ -142,6 +147,7 @@ struct SimpleLockExtension final : Extension { void moduleRegisterNative() override { HHVM_NAMED_FE(HH\\SimpleLock\\lock, HHVM_FN(lock_mutex)); HHVM_NAMED_FE(HH\\SimpleLock\\try_lock, HHVM_FN(try_lock_mutex)); + HHVM_NAMED_FE(HH\\SimpleLock\\is_held, HHVM_FN(is_held)); HHVM_NAMED_FE_STR( "HH\\SimpleLock\\unlock", HHVM_FN(unlock_mutex), nativeFuncs() ); diff --git a/hphp/runtime/ext/simplelock/ext_simplelock.php b/hphp/runtime/ext/simplelock/ext_simplelock.php index c72b7348f1aa29..4e2f2e4c257915 100644 --- a/hphp/runtime/ext/simplelock/ext_simplelock.php +++ b/hphp/runtime/ext/simplelock/ext_simplelock.php @@ -37,6 +37,16 @@ function unlock(string $name): void; <<__Native>> function try_lock(string $name): bool; +/* + * Checks if any thread (including the current request) is holding a lock with a + * unique name. + * + * @param string $name - the name of the lock + * @return bool - whether someone is holding the lock + */ +<<__Native>> +function is_held(string $name): bool; + /* * Acquired a cross-request mutual exclusion lock with a unique name and a * timeout. diff --git a/hphp/test/slow/simplelock/isheld.php b/hphp/test/slow/simplelock/isheld.php new file mode 100644 index 00000000000000..f4b007b9df7bcc --- /dev/null +++ b/hphp/test/slow/simplelock/isheld.php @@ -0,0 +1,61 @@ +> +async function main() { + if (HH\execution_context() === "xbox") return; + + await HH\SimpleLock\lock('main_lock1'); + await HH\SimpleLock\lock('main_lock2'); + await HH\SimpleLock\lock('sync'); + apc_store('threads', 0); + + $func = fb_call_user_func_async( + __FILE__, + 'thread_main' + ); + + $_ = true; + while (apc_fetch('threads', inout $_) !== 1) usleep(10); + + echo 'main: '; var_dump(HH\SimpleLock\is_held('main_lock1')); + echo 'main: '; var_dump(HH\SimpleLock\is_held('main_lock2')); + echo 'main: '; var_dump(HH\SimpleLock\is_held('thread_lock1')); + echo 'main: '; var_dump(HH\SimpleLock\is_held('thread_lock2')); + + HH\SimpleLock\unlock('sync'); + fb_end_user_func_async($func); + + HH\SimpleLock\unlock('main_lock2'); + await HH\SimpleLock\lock('end_lock'); + + echo 'main: '; var_dump(HH\SimpleLock\is_held('main_lock1')); + echo 'main: '; var_dump(HH\SimpleLock\is_held('main_lock2')); + echo 'main: '; var_dump(HH\SimpleLock\is_held('thread_lock1')); + echo 'main: '; var_dump(HH\SimpleLock\is_held('thread_lock2')); + + echo "Main done.\n"; +} + diff --git a/hphp/test/slow/simplelock/isheld.php.expect b/hphp/test/slow/simplelock/isheld.php.expect new file mode 100644 index 00000000000000..1477831ca668fe --- /dev/null +++ b/hphp/test/slow/simplelock/isheld.php.expect @@ -0,0 +1,14 @@ +thread +thread: bool(true) +thread: bool(true) +thread: bool(true) +thread: bool(true) +main: bool(true) +main: bool(true) +main: bool(true) +main: bool(true) +main: bool(true) +main: bool(false) +main: bool(false) +main: bool(false) +Main done. diff --git a/hphp/test/slow/simplelock/isheld.php.norepo b/hphp/test/slow/simplelock/isheld.php.norepo new file mode 100644 index 00000000000000..e69de29bb2d1d6