@@ -53,6 +53,7 @@ extern const char *cbm_test_program_path;
5353#include <sys/wait.h>
5454#include <pwd.h>
5555#include <pthread.h>
56+ #include <time.h>
5657#include <unistd.h>
5758#ifdef __linux__
5859#include <sched.h> /* #1830 userns real smoke: unshare(CLONE_NEWUSER). */
@@ -3507,8 +3508,8 @@ TEST(daemon_ipc_posix_lifetime_reservation_concurrent_queries_preserve_kernel_lo
35073508 if (reservation ) {
35083509 probe_worker .endpoint = endpoint ;
35093510 acquire_worker .endpoint = endpoint ;
3510- probe_started = pthread_create ( & probe_thread , NULL , ipc_lifetime_concurrent_worker ,
3511- & probe_worker ) == 0 ;
3511+ probe_started =
3512+ pthread_create ( & probe_thread , NULL , ipc_lifetime_concurrent_worker , & probe_worker ) == 0 ;
35123513 acquire_started = pthread_create (& acquire_thread , NULL , ipc_lifetime_concurrent_worker ,
35133514 & acquire_worker ) == 0 ;
35143515 }
@@ -3567,6 +3568,180 @@ TEST(daemon_ipc_posix_lifetime_reservation_concurrent_queries_preserve_kernel_lo
35673568 PASS ();
35683569}
35693570
3571+ typedef struct {
3572+ const cbm_daemon_ipc_endpoint_t * endpoint ;
3573+ pthread_mutex_t mutex ;
3574+ pthread_cond_t condition ;
3575+ bool probe_parked ;
3576+ bool release_probe ;
3577+ atomic_bool acquire_started ;
3578+ atomic_bool acquire_done ;
3579+ cbm_daemon_ipc_lifetime_reservation_t * reservation ;
3580+ int acquire_result ;
3581+ int probe_result ;
3582+ } ipc_lifetime_interleave_t ;
3583+
3584+ static void ipc_test_pause_lifetime_probe (void * opaque ) {
3585+ ipc_lifetime_interleave_t * state = opaque ;
3586+ (void )pthread_mutex_lock (& state -> mutex );
3587+ state -> probe_parked = true;
3588+ (void )pthread_cond_broadcast (& state -> condition );
3589+ while (!state -> release_probe ) {
3590+ (void )pthread_cond_wait (& state -> condition , & state -> mutex );
3591+ }
3592+ (void )pthread_mutex_unlock (& state -> mutex );
3593+ }
3594+
3595+ static void * ipc_test_lifetime_probe_interleave (void * opaque ) {
3596+ ipc_lifetime_interleave_t * state = opaque ;
3597+ state -> probe_result = cbm_daemon_ipc_lifetime_reservation_probe (state -> endpoint );
3598+ return NULL ;
3599+ }
3600+
3601+ static void * ipc_test_lifetime_acquire_interleave (void * opaque ) {
3602+ ipc_lifetime_interleave_t * state = opaque ;
3603+ atomic_store_explicit (& state -> acquire_started , true, memory_order_release );
3604+ state -> acquire_result =
3605+ cbm_daemon_ipc_lifetime_reservation_try_acquire (state -> endpoint , & state -> reservation );
3606+ atomic_store_explicit (& state -> acquire_done , true, memory_order_release );
3607+ (void )pthread_mutex_lock (& state -> mutex );
3608+ (void )pthread_cond_broadcast (& state -> condition );
3609+ (void )pthread_mutex_unlock (& state -> mutex );
3610+ return NULL ;
3611+ }
3612+
3613+ TEST (daemon_ipc_posix_first_lifetime_acquire_serializes_with_probe ) {
3614+ static const char key [] = "a1a1b2b2c3c3d4d4" ;
3615+ char parent [TEST_PATH_CAP ] = {0 }, runtime_dir [TEST_PATH_CAP ] = {0 };
3616+ char lock_path [TEST_PATH_CAP ] = {0 };
3617+ cbm_daemon_ipc_endpoint_t * endpoint = NULL ;
3618+ ipc_lifetime_interleave_t state = {0 };
3619+ pthread_t probe_thread , acquire_thread ;
3620+ bool mutex_ready = false, condition_ready = false;
3621+ bool probe_started = false, acquire_started = false;
3622+ bool probe_joined = false, acquire_joined = false, acquire_blocked = false;
3623+ pid_t child = -1 ;
3624+ int child_status = -1 , result_pipe [2 ] = {-1 , -1 };
3625+ uint8_t child_result = IPC_TEST_LOCK_ERROR ;
3626+ int free_after_release = -1 ;
3627+
3628+ if (ipc_test_parent_new (parent , "first-lifetime-interleave" )) {
3629+ endpoint = cbm_daemon_ipc_endpoint_new (key , parent );
3630+ }
3631+ if (endpoint ) {
3632+ ipc_test_copy_path (runtime_dir , cbm_daemon_ipc_endpoint_runtime_dir (endpoint ));
3633+ state .endpoint = endpoint ;
3634+ }
3635+ if (endpoint && ipc_test_lifetime_lock_path (lock_path , runtime_dir , key ) &&
3636+ pthread_mutex_init (& state .mutex , NULL ) == 0 ) {
3637+ mutex_ready = true;
3638+ if (pthread_cond_init (& state .condition , NULL ) == 0 ) {
3639+ condition_ready = true;
3640+ atomic_init (& state .acquire_started , false);
3641+ atomic_init (& state .acquire_done , false);
3642+ cbm_daemon_ipc_posix_lifetime_probe_hook_set_for_test (ipc_test_pause_lifetime_probe ,
3643+ & state );
3644+ probe_started = pthread_create (& probe_thread , NULL , ipc_test_lifetime_probe_interleave ,
3645+ & state ) == 0 ;
3646+ if (probe_started ) {
3647+ (void )pthread_mutex_lock (& state .mutex );
3648+ while (!state .probe_parked ) {
3649+ (void )pthread_cond_wait (& state .condition , & state .mutex );
3650+ }
3651+ (void )pthread_mutex_unlock (& state .mutex );
3652+ acquire_started = pthread_create (& acquire_thread , NULL ,
3653+ ipc_test_lifetime_acquire_interleave , & state ) == 0 ;
3654+ if (acquire_started ) {
3655+ while (!atomic_load_explicit (& state .acquire_started , memory_order_acquire )) {}
3656+ struct timespec deadline ;
3657+ (void )clock_gettime (CLOCK_REALTIME , & deadline );
3658+ deadline .tv_nsec += 250000000L ;
3659+ if (deadline .tv_nsec >= 1000000000L ) {
3660+ deadline .tv_sec ++ ;
3661+ deadline .tv_nsec -= 1000000000L ;
3662+ }
3663+ (void )pthread_mutex_lock (& state .mutex );
3664+ int wait_status = 0 ;
3665+ while (!atomic_load_explicit (& state .acquire_done , memory_order_acquire ) &&
3666+ wait_status == 0 ) {
3667+ wait_status =
3668+ pthread_cond_timedwait (& state .condition , & state .mutex , & deadline );
3669+ }
3670+ acquire_blocked =
3671+ !atomic_load_explicit (& state .acquire_done , memory_order_acquire );
3672+ state .release_probe = true;
3673+ (void )pthread_cond_broadcast (& state .condition );
3674+ (void )pthread_mutex_unlock (& state .mutex );
3675+ } else {
3676+ (void )pthread_mutex_lock (& state .mutex );
3677+ state .release_probe = true;
3678+ (void )pthread_cond_broadcast (& state .condition );
3679+ (void )pthread_mutex_unlock (& state .mutex );
3680+ }
3681+ }
3682+ if (probe_started ) {
3683+ probe_joined = pthread_join (probe_thread , NULL ) == 0 ;
3684+ }
3685+ if (acquire_started ) {
3686+ acquire_joined = pthread_join (acquire_thread , NULL ) == 0 ;
3687+ }
3688+ cbm_daemon_ipc_posix_lifetime_probe_hook_set_for_test (NULL , NULL );
3689+ }
3690+ }
3691+ if (state .reservation && pipe (result_pipe ) == 0 ) {
3692+ child = fork ();
3693+ }
3694+ if (child == 0 ) {
3695+ (void )close (result_pipe [0 ]);
3696+ child_result = ipc_test_lock_child_verdict (lock_path );
3697+ bool sent = ipc_test_fd_write_all (result_pipe [1 ], & child_result , sizeof (child_result ));
3698+ (void )close (result_pipe [1 ]);
3699+ _exit (sent ? 0 : 1 );
3700+ }
3701+ if (child > 0 ) {
3702+ (void )close (result_pipe [1 ]);
3703+ result_pipe [1 ] = -1 ;
3704+ bool received = ipc_test_fd_read_all (result_pipe [0 ], & child_result , sizeof (child_result ));
3705+ (void )close (result_pipe [0 ]);
3706+ result_pipe [0 ] = -1 ;
3707+ if (!received ) {
3708+ child_result = IPC_TEST_LOCK_ERROR ;
3709+ }
3710+ while (waitpid (child , & child_status , 0 ) < 0 && errno == EINTR ) {}
3711+ }
3712+ cbm_daemon_ipc_lifetime_reservation_release (state .reservation );
3713+ if (endpoint ) {
3714+ free_after_release = cbm_daemon_ipc_lifetime_reservation_probe (endpoint );
3715+ }
3716+ for (size_t index = 0 ; index < 2 ; index ++ ) {
3717+ if (result_pipe [index ] >= 0 ) {
3718+ (void )close (result_pipe [index ]);
3719+ }
3720+ }
3721+ if (condition_ready ) {
3722+ (void )pthread_cond_destroy (& state .condition );
3723+ }
3724+ if (mutex_ready ) {
3725+ (void )pthread_mutex_destroy (& state .mutex );
3726+ }
3727+ cbm_daemon_ipc_endpoint_free (endpoint );
3728+ ipc_test_remove_tree (runtime_dir , parent );
3729+
3730+ ASSERT_TRUE (probe_started );
3731+ ASSERT_TRUE (acquire_started );
3732+ ASSERT_TRUE (probe_joined );
3733+ ASSERT_TRUE (acquire_joined );
3734+ ASSERT_EQ (state .probe_result , 0 );
3735+ ASSERT_EQ (state .acquire_result , 1 );
3736+ ASSERT_GT (child , 0 );
3737+ ASSERT_TRUE (WIFEXITED (child_status ));
3738+ ASSERT_EQ (WEXITSTATUS (child_status ), 0 );
3739+ ASSERT_EQ (child_result , IPC_TEST_LOCK_HELD );
3740+ ASSERT_TRUE (acquire_blocked );
3741+ ASSERT_EQ (free_after_release , 0 );
3742+ PASS ();
3743+ }
3744+
35703745TEST (daemon_ipc_posix_lifetime_reservation_probe_does_not_drop_held_lock ) {
35713746 static const char key [] = "c1c1d2d2e3e3f4f4" ;
35723747 char parent [TEST_PATH_CAP ] = {0 };
@@ -6163,6 +6338,7 @@ SUITE(daemon_ipc) {
61636338 RUN_TEST (daemon_ipc_posix_lifetime_reservation_rejects_fork_inheritance );
61646339 RUN_TEST (daemon_ipc_posix_lifetime_reservation_probe_does_not_drop_held_lock );
61656340 RUN_TEST (daemon_ipc_posix_lifetime_reservation_concurrent_queries_preserve_kernel_lock );
6341+ RUN_TEST (daemon_ipc_posix_first_lifetime_acquire_serializes_with_probe );
61666342 RUN_TEST (daemon_ipc_posix_lifetime_reservation_try_acquire_again_does_not_drop_held_lock );
61676343 RUN_TEST (daemon_ipc_posix_child_participant_handoff_retains_legacy_bridge );
61686344 RUN_TEST (daemon_ipc_posix_publication_boundaries_recover_from_crash );
0 commit comments