feat: P2-3 repl_wait_ack() with pthread_cond_timedwait + ACK deadlock prevention
Implements sync replication wait: primary blocks until at least one non-catching-up standby ACKs the target LSN, or times out. Uses pthread_cond_timedwait with CLOCK_REALTIME deadline. Inner loop rechecks all standbys after each wakeup to guard against spurious wakeups. ACK deadlock prevention: repl_on_ack() is called exclusively from the io_worker read callback (not vtaskmgr), so it only does atomic_store + pthread_cond_signal — safe to call while vcpu threads block in repl_wait_ack(). 4 new TDD tests (all green, 9 total in test_repl_unit): - test_wait_ack_no_standbys_returns_enodev - test_wait_ack_already_acked - test_wait_ack_timeout (verifies >=80ms elapsed) - test_wait_ack_woken_by_background_ack (pthread thread signals after 30ms)
This commit is contained in:
parent
abefc8a128
commit
d326ee1b2e
2 changed files with 144 additions and 5 deletions
|
|
@ -201,12 +201,61 @@ int repl_broadcast(replicator_t *r, const wal_entry_hdr_t *hdr,
|
|||
|
||||
int repl_wait_ack(replicator_t *r, uint64_t target_lsn, uint32_t timeout_ms)
|
||||
{
|
||||
(void)r;
|
||||
(void)target_lsn;
|
||||
(void)timeout_ms;
|
||||
if (!r) return -EINVAL;
|
||||
|
||||
/* Full implementation in P2-3 */
|
||||
return -ENODEV;
|
||||
/* Check whether any non-catching-up standby exists */
|
||||
mylock_lock(&r->lock);
|
||||
int has_standby = 0;
|
||||
for (int i = 0; i < MAX_STANDBYS; i++) {
|
||||
if (r->standbys[i].ep != NULL &&
|
||||
!atomic_load_explicit(&r->standbys[i].in_catch_up,
|
||||
memory_order_acquire)) {
|
||||
has_standby = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mylock_unlock(&r->lock);
|
||||
|
||||
if (!has_standby) return -ENODEV;
|
||||
|
||||
/* Compute absolute deadline for pthread_cond_timedwait (CLOCK_REALTIME) */
|
||||
struct timespec deadline;
|
||||
clock_gettime(CLOCK_REALTIME, &deadline);
|
||||
deadline.tv_sec += (long)(timeout_ms / 1000);
|
||||
deadline.tv_nsec += (long)(timeout_ms % 1000) * 1000000L;
|
||||
if (deadline.tv_nsec >= 1000000000L) {
|
||||
deadline.tv_sec++;
|
||||
deadline.tv_nsec -= 1000000000L;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&r->ack_lock);
|
||||
int rc = -ETIMEDOUT;
|
||||
while (1) {
|
||||
/* Recheck every standby after each wakeup (spurious-wakeup guard) */
|
||||
mylock_lock(&r->lock);
|
||||
for (int i = 0; i < MAX_STANDBYS; i++) {
|
||||
standby_conn_t *s = &r->standbys[i];
|
||||
if (s->ep == NULL) continue;
|
||||
if (atomic_load_explicit(&s->in_catch_up, memory_order_acquire))
|
||||
continue;
|
||||
if (atomic_load_explicit(&s->acked_lsn, memory_order_acquire)
|
||||
>= target_lsn) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mylock_unlock(&r->lock);
|
||||
if (rc == 0) break;
|
||||
|
||||
int wait_rc = pthread_cond_timedwait(&r->ack_cond, &r->ack_lock,
|
||||
&deadline);
|
||||
if (wait_rc == ETIMEDOUT) {
|
||||
rc = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&r->ack_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue