From c0c9da4f3c88394000eac3ea9611334f7c30fe3f Mon Sep 17 00:00:00 2001
From: Brandon Nesterenko <brandon.nesterenko@mariadb.com>
Date: Thu, 19 Jun 2025 10:28:49 -0600
Subject: [PATCH] B additions

---
 .../r/rpl_semi_sync_cond_var_per_thd.result   |  66 ++++++++++-
 .../rpl/t/rpl_semi_sync_cond_var_per_thd.test | 107 +++++++++++++++++-
 sql/semisync_master.cc                        |  11 ++
 3 files changed, 180 insertions(+), 4 deletions(-)

diff --git a/mysql-test/suite/rpl/r/rpl_semi_sync_cond_var_per_thd.result b/mysql-test/suite/rpl/r/rpl_semi_sync_cond_var_per_thd.result
index 18ad5d3d2cc..5c6abac7b97 100644
--- a/mysql-test/suite/rpl/r/rpl_semi_sync_cond_var_per_thd.result
+++ b/mysql-test/suite/rpl/r/rpl_semi_sync_cond_var_per_thd.result
@@ -7,6 +7,8 @@ call mtr.add_suppression("Could not read packet");
 call mtr.add_suppression("Could not write packet");
 set @save_bgc_count= @@global.binlog_commit_wait_count;
 set @save_bgc_usec= @@global.binlog_commit_wait_usec;
+set @old_master_wait_point= @@global.rpl_semi_sync_master_wait_point;
+set @old_master_wait_no_slave= @@global.rpl_semi_sync_master_wait_no_slave;
 set @save_debug_dbug= @@global.debug_dbug;
 set @@global.binlog_commit_wait_count=3;
 set @@global.binlog_commit_wait_usec=10000000;
@@ -46,8 +48,6 @@ drop table t1, t2, t3;
 # the binlogging to semi-sync, and starting the wait for ACK; and during
 # this pause, semi-sync is manually switched off and on.
 connection master;
-set @old_master_wait_point= @@global.rpl_semi_sync_master_wait_point;
-set @old_master_wait_no_slave= @@global.rpl_semi_sync_master_wait_no_slave;
 set @@global.rpl_semi_sync_master_wait_point= AFTER_SYNC;
 set @@global.rpl_semi_sync_master_wait_no_slave= 1;
 set @@global.debug_dbug="+d,semisync_log_skip_trx_wait";
@@ -100,7 +100,69 @@ commit;
 # Cleanup
 connection master;
 drop table tn;
+set @@global.debug_dbug=@save_debug_dbug;
+#
+# MDEV-36934
+# The server could indefinitely hang due to a memory leak which tried to
+# pthread signal on a destroyed condition variable. In effect, no
+# connections could commit transactions because there would be a thread
+# stuck on a never-returning call to pthread_cond_signal() while
+# holding Repl_semi_sync_master::LOCK_log.
+connection master;
+set @@global.rpl_semi_sync_master_wait_point= AFTER_COMMIT;
+set @@global.rpl_semi_sync_master_wait_no_slave= 0;
+# Ensure servers are in proper state
+connection master;
+connection slave;
+# Test case initial set-up
+connection master;
+create table t_36934 (a int) engine=innodb;
+include/save_master_gtid.inc
+connection slave;
+include/sync_with_master_gtid.inc
+# Pause the user transaction before inserting into Active_tranx
+connect user_con,localhost,root,,;
+set debug_dbug="+d,semisync_pause_at_write_tranx_in_binlog";
+insert into t_36934 values (1);
+connection server_1;
+set debug_sync="now wait_for at_write_tranx_in_binlog";
+# Disconnect the slave (note that the binlog dump thread won't yet be
+# notified of a binlog update from the last transaction, so the slave
+# should neither receiver nor ACK the transaction).
+connection slave;
+include/stop_slave.inc
+# Waiting for master to realize the slave has disconnected..
+connection server_1;
+# ..done
+# Resuming transaction (it will exit commit_trx early without waiting)
+set debug_sync="now signal resume_write_tranx_in_binlog";
+connection user_con;
+disconnect user_con;
+# Force delete the user thread (FLUSH THREADS ensures the thread won't
+# stay in the thread cache)
+connection master;
+FLUSH THREADS;
+# BUG: Re-connect slave. MDEV-36934 reports that the master would hang
+# when the slave would re-connect and try to ACK the last transaction
+# who's thread has been deleted
+connection slave;
+set @@global.debug_dbug=@save_debug_dbug;
+include/start_slave.inc
+# Try to commit another transaction (prior to MDEV-36934 fixes, this
+# would hang indefinitely)
+connection master;
+set @@global.debug_dbug=@save_debug_dbug;
+set debug_dbug=@save_debug_dbug;
+set debug_sync="RESET";
+insert into t_36934 values (2);
+connection server_1;
+# Waiting 5s for last query to complete..
+connection master;
+# ..done
+# Cleanup
+connection master;
 set @@global.rpl_semi_sync_master_wait_point= @old_master_wait_point;
 set @@global.rpl_semi_sync_master_wait_no_slave= @old_master_wait_no_slave;
 set @@global.debug_dbug=@save_debug_dbug;
+drop table t_36934;
 include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_semi_sync_cond_var_per_thd.test b/mysql-test/suite/rpl/t/rpl_semi_sync_cond_var_per_thd.test
index 537a4bf7fa8..34b857486b4 100644
--- a/mysql-test/suite/rpl/t/rpl_semi_sync_cond_var_per_thd.test
+++ b/mysql-test/suite/rpl/t/rpl_semi_sync_cond_var_per_thd.test
@@ -34,6 +34,8 @@ call mtr.add_suppression("Could not read packet");
 call mtr.add_suppression("Could not write packet");
 set @save_bgc_count= @@global.binlog_commit_wait_count;
 set @save_bgc_usec= @@global.binlog_commit_wait_usec;
+set @old_master_wait_point= @@global.rpl_semi_sync_master_wait_point;
+set @old_master_wait_no_slave= @@global.rpl_semi_sync_master_wait_no_slave;
 set @save_debug_dbug= @@global.debug_dbug;
 set @@global.binlog_commit_wait_count=3;
 set @@global.binlog_commit_wait_usec=10000000;
@@ -98,8 +100,6 @@ drop table t1, t2, t3;
 
 
 --connection master
-set @old_master_wait_point= @@global.rpl_semi_sync_master_wait_point;
-set @old_master_wait_no_slave= @@global.rpl_semi_sync_master_wait_no_slave;
 set @@global.rpl_semi_sync_master_wait_point= AFTER_SYNC;
 set @@global.rpl_semi_sync_master_wait_no_slave= 1;
 --eval set @@global.debug_dbug="+d,semisync_log_skip_trx_wait"
@@ -190,8 +190,111 @@ commit;
 --echo # Cleanup
 --connection master
 drop table tn;
+set @@global.debug_dbug=@save_debug_dbug;
+
+
+--echo #
+--echo # MDEV-36934
+--echo # The server could indefinitely hang due to a memory leak which tried to
+--echo # pthread signal on a destroyed condition variable. In effect, no
+--echo # connections could commit transactions because there would be a thread
+--echo # stuck on a never-returning call to pthread_cond_signal() while
+--echo # holding Repl_semi_sync_master::LOCK_log.
+
+--connection master
+set @@global.rpl_semi_sync_master_wait_point= AFTER_COMMIT;
+set @@global.rpl_semi_sync_master_wait_no_slave= 0;
+
+--echo # Ensure servers are in proper state
+--connection master
+let $status_var= rpl_semi_sync_master_status;
+let $status_var_value= ON;
+source include/wait_for_status_var.inc;
+--connection slave
+let $status_var= rpl_semi_sync_slave_status;
+let $status_var_value= ON;
+source include/wait_for_status_var.inc;
+
+--echo # Test case initial set-up
+--connection master
+create table t_36934 (a int) engine=innodb;
+--source include/save_master_gtid.inc
+--connection slave
+--source include/sync_with_master_gtid.inc
+
+--echo # Pause the user transaction before inserting into Active_tranx
+--connect(user_con,localhost,root,,)
+set debug_dbug="+d,semisync_pause_at_write_tranx_in_binlog";
+--send insert into t_36934 values (1)
+
+--connection server_1
+set debug_sync="now wait_for at_write_tranx_in_binlog";
+
+--echo # Disconnect the slave (note that the binlog dump thread won't yet be
+--echo # notified of a binlog update from the last transaction, so the slave
+--echo # should neither receiver nor ACK the transaction).
+--connection slave
+--source include/stop_slave.inc
+
+--echo # Waiting for master to realize the slave has disconnected..
+--connection server_1
+let $status_var= rpl_semi_sync_master_clients;
+let $status_var_value= 0;
+source include/wait_for_status_var.inc;
+--echo # ..done
+
+--echo # Resuming transaction (it will exit commit_trx early without waiting)
+set debug_sync="now signal resume_write_tranx_in_binlog";
+
+--connection user_con
+--reap
+--let $user_con_tid= `SELECT connection_id()`
+--disconnect user_con
+--source include/wait_until_disconnected.inc
+
+--echo # Force delete the user thread (FLUSH THREADS ensures the thread won't
+--echo # stay in the thread cache)
+--connection master
+FLUSH THREADS;
+
+--echo # BUG: Re-connect slave. MDEV-36934 reports that the master would hang
+--echo # when the slave would re-connect and try to ACK the last transaction
+--echo # who's thread has been deleted
+--connection slave
+set @@global.debug_dbug=@save_debug_dbug;
+--source include/start_slave.inc
+
+--echo # Try to commit another transaction (prior to MDEV-36934 fixes, this
+--echo # would hang indefinitely)
+--connection master
+set @@global.debug_dbug=@save_debug_dbug;
+set debug_dbug=@save_debug_dbug;
+set debug_sync="RESET";
+--send insert into t_36934 values (2)
+
+--connection server_1
+--echo # Waiting 5s for last query to complete..
+--let $wait_timeout= 5
+--let $wait_condition= SELECT count(*)=0 FROM information_schema.processlist WHERE info LIKE 'insert into t_36934%';
+--source include/wait_condition.inc
+
+# Variable `success` is set by wait_condition.inc
+if (!$success)
+{
+  --echo # ..error
+  --die Query is hung
+}
+
+--connection master
+--reap
+--echo # ..done
+
+
+--echo # Cleanup
+--connection master
 set @@global.rpl_semi_sync_master_wait_point= @old_master_wait_point;
 set @@global.rpl_semi_sync_master_wait_no_slave= @old_master_wait_no_slave;
 set @@global.debug_dbug=@save_debug_dbug;
+drop table t_36934;
 
 --source include/rpl_end.inc
diff --git a/sql/semisync_master.cc b/sql/semisync_master.cc
index cd2b440ab36..d86d6f1d0a1 100644
--- a/sql/semisync_master.cc
+++ b/sql/semisync_master.cc
@@ -18,6 +18,7 @@
 
 #include <my_global.h>
 #include "semisync_master.h"
+#include "debug_sync.h"                         // debug_sync_set_action
 #include <algorithm>
 #include <mysql_com.h>
 
@@ -1307,6 +1308,16 @@ int Repl_semi_sync_master::write_tranx_in_binlog(THD *thd,
 
   DBUG_ENTER("Repl_semi_sync_master::write_tranx_in_binlog");
 
+#ifdef ENABLED_DEBUG_SYNC
+  DBUG_EXECUTE_IF(
+      "semisync_pause_at_write_tranx_in_binlog",
+      DBUG_ASSERT(!debug_sync_set_action(
+          current_thd,
+          STRING_WITH_LEN("now SIGNAL at_write_tranx_in_binlog WAIT_FOR "
+                          "resume_write_tranx_in_binlog")));
+      DBUG_SET("-d,semisync_pause_at_write_tranx_in_binlog"););
+#endif
+
   lock();
 
   /* This is the real check inside the mutex. */
-- 
2.43.0

