List:Commits« Previous MessageNext Message »
From:Ingo Struewing Date:January 9 2008 5:11pm
Subject:bk commit into 5.1 tree (istruewing:1.2650)
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of istruewing.  When istruewing does a push these changes
will be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2008-01-09 17:11:41+01:00, istruewing@stripped +24 -0
  Test Synchronization Facility #3
  
  This is a proposal for a facility to synchronize threads in the
  test suite to make race conditions repeatable and get rid of sleeps.
  
  It is slightly based on a proposal from Monty. The facility is
  contolled by a system variable.
  
  The patch does also include a changed merge.test and its result.
  It is an example how to use the facility.
  
  The synchronization points required in the example tests are also
  included.

  libmysqld/CMakeLists.txt@stripped, 2008-01-09 17:11:38+01:00, istruewing@stripped +1 -1
    Test Synchronization Facility #3
    Included mysql_test_sync.cc.

  libmysqld/Makefile.am@stripped, 2008-01-09 17:11:38+01:00, istruewing@stripped +1 -1
    Test Synchronization Facility #3
    Included mysql_test_sync.cc.

  mysql-test/mysql-test-run.pl@stripped, 2008-01-09 17:11:38+01:00, istruewing@stripped
+8 -0
    Test Synchronization Facility #3
    Added code for enabling/disabling of the facility.

  mysql-test/r/merge.result@stripped, 2008-01-09 17:11:38+01:00, istruewing@stripped +116
-86
    Test Synchronization Facility #3
    Fixed the example test result.

  mysql-test/r/test_synchronize.result@stripped, 2008-01-09 17:11:39+01:00,
istruewing@stripped +367 -0
    Test Synchronization Facility #3
    Added test result.

  mysql-test/r/test_synchronize.result@stripped, 2008-01-09 17:11:39+01:00,
istruewing@stripped +0 -0

  mysql-test/t/merge.test@stripped, 2008-01-09 17:11:38+01:00, istruewing@stripped +253
-63
    Test Synchronization Facility #3
    Example tests.
    Reworked tests to utilize the new facility.
    Added more tests.

  mysql-test/t/test_synchronize.test@stripped, 2008-01-09 17:11:39+01:00,
istruewing@stripped +411 -0
    Test Synchronization Facility #3
    Added a new test case to test the facility itself.

  mysql-test/t/test_synchronize.test@stripped, 2008-01-09 17:11:39+01:00,
istruewing@stripped +0 -0

  mysys/thr_lock.c@stripped, 2008-01-09 17:11:38+01:00, istruewing@stripped +13 -0
    Test Synchronization Facility #3
    Added a synchronization point hook.

  sql/CMakeLists.txt@stripped, 2008-01-09 17:11:38+01:00, istruewing@stripped +1 -0
    Test Synchronization Facility #3
    Included mysql_test_sync.cc and mysql_test_sync.h.

  sql/Makefile.am@stripped, 2008-01-09 17:11:38+01:00, istruewing@stripped +2 -2
    Test Synchronization Facility #3
    Included mysql_test_sync.cc and mysql_test_sync.h.

  sql/item_func.cc@stripped, 2008-01-09 17:11:38+01:00, istruewing@stripped +2 -0
    Test Synchronization Facility #3
    Added a synchronization point.

  sql/lock.cc@stripped, 2008-01-09 17:11:38+01:00, istruewing@stripped +2 -0
    Test Synchronization Facility #3
    Added synchronization points.

  sql/mysql_priv.h@stripped, 2008-01-09 17:11:38+01:00, istruewing@stripped +3 -0
    Test Synchronization Facility #3
    Added inclusion of test synchronization definitions.

  sql/mysql_test_sync.cc@stripped, 2008-01-09 17:11:39+01:00, istruewing@stripped +1604 -0
    Test Synchronization Facility #3
    Added variables and functions for the facility.

  sql/mysql_test_sync.cc@stripped, 2008-01-09 17:11:39+01:00, istruewing@stripped +0 -0

  sql/mysql_test_sync.h@stripped, 2008-01-09 17:11:39+01:00, istruewing@stripped +76 -0
    Test Synchronization Facility #3
    Added declarations for the facility.

  sql/mysql_test_sync.h@stripped, 2008-01-09 17:11:39+01:00, istruewing@stripped +0 -0

  sql/mysqld.cc@stripped, 2008-01-09 17:11:38+01:00, istruewing@stripped +38 -1
    Test Synchronization Facility #3
    Added initialization/deletion of the facility for the server.
    Added option "test-synchronize".

  sql/set_var.cc@stripped, 2008-01-09 17:11:39+01:00, istruewing@stripped +4 -0
    Test Synchronization Facility #3
    Added definition of the 'test_synchronize' system variable.

  sql/set_var.h@stripped, 2008-01-09 17:11:39+01:00, istruewing@stripped +13 -0
    Test Synchronization Facility #3
    Added declaration of the 'sys_var_test_synchronize' class.

  sql/sql_base.cc@stripped, 2008-01-09 17:11:39+01:00, istruewing@stripped +6 -0
    Test Synchronization Facility #3
    Added synchronization points.

  sql/sql_class.cc@stripped, 2008-01-09 17:11:39+01:00, istruewing@stripped +8 -1
    Test Synchronization Facility #3
    Added initialization/deletion of the facility for THD.

  sql/sql_class.h@stripped, 2008-01-09 17:11:39+01:00, istruewing@stripped +3 -0
    Test Synchronization Facility #3
    Added the 'test_sync_control' pointer to 'THD'.

  sql/sql_parse.cc@stripped, 2008-01-09 17:11:39+01:00, istruewing@stripped +1 -0
    Test Synchronization Facility #3
    Added a synchronization point.

  sql/sql_table.cc@stripped, 2008-01-09 17:11:39+01:00, istruewing@stripped +1 -0
    Test Synchronization Facility #3
    Added a synchronization point.

  storage/myisammrg/ha_myisammrg.cc@stripped, 2008-01-09 17:11:39+01:00,
istruewing@stripped +4 -0
    Test Synchronization Facility #3
    Added synchronization points.

diff -Nrup a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt
--- a/libmysqld/CMakeLists.txt	2007-11-17 21:32:04 +01:00
+++ b/libmysqld/CMakeLists.txt	2008-01-09 17:11:38 +01:00
@@ -187,7 +187,7 @@ SET(LIBMYSQLD_SOURCES emb_qcache.cc libm
            ../sql/strfunc.cc ../sql/table.cc ../sql/thr_malloc.cc
            ../sql/time.cc ../sql/tztime.cc ../sql/uniques.cc ../sql/unireg.cc
            ../sql/partition_info.cc ../sql/sql_connect.cc 
-           ../sql/scheduler.cc
+           ../sql/scheduler.cc ../sql/mysql_test_sync.cc
            ${GEN_SOURCES}
            ${LIB_SOURCES})
 
diff -Nrup a/libmysqld/Makefile.am b/libmysqld/Makefile.am
--- a/libmysqld/Makefile.am	2007-08-31 01:23:07 +02:00
+++ b/libmysqld/Makefile.am	2008-01-09 17:11:38 +01:00
@@ -76,7 +76,7 @@ sqlsources = derror.cc field.cc field_co
 	rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \
 	sql_tablespace.cc \
 	rpl_injector.cc my_user.c partition_info.cc \
-	sql_servers.cc
+	sql_servers.cc mysql_test_sync.cc
 
 libmysqld_int_a_SOURCES= $(libmysqld_sources)
 nodist_libmysqld_int_a_SOURCES= $(libmysqlsources) $(sqlsources)
diff -Nrup a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl
--- a/mysql-test/mysql-test-run.pl	2007-12-17 19:01:09 +01:00
+++ b/mysql-test/mysql-test-run.pl	2008-01-09 17:11:38 +01:00
@@ -272,6 +272,7 @@ my @default_valgrind_args= ("--show-reac
 my @valgrind_args;
 my $opt_valgrind_path;
 my $opt_callgrind;
+my $opt_test_synchronize= 300; # Default timeout for WAIT_FOR actions.
 
 our $opt_stress=               "";
 our $opt_stress_suite=     "main";
@@ -630,6 +631,7 @@ sub command_line_setup () {
              'valgrind-option=s'        => \@valgrind_args,
              'valgrind-path=s'          => \$opt_valgrind_path,
 	     'callgrind'                => \$opt_callgrind,
+	     'test-synchronize=i'       => \$opt_test_synchronize,
 
              # Stress testing 
              'stress'                   => \$opt_stress,
@@ -3825,6 +3827,10 @@ sub mysqld_arguments ($$$$) {
   # see BUG#28359
   mtr_add_arg($args, "%s--connect-timeout=60", $prefix);
 
+  # Enable the TEST SYNCHRONIZE facility, set default wait timeout.
+  # Facility stays disabled if timeout value is negative.
+  mtr_add_arg($args, "%s--test-synchronize=%s", $prefix,
+              $opt_test_synchronize);
 
   # When mysqld is run by a root user(euid is 0), it will fail
   # to start unless we specify what user to run as. If not running
@@ -5285,6 +5291,8 @@ Options for coverage, profiling etc
                         can be specified more then once
   valgrind-path=[EXE]   Path to the valgrind executable
   callgrind             Instruct valgrind to use callgrind
+  test-synchronize=NUM  Set default timeout for WAIT_FOR test synchronize
+                        actions. Disable facility with NUM=-1.
 
 Misc options
 
diff -Nrup a/mysql-test/r/merge.result b/mysql-test/r/merge.result
--- a/mysql-test/r/merge.result	2007-12-13 12:49:51 +01:00
+++ b/mysql-test/r/merge.result	2008-01-09 17:11:38 +01:00
@@ -1,3 +1,4 @@
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
 drop table if exists t1,t2,t3,t4,t5,t6;
 drop database if exists mysqltest;
 create table t1 (a int not null primary key auto_increment, message char(20));
@@ -1071,28 +1072,60 @@ UNLOCK TABLES;
 DROP TABLE t1, t2, t3, t4;
 CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
 CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST;
+connection con1
+SET TEST_SYNCHRONIZE= 'after_admin_flush
+                           SIGNAL admin_flush WAIT_FOR end_repair';
 REPAIR TABLE t1;
+connection default;
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR admin_flush';
+SET TEST_SYNCHRONIZE= 'mysql_lock_retry HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE= 'before_open_table_wait_refresh SIGNAL end_repair';
 INSERT INTO t2 VALUES (1);
+SET TEST_SYNCHRONIZE= 'now SIGNAL end_repair';
+connection con1
 Table	Op	Msg_type	Msg_text
 test.t1	repair	status	OK
+connection default;
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
 DROP TABLE t1, t2;
 CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
 CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST;
 LOCK TABLE t1 WRITE;
-INSERT INTO t2 VALUES (1);
 REPAIR TABLE t1;
 Table	Op	Msg_type	Msg_text
 test.t1	repair	status	OK
+connection con1
+SET TEST_SYNCHRONIZE= 'mysql_lock_retry HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE= 'after_insert SIGNAL end_repair';
+SET TEST_SYNCHRONIZE= 'before_open_table_wait_refresh SIGNAL end_repair';
+INSERT INTO t2 VALUES (1);
+connection default;
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR end_repair';
 UNLOCK TABLES;
+connection con1
+connection default;
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
 DROP TABLE t1, t2;
 CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
 LOCK TABLE t1 WRITE;
+connection con1
+SET TEST_SYNCHRONIZE= 'before_lock_tables_takes_lock
+                           SIGNAL opened WAIT_FOR flushed';
+SET TEST_SYNCHRONIZE= 'wait_for_lock SIGNAL locked EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'before_open_table_wait_refresh SIGNAL locked';
+SET TEST_SYNCHRONIZE= 'after_insert SIGNAL locked';
 INSERT INTO t1 VALUES (1);
+connection default
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR opened';
+SET TEST_SYNCHRONIZE= 'after_flush_unlock SIGNAL flushed';
 FLUSH TABLES;
-FLUSH TABLES;
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR locked';
 SELECT * FROM t1;
 c1
 UNLOCK TABLES;
+connection con1
+connection default
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
 DROP TABLE t1;
 #
 # Extra tests for Bug#26379 - Combination of FLUSH TABLE and
@@ -1611,6 +1644,7 @@ c1
 33
 DELETE FROM t4 WHERE c1 = 33;
 DROP TRIGGER t3_ai;
+UNLOCK TABLES;
 #
 # Trigger with table use on child
 DELETE FROM t4 WHERE c1 = 4;
@@ -1793,9 +1827,9 @@ ALTER TABLE t2 UNION=(t3,t1);
 SELECT * FROM t2;
 ERROR HY000: Table 't3' is differently defined or of non-MyISAM type or doesn't exist
 DROP TABLE t1, t2, t3;
-CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
-CREATE TABLE t2 (c1 INT) ENGINE= MyISAM;
-CREATE TABLE t3 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1, t2);
+CREATE TABLE t1 (c1 INT) ENGINE=MyISAM;
+CREATE TABLE t2 (c1 INT) ENGINE=MyISAM;
+CREATE TABLE t3 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2);
 INSERT INTO t1 VALUES (1);
 INSERT INTO t2 VALUES (2);
 SELECT * FROM t3;
@@ -1807,88 +1841,26 @@ SELECT * FROM t3;
 c1
 2
 DROP TABLE t1, t2, t3;
-CREATE TABLE t1 (id INTEGER, grp TINYINT, id_rev INTEGER);
-SET @rnd_max= 2147483647;
-SET @rnd= RAND();
-SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
-SET @id_rev= @rnd_max - @id;
-SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
-INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
-SET @rnd= RAND();
-SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
-SET @id_rev= @rnd_max - @id;
-SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
-INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
-SET @rnd= RAND();
-SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
-SET @id_rev= @rnd_max - @id;
-SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
-INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
-SET @rnd= RAND();
-SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
-SET @id_rev= @rnd_max - @id;
-SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
-INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
-SET @rnd= RAND();
-SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
-SET @id_rev= @rnd_max - @id;
-SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
-INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
-SET @rnd= RAND();
-SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
-SET @id_rev= @rnd_max - @id;
-SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
-INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
-SET @rnd= RAND();
-SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
-SET @id_rev= @rnd_max - @id;
-SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
-INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
-SET @rnd= RAND();
-SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
-SET @id_rev= @rnd_max - @id;
-SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
-INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
-SET @rnd= RAND();
-SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
-SET @id_rev= @rnd_max - @id;
-SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
-INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
-SET @rnd= RAND();
-SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
-SET @id_rev= @rnd_max - @id;
-SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
-INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
-set @@read_buffer_size=2*1024*1024;
-CREATE TABLE t2 SELECT * FROM t1;
-INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2;
-INSERT INTO t2 (id, grp, id_rev) SELECT id, grp, id_rev FROM t1;
-INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2;
-INSERT INTO t2 (id, grp, id_rev) SELECT id, grp, id_rev FROM t1;
-INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2;
-CREATE TABLE t3 (id INTEGER, grp TINYINT, id_rev INTEGER)
-ENGINE= MRG_MYISAM UNION= (t1, t2);
-SELECT COUNT(*) FROM t1;
-COUNT(*)
-130
-SELECT COUNT(*) FROM t2;
-COUNT(*)
-80
-SELECT COUNT(*) FROM t3;
-COUNT(*)
-210
-SELECT COUNT(DISTINCT a1.id) FROM t3 AS a1, t3 AS a2
-WHERE a1.id = a2.id GROUP BY a2.grp;
+CREATE TABLE t1 (c1 REAL) ENGINE=MyISAM;
+CREATE TABLE t2 (c1 REAL) ENGINE=MyISAM;
+INSERT INTO t1 VALUES(0.1);
+INSERT INTO t2 VALUES(0.2);
+CREATE TABLE t3 (c1 REAL) ENGINE=MRG_MYISAM UNION=(t1,t2);
+connection con1
+SET TEST_SYNCHRONIZE= 'before_acos_function
+                           SIGNAL select WAIT_FOR truncated';
+SELECT ACOS(c1) FROM t3;
+connection default
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR select';
+SET TEST_SYNCHRONIZE= 'before_wait_locked_tname SIGNAL truncated';
 TRUNCATE TABLE t1;
-SELECT COUNT(*) FROM t1;
-COUNT(*)
-0
-SELECT COUNT(*) FROM t2;
-COUNT(*)
-80
-SELECT COUNT(*) FROM t3;
-COUNT(*)
-80
+SET TEST_SYNCHRONIZE= 'now SIGNAL truncated';
+connection con1
+connection default
+SELECT ACOS(c1) FROM t3;
+ACOS(c1)
+1.3694384060046
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
 DROP TABLE t1, t2, t3;
 CREATE TABLE t1 (c1 INT) ENGINE=MyISAM;
 CREATE TABLE t2 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST;
@@ -1984,3 +1956,61 @@ test.t1	optimize	status	OK
 FLUSH TABLES m1, t1;
 UNLOCK TABLES;
 DROP TABLE t1, m1;
+CREATE TABLE t1 (c1 INT) ENGINE=MyISAM;
+CREATE TABLE m1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST;
+connection con1
+SET TEST_SYNCHRONIZE= 'before_myisammrg_attach
+                           SIGNAL attach WAIT_FOR flushed';
+INSERT INTO m1 VALUES (2);
+connection default;
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR attach';
+SET TEST_SYNCHRONIZE= 'after_flush_unlock SIGNAL flushed';
+FLUSH TABLE m1;
+connection con1
+connection default;
+SELECT * FROM m1;
+c1
+2
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
+DROP TABLE m1, t1;
+CREATE TABLE t1 (c1 INT) ENGINE=MyISAM;
+CREATE TABLE m1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST;
+connection con1
+SET TEST_SYNCHRONIZE= 'before_myisammrg_attach
+                           SIGNAL attach WAIT_FOR store_lock1';
+SET TEST_SYNCHRONIZE= 'before_myisammrg_store_lock
+                           SIGNAL store_lock2 WAIT_FOR flushed';
+INSERT INTO m1 VALUES (2);
+connection default;
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR attach';
+SET TEST_SYNCHRONIZE= 'before_myisammrg_store_lock
+                       SIGNAL store_lock1 WAIT_FOR store_lock2';
+SET TEST_SYNCHRONIZE= 'after_flush_unlock SIGNAL flushed';
+FLUSH TABLE m1;
+connection con1
+connection default;
+SELECT * FROM m1;
+c1
+2
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
+DROP TABLE m1, t1;
+CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
+LOCK TABLE t1 WRITE;
+connection con1
+SET TEST_SYNCHRONIZE= 'mysql_lock_retry HIT_LIMIT 1';
+SET TEST_SYNCHRONIZE= 'before_lock_tables_takes_lock
+                           SIGNAL opened WAIT_FOR flushed';
+SET TEST_SYNCHRONIZE= 'after_insert SIGNAL locked';
+SET TEST_SYNCHRONIZE= 'wait_for_lock SIGNAL locked EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'before_open_table_wait_refresh SIGNAL locked';
+INSERT INTO t1 VALUES (1);
+connection default;
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR opened';
+SET TEST_SYNCHRONIZE= 'after_flush_unlock SIGNAL flushed EXECUTE 2';
+FLUSH TABLES;
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR locked';
+UNLOCK TABLES;
+connection con1
+connection default;
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
+DROP TABLE t1;
diff -Nrup a/mysql-test/r/test_synchronize.result b/mysql-test/r/test_synchronize.result
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/r/test_synchronize.result	2008-01-09 17:11:39 +01:00
@@ -0,0 +1,367 @@
+SET TEST_SYNCHRONIZE= '- RESET';
+DROP TABLE IF EXISTS t1;
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+Variable_name	Value
+test_synchronize	ON - current signal: ''
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6           HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2           EXECUTE 2 HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2           EXECUTE 2';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2                     HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1                       EXECUTE 2 HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1                       EXECUTE 2';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1                                 HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1';
+SET TEST_SYNCHRONIZE='p0           WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0           WAIT_FOR s2 TIMEOUT 6 EXECUTE 2';
+SET TEST_SYNCHRONIZE='p0           WAIT_FOR s2 TIMEOUT 6           HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0           WAIT_FOR s2 TIMEOUT 6';
+SET TEST_SYNCHRONIZE='p0           WAIT_FOR s2           EXECUTE 2 HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0           WAIT_FOR s2           EXECUTE 2';
+SET TEST_SYNCHRONIZE='p0           WAIT_FOR s2                     HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0           WAIT_FOR s2';
+SET TEST_SYNCHRONIZE='p0                                           HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0 CLEAR';
+SET TEST_SYNCHRONIZE='p0 RESET';
+SET TEST_SYNCHRONIZE='xy RESET';
+SET TEST_SYNCHRONIZE='-- RESET';
+SET TEST_SYNCHRONIZE='p0 WARN_ACTIONS';
+SET TEST_SYNCHRONIZE='xy WARN_ACTIONS';
+SET TEST_SYNCHRONIZE='-- WARN_ACTIONS';
+SET TEST_SYNCHRONIZE='p0 TEST_SYNCHRONIZE';
+set test_synchronize='p0 signal s1 wait_for s2 timeout 6 execute 2 hit_limit 3';
+set test_synchronize='p0 signal s1 wait_for s2 timeout 6 execute 2';
+set test_synchronize='p0 signal s1 wait_for s2 timeout 6           hit_limit 3';
+set test_synchronize='p0 signal s1 wait_for s2 timeout 6';
+set test_synchronize='p0 signal s1 wait_for s2           execute 2 hit_limit 3';
+set test_synchronize='p0 signal s1 wait_for s2           execute 2';
+set test_synchronize='p0 signal s1 wait_for s2                     hit_limit 3';
+set test_synchronize='p0 signal s1 wait_for s2';
+set test_synchronize='p0 signal s1                       execute 2 hit_limit 3';
+set test_synchronize='p0 signal s1                       execute 2';
+set test_synchronize='p0 signal s1                                 hit_limit 3';
+set test_synchronize='p0 signal s1';
+set test_synchronize='p0           wait_for s2 timeout 6 execute 2 hit_limit 3';
+set test_synchronize='p0           wait_for s2 timeout 6 execute 2';
+set test_synchronize='p0           wait_for s2 timeout 6           hit_limit 3';
+set test_synchronize='p0           wait_for s2 timeout 6';
+set test_synchronize='p0           wait_for s2           execute 2 hit_limit 3';
+set test_synchronize='p0           wait_for s2           execute 2';
+set test_synchronize='p0           wait_for s2                     hit_limit 3';
+set test_synchronize='p0           wait_for s2';
+set test_synchronize='p0                                           hit_limit 3';
+set test_synchronize='p0 clear';
+set test_synchronize='p0 reset';
+set test_synchronize='xy reset';
+set test_synchronize='-- reset';
+set test_synchronize='p0 warn_actions';
+set test_synchronize='xy warn_actions';
+set test_synchronize='-- warn_actions';
+set test_synchronize='p0 test_synchronize';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6
+                      EXECUTE 2 HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0';
+ERROR 42000: Missing action after synchronization point name 'p0'
+SET TEST_SYNCHRONIZE='p0                                          EXECUTE 2';
+ERROR 42000: Missing action before EXECUTE
+SET TEST_SYNCHRONIZE='p0                                TIMEOUT 6 EXECUTE 2';
+ERROR 42000: Illegal or out of order stuff: 'TIMEOUT'
+SET TEST_SYNCHRONIZE='p0                                TIMEOUT 6';
+ERROR 42000: Illegal or out of order stuff: 'TIMEOUT'
+SET TEST_SYNCHRONIZE='p0 WAIT_FOR s2 SIGNAL s1';
+ERROR 42000: Illegal or out of order stuff: 'SIGNAL'
+SET TEST_SYNCHRONIZE='p0 WAIT_FOR s2 SIGNAL s1           EXECUTE 2';
+ERROR 42000: Illegal or out of order stuff: 'SIGNAL'
+SET TEST_SYNCHRONIZE='p0 WAIT_FOR s2 SIGNAL s1 TIMEOUT 6 EXECUTE 2';
+ERROR 42000: Illegal or out of order stuff: 'SIGNAL'
+SET TEST_SYNCHRONIZE='p0 WAIT_FOR s2 SIGNAL s1 TIMEOUT 6';
+ERROR 42000: Illegal or out of order stuff: 'SIGNAL'
+SET TEST_SYNCHRONIZE='p0 WAIT_FOR s2 TIMEOUT 6 SIGNAL s1 EXECUTE 2';
+ERROR 42000: Illegal or out of order stuff: 'SIGNAL'
+SET TEST_SYNCHRONIZE='p0 WAIT_FOR s2 TIMEOUT 6 SIGNAL s1';
+ERROR 42000: Illegal or out of order stuff: 'SIGNAL'
+SET TEST_SYNCHRONIZE='p0 TIMEOUT 6 WAIT_FOR s2 EXECUTE 2';
+ERROR 42000: Illegal or out of order stuff: 'TIMEOUT'
+SET TEST_SYNCHRONIZE='p0 TIMEOUT 6 WAIT_FOR s2';
+ERROR 42000: Illegal or out of order stuff: 'TIMEOUT'
+SET TEST_SYNCHRONIZE='p0                  SIGNAL s1 TIMEOUT 6 EXECUTE 2';
+ERROR 42000: Illegal or out of order stuff: 'TIMEOUT'
+SET TEST_SYNCHRONIZE='p0                  SIGNAL s1 TIMEOUT 6';
+ERROR 42000: Illegal or out of order stuff: 'TIMEOUT'
+SET TEST_SYNCHRONIZE='p0 EXECUTE 2 SIGNAL s1 TIMEOUT 6';
+ERROR 42000: Missing action before EXECUTE
+SET TEST_SYNCHRONIZE='p0 TIMEOUT 6 SIGNAL s1';
+ERROR 42000: Illegal or out of order stuff: 'TIMEOUT'
+SET TEST_SYNCHRONIZE='p0 EXECUTE 2 TIMEOUT 6 SIGNAL s1';
+ERROR 42000: Missing action before EXECUTE
+SET TEST_SYNCHRONIZE='p0 CLEAR HIT_LIMIT 3';
+ERROR 42000: Nothing must follow action CLEAR
+SET TEST_SYNCHRONIZE='CLEAR';
+ERROR 42000: Missing action after synchronization point name 'CLEAR'
+SET TEST_SYNCHRONIZE='p0 CLEAR p0';
+ERROR 42000: Nothing must follow action CLEAR
+SET TEST_SYNCHRONIZE='RESET';
+ERROR 42000: Missing action after synchronization point name 'RESET'
+SET TEST_SYNCHRONIZE='p0 RESET p0';
+ERROR 42000: Nothing must follow action RESET
+SET TEST_SYNCHRONIZE='WARN_ACTIONS';
+ERROR 42000: Missing action after synchronization point name 'WARN_ACTIONS'
+SET TEST_SYNCHRONIZE='p0 WARN_ACTIONS p0';
+ERROR 42000: Nothing must follow action WARN_ACTIONS
+SET TEST_SYNCHRONIZE='TEST_SYNCHRONIZE';
+ERROR 42000: Missing action after synchronization point name 'TEST_SYNCHRONIZE'
+SET TEST_SYNCHRONIZE='p0 TEST_SYNCHRONIZE p0';
+ERROR 42000: Nothing must follow action TEST_SYNCHRONIZE
+SET TEST_SYNCHRONIZx='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
+ERROR HY000: Unknown system variable 'TEST_SYNCHRONIZx'
+SET TEST_SYNCHRONIZE='p0 SIGNAx s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
+ERROR 42000: Illegal or out of order stuff: 'SIGNAx'
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOx s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
+ERROR 42000: Illegal or out of order stuff: 'WAIT_FOx'
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUx 0 EXECUTE 2 HIT_LIMIT 3';
+ERROR 42000: Illegal or out of order stuff: 'TIMEOUx'
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTx 2 HIT_LIMIT 3';
+ERROR 42000: Illegal or out of order stuff: 'EXECUTx'
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIx 3';
+ERROR 42000: Illegal or out of order stuff: 'HIT_LIMIx'
+SET TEST_SYNCHRONIZE='p0 CLEARx';
+ERROR 42000: Illegal or out of order stuff: 'CLEARx'
+SET TEST_SYNCHRONIZE='-- RESETx';
+ERROR 42000: Illegal or out of order stuff: 'RESETx'
+SET TEST_SYNCHRONIZE='-- WARN_ACTIONSx';
+ERROR 42000: Illegal or out of order stuff: 'WARN_ACTIONSx'
+SET TEST_SYNCHRONIZE='p0 TEST_SYNCHRONIZEx';
+ERROR 42000: Illegal or out of order stuff: 'TEST_SYNCHRONIZEx'
+SET TEST_SYNCHRONIZE='p0 WAIT_FOR s2 TIMEOUT 0x6 EXECUTE 2 HIT_LIMIT 3';
+ERROR 42000: Missing valid number after TIMEOUT
+SET TEST_SYNCHRONIZE='p0 WAIT_FOR s2 TIMEOUT 6 EXECUTE 0x2 HIT_LIMIT 3';
+ERROR 42000: Missing valid number after EXECUTE
+SET TEST_SYNCHRONIZE='p0 WAIT_FOR s2 TIMEOUT 7 EXECUTE 2 HIT_LIMIT 0x3';
+ERROR 42000: Missing valid number after HIT_LIMIT
+SET TEST_SYNCHRONIZE= 7;
+ERROR 42000: Incorrect argument type to variable 'test_synchronize'
+SET GLOBAL TEST_SYNCHRONIZE= 'p0 CLEAR';
+ERROR HY000: 'SESSION.test_synchronize' is not global variable
+SET TEST_SYNCHRONIZE= 'now SIGNAL something';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+Variable_name	Value
+test_synchronize	ON - current signal: 'something'
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR nothing TIMEOUT 0';
+Warnings:
+Warning	1105	TEST_SYNCHRONIZE sync point timed out
+SET TEST_SYNCHRONIZE= 'now SIGNAL nothing';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+Variable_name	Value
+test_synchronize	ON - current signal: 'nothing'
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR nothing TIMEOUT 0';
+SET TEST_SYNCHRONIZE= 'now SIGNAL something EXECUTE 0';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+Variable_name	Value
+test_synchronize	ON - current signal: 'nothing'
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR anotherthing TIMEOUT 0 EXECUTE 0';
+SET TEST_SYNCHRONIZE= 'now HIT_LIMIT 1';
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+SET TEST_SYNCHRONIZE= '=== WARN_ACTIONS';
+Warnings:
+Note	1105	static action p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3
+SET TEST_SYNCHRONIZE= 'now SIGNAL s1 EXECUTE 2';
+SET TEST_SYNCHRONIZE= '=== WARN_ACTIONS';
+Warnings:
+Note	1105	static action p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3
+Note	1105	static action now SIGNAL s1
+SET TEST_SYNCHRONIZE= 'p0  CLEAR';
+SET TEST_SYNCHRONIZE= '=== WARN_ACTIONS';
+#
+SET TEST_SYNCHRONIZE= '- RESET';
+SET TEST_SYNCHRONIZE= '- WARN_ACTIONS';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+Variable_name	Value
+test_synchronize	ON - current signal: ''
+#
+SET TEST_SYNCHRONIZE= 'p1abcd SIGNAL s1 EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'p1abcd WARN_ACTIONS';
+Warnings:
+Note	1105	dynamc action p1abcd SIGNAL s1 EXECUTE 2
+SET TEST_SYNCHRONIZE= 'p1abcd TEST_SYNCHRONIZE';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+Variable_name	Value
+test_synchronize	ON - current signal: 's1'
+#
+SET TEST_SYNCHRONIZE= 'p2abc SIGNAL s2 EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'p2abc WARN_ACTIONS';
+Warnings:
+Note	1105	dynamc action p2abc SIGNAL s2 EXECUTE 2
+Note	1105	dynamc action p1abcd SIGNAL s1
+SET TEST_SYNCHRONIZE= 'p2abc TEST_SYNCHRONIZE';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+Variable_name	Value
+test_synchronize	ON - current signal: 's2'
+#
+SET TEST_SYNCHRONIZE= 'p9abcdef SIGNAL s9 EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'p9abcdef WARN_ACTIONS';
+Warnings:
+Note	1105	dynamc action p2abc SIGNAL s2
+Note	1105	dynamc action p1abcd SIGNAL s1
+Note	1105	dynamc action p9abcdef SIGNAL s9 EXECUTE 2
+SET TEST_SYNCHRONIZE= 'p9abcdef TEST_SYNCHRONIZE';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+Variable_name	Value
+test_synchronize	ON - current signal: 's9'
+#
+SET TEST_SYNCHRONIZE= 'p4a SIGNAL s4 EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'p4a WARN_ACTIONS';
+Warnings:
+Note	1105	dynamc action p4a SIGNAL s4 EXECUTE 2
+Note	1105	dynamc action p2abc SIGNAL s2
+Note	1105	dynamc action p1abcd SIGNAL s1
+Note	1105	dynamc action p9abcdef SIGNAL s9
+SET TEST_SYNCHRONIZE= 'p4a TEST_SYNCHRONIZE';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+Variable_name	Value
+test_synchronize	ON - current signal: 's4'
+#
+SET TEST_SYNCHRONIZE= 'p5abcde SIGNAL s5 EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'p5abcde WARN_ACTIONS';
+Warnings:
+Note	1105	dynamc action p4a SIGNAL s4
+Note	1105	dynamc action p2abc SIGNAL s2
+Note	1105	dynamc action p1abcd SIGNAL s1
+Note	1105	dynamc action p5abcde SIGNAL s5 EXECUTE 2
+Note	1105	dynamc action p9abcdef SIGNAL s9
+SET TEST_SYNCHRONIZE= 'p5abcde TEST_SYNCHRONIZE';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+Variable_name	Value
+test_synchronize	ON - current signal: 's5'
+#
+SET TEST_SYNCHRONIZE= 'p6ab SIGNAL s6 EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'p6ab WARN_ACTIONS';
+Warnings:
+Note	1105	dynamc action p4a SIGNAL s4
+Note	1105	dynamc action p6ab SIGNAL s6 EXECUTE 2
+Note	1105	dynamc action p2abc SIGNAL s2
+Note	1105	dynamc action p1abcd SIGNAL s1
+Note	1105	dynamc action p5abcde SIGNAL s5
+Note	1105	dynamc action p9abcdef SIGNAL s9
+SET TEST_SYNCHRONIZE= 'p6ab TEST_SYNCHRONIZE';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+Variable_name	Value
+test_synchronize	ON - current signal: 's6'
+#
+SET TEST_SYNCHRONIZE= 'p7 SIGNAL s7 EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'p7 WARN_ACTIONS';
+Warnings:
+Note	1105	dynamc action p7 SIGNAL s7 EXECUTE 2
+Note	1105	dynamc action p4a SIGNAL s4
+Note	1105	dynamc action p6ab SIGNAL s6
+Note	1105	dynamc action p2abc SIGNAL s2
+Note	1105	dynamc action p1abcd SIGNAL s1
+Note	1105	dynamc action p5abcde SIGNAL s5
+Note	1105	dynamc action p9abcdef SIGNAL s9
+SET TEST_SYNCHRONIZE= 'p7 TEST_SYNCHRONIZE';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+Variable_name	Value
+test_synchronize	ON - current signal: 's7'
+#
+SET TEST_SYNCHRONIZE= 'p8abcdef SIGNAL s8 EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'p8abcdef WARN_ACTIONS';
+Warnings:
+Note	1105	dynamc action p7 SIGNAL s7
+Note	1105	dynamc action p4a SIGNAL s4
+Note	1105	dynamc action p6ab SIGNAL s6
+Note	1105	dynamc action p2abc SIGNAL s2
+Note	1105	dynamc action p1abcd SIGNAL s1
+Note	1105	dynamc action p5abcde SIGNAL s5
+Note	1105	dynamc action p8abcdef SIGNAL s8 EXECUTE 2
+Note	1105	dynamc action p9abcdef SIGNAL s9
+SET TEST_SYNCHRONIZE= 'p8abcdef TEST_SYNCHRONIZE';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+Variable_name	Value
+test_synchronize	ON - current signal: 's8'
+#
+SET TEST_SYNCHRONIZE= 'p3abcdef SIGNAL s3 EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'p3abcdef WARN_ACTIONS';
+Warnings:
+Note	1105	dynamc action p7 SIGNAL s7
+Note	1105	dynamc action p4a SIGNAL s4
+Note	1105	dynamc action p6ab SIGNAL s6
+Note	1105	dynamc action p2abc SIGNAL s2
+Note	1105	dynamc action p1abcd SIGNAL s1
+Note	1105	dynamc action p5abcde SIGNAL s5
+Note	1105	dynamc action p3abcdef SIGNAL s3 EXECUTE 2
+Note	1105	dynamc action p8abcdef SIGNAL s8
+Note	1105	dynamc action p9abcdef SIGNAL s9
+SET TEST_SYNCHRONIZE= 'p3abcdef TEST_SYNCHRONIZE';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+Variable_name	Value
+test_synchronize	ON - current signal: 's3'
+#
+SET TEST_SYNCHRONIZE= 'p4a TEST_SYNCHRONIZE';
+SET TEST_SYNCHRONIZE= 'p7 TEST_SYNCHRONIZE';
+SET TEST_SYNCHRONIZE= 'p3abcdef TEST_SYNCHRONIZE';
+SET TEST_SYNCHRONIZE= 'x WARN_ACTIONS';
+Warnings:
+Note	1105	dynamc action p6ab SIGNAL s6
+Note	1105	dynamc action p2abc SIGNAL s2
+Note	1105	dynamc action p1abcd SIGNAL s1
+Note	1105	dynamc action p5abcde SIGNAL s5
+Note	1105	dynamc action p8abcdef SIGNAL s8
+Note	1105	dynamc action p9abcdef SIGNAL s9
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+Variable_name	Value
+test_synchronize	ON - current signal: 's3'
+#
+SET TEST_SYNCHRONIZE= 'p1abcd CLEAR';
+SET TEST_SYNCHRONIZE= 'p2abc CLEAR';
+SET TEST_SYNCHRONIZE= 'p5abcde CLEAR';
+SET TEST_SYNCHRONIZE= '- WARN_ACTIONS';
+Warnings:
+Note	1105	dynamc action p6ab SIGNAL s6
+Note	1105	dynamc action p8abcdef SIGNAL s8
+Note	1105	dynamc action p9abcdef SIGNAL s9
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+Variable_name	Value
+test_synchronize	ON - current signal: 's3'
+#
+SET TEST_SYNCHRONIZE= 'p6ab CLEAR';
+SET TEST_SYNCHRONIZE= 'p8abcdef CLEAR';
+SET TEST_SYNCHRONIZE= 'p9abcdef CLEAR';
+SET TEST_SYNCHRONIZE= '- WARN_ACTIONS';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+Variable_name	Value
+test_synchronize	ON - current signal: 's3'
+SET TEST_SYNCHRONIZE= '- RESET';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+Variable_name	Value
+test_synchronize	ON - current signal: ''
+CREATE USER mysqltest_1@localhost;
+GRANT SUPER ON *.* TO mysqltest_1@localhost;
+connection con1, mysqltest_1
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
+connection default
+DROP USER mysqltest_1@localhost;
+CREATE USER mysqltest_2@localhost;
+GRANT ALL ON *.* TO mysqltest_2@localhost;
+REVOKE SUPER ON *.* FROM mysqltest_2@localhost;
+connection con1, mysqltest_2
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
+ERROR 42000: Access denied; you need the SUPER privilege for this operation
+connection default
+DROP USER mysqltest_2@localhost;
+SET TEST_SYNCHRONIZE= '- RESET';
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (c1 INT);
+connection con1
+SET TEST_SYNCHRONIZE= 'before_lock_tables_takes_lock
+      SIGNAL opened WAIT_FOR flushed';
+INSERT INTO t1 VALUES(1);
+connection default
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR opened';
+SET TEST_SYNCHRONIZE= 'after_flush_unlock SIGNAL flushed';
+FLUSH TABLE t1;
+connection con1
+connection default
+DROP TABLE t1;
+SET TEST_SYNCHRONIZE= '- RESET';
diff -Nrup a/mysql-test/t/merge.test b/mysql-test/t/merge.test
--- a/mysql-test/t/merge.test	2007-12-13 12:57:02 +01:00
+++ b/mysql-test/t/merge.test	2008-01-09 17:11:38 +01:00
@@ -2,7 +2,9 @@
 # Test of MERGE TABLES
 #
 
+# Clean up resources used in this test case.
 --disable_warnings
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
 drop table if exists t1,t2,t3,t4,t5,t6;
 drop database if exists mysqltest;
 --enable_warnings
@@ -693,12 +695,6 @@ DROP TABLE t1, t2, t3, t4;
 
 #
 # Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
-# Preparation
-connect (con1,localhost,root,,);
-connect (con2,localhost,root,,);
-connection default;
-#
-# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
 # Problem #1
 # A thread trying to lock a MERGE table performed busy waiting while
 # REPAIR TABLE or a similar table administration task was ongoing on one or
@@ -720,12 +716,40 @@ connection default;
 #
 CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
 CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST;
-send REPAIR TABLE t1;
+#
+    --echo connection con1
+    connect (con1,localhost,root,,);
+    # When reaching repair code, signal admin_flush and wait for end_repair.
+    SET TEST_SYNCHRONIZE= 'after_admin_flush
+                           SIGNAL admin_flush WAIT_FOR end_repair';
+    send REPAIR TABLE t1;
+#
+--echo connection default;
+connection default;
+# Wait that the other thread reaches repair.
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR admin_flush';
+#
+# If the bug exists, INSERT will loop infinitely in getting its lock.
+# Bail out if lock retry is done 3 times.
+SET TEST_SYNCHRONIZE= 'mysql_lock_retry HIT_LIMIT 3';
+# When the bug is fixed, we wait for refresh of repaired table.
+# In this case resume repair thread so that we do not deadlock.
+SET TEST_SYNCHRONIZE= 'before_open_table_wait_refresh SIGNAL end_repair';
+# Succeeds with bug fixed.
+INSERT INTO t2 VALUES (1);
+#
+# Resume the other thread. (non-bug fixed case)
+SET TEST_SYNCHRONIZE= 'now SIGNAL end_repair';
+#
+    --echo connection con1
     connection con1;
-    sleep 1; # let repair run into its sleep
-    INSERT INTO t2 VALUES (1);
+    reap;
+    disconnect con1;
+#
+--echo connection default;
 connection default;
-reap;
+# Clear test_sync signal.
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
 DROP TABLE t1, t2;
 #
 # Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
@@ -740,20 +764,39 @@ DROP TABLE t1, t2;
 # This is the same test case as for
 # Bug#26867 - LOCK TABLES + REPAIR + merge table result in memory/cpu hogging
 #
-#
 CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
 CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST;
 LOCK TABLE t1 WRITE;
-    connection con1;
+REPAIR TABLE t1;
+#
+    --echo connection con1
+    connect (con1,localhost,root,,);
+    # If the bug exists, the insert will loop infinitely in getting its lock.
+    # Bail out if lock retry is done 3 times.
+    SET TEST_SYNCHRONIZE= 'mysql_lock_retry HIT_LIMIT 3';
+    # If the bug exists, resume repair thread after reaching the retry limit.
+    SET TEST_SYNCHRONIZE= 'after_insert SIGNAL end_repair';
+    # If the bug is fixed, we wait for refresh of repaired table.
+    # In this case resume repair thread so that we do not deadlock.
+    SET TEST_SYNCHRONIZE= 'before_open_table_wait_refresh SIGNAL end_repair';
     send INSERT INTO t2 VALUES (1);
+#
+--echo connection default;
 connection default;
-sleep 1; # Let INSERT go into thr_multi_lock().
-REPAIR TABLE t1;
-sleep 2; # con1 performs busy waiting during this sleep.
+# Wait for signal from insert. Would be infinite with bug and no retry limit.
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR end_repair';
 UNLOCK TABLES;
+#
+    --echo connection con1
     connection con1;
+    # Succeeds with bug fixed.
     reap;
+    disconnect con1;
+#
+--echo connection default;
 connection default;
+# Clear test_sync signal.
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
 DROP TABLE t1, t2;
 #
 # Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
@@ -775,28 +818,54 @@ DROP TABLE t1, t2;
 # FLUSH TABLES must happen while the INSERT was on its way from
 # wait_for_tables() to the next call of thr_multi_lock(). This needed to be
 # supported by a sleep to make it repeatable.
+# In >= 5.0, when waiting after open_tables() one FLUSH is sufficient
+# to allow the INSERT to step in.
 #
 CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
 LOCK TABLE t1 WRITE;
-    connection con1;
+#
+    --echo connection con1
+    connect (con1,localhost,root,,);
+    # After open, wait for flush.
+    SET TEST_SYNCHRONIZE= 'before_lock_tables_takes_lock
+                           SIGNAL opened WAIT_FOR flushed';
+    # If bug is fixed, INSERT will go into wait_for_lock.
+    # Retain action after use. First used by general_log.
+    SET TEST_SYNCHRONIZE= 'wait_for_lock SIGNAL locked EXECUTE 2';
+    # Alternatively INSERT can wait for refresh in open_table().
+    SET TEST_SYNCHRONIZE= 'before_open_table_wait_refresh SIGNAL locked';
+    # If bug is not fixed, INSERT will succeed. Pretend locked.
+    SET TEST_SYNCHRONIZE= 'after_insert SIGNAL locked';
     send INSERT INTO t1 VALUES (1);
+#
+--echo connection default
 connection default;
-sleep 1; # Let INSERT go into thr_multi_lock().
-FLUSH TABLES;
-sleep 1; # Let INSERT go through wait_for_tables() where it sleeps.
+# Wait until INSERT opened the table.
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR opened';
+#
+# Let INSERT exploit the gap when flush waits wthout lock
+# for other threads to close the tables.
+SET TEST_SYNCHRONIZE= 'after_flush_unlock SIGNAL flushed';
 FLUSH TABLES;
-# This should give no result. But it will with sleep(2) at the right place.
+#
+# Wait until INSERT is locked (bug fixed) or finished (bug not fixed).
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR locked';
+#
+# This should give no result. But it will if the bug exists.
 SELECT * FROM t1;
 UNLOCK TABLES;
+#
+    --echo connection con1
     connection con1;
     reap;
+    disconnect con1;
+#
+--echo connection default
 connection default;
+# Clear test_sync signal.
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
 DROP TABLE t1;
 #
-# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
-# Cleanup
-disconnect con1;
-disconnect con2;
 #
 --echo #
 --echo # Extra tests for Bug#26379 - Combination of FLUSH TABLE and
@@ -1122,6 +1191,7 @@ SELECT @a;
 SELECT * FROM t4 ORDER BY c1;
 DELETE FROM t4 WHERE c1 = 33;
 DROP TRIGGER t3_ai;
+UNLOCK TABLES;
 --echo #
 --echo # Trigger with table use on child
 DELETE FROM t4 WHERE c1 = 4;
@@ -1225,11 +1295,12 @@ DROP TABLE t1, t2, t3;
 
 #
 # Bug#25038 - Waiting TRUNCATE
+# Truncate failed with error message when table was in use by MERGE.
 #
 # Show that truncate of child table after use of parent table works.
-CREATE TABLE t1 (c1 INT) ENGINE= MyISAM; 
-CREATE TABLE t2 (c1 INT) ENGINE= MyISAM; 
-CREATE TABLE t3 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1, t2); 
+CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; 
+CREATE TABLE t2 (c1 INT) ENGINE=MyISAM; 
+CREATE TABLE t3 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2); 
 INSERT INTO t1 VALUES (1);
 INSERT INTO t2 VALUES (2);
 SELECT * FROM t3;
@@ -1238,49 +1309,45 @@ SELECT * FROM t3;
 DROP TABLE t1, t2, t3;
 #
 # Show that truncate of child table waits while parent table is used.
-# (test partly borrowed from count_distinct3.)
-CREATE TABLE t1 (id INTEGER, grp TINYINT, id_rev INTEGER);
-SET @rnd_max= 2147483647;
-let $1 = 10;
-while ($1)
-{
-  SET @rnd= RAND();
-  SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
-  SET @id_rev= @rnd_max - @id;
-  SET @grp= CAST(127.0 * @rnd AS UNSIGNED); 
-  INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev); 
-  dec $1;
-}
-set @@read_buffer_size=2*1024*1024;
-CREATE TABLE t2 SELECT * FROM t1;
-INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2;
-INSERT INTO t2 (id, grp, id_rev) SELECT id, grp, id_rev FROM t1;
-INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2;
-INSERT INTO t2 (id, grp, id_rev) SELECT id, grp, id_rev FROM t1;
-INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2;
-CREATE TABLE t3 (id INTEGER, grp TINYINT, id_rev INTEGER)
-  ENGINE= MRG_MYISAM UNION= (t1, t2);
-SELECT COUNT(*) FROM t1;
-SELECT COUNT(*) FROM t2;
-SELECT COUNT(*) FROM t3;
-connect (con1,localhost,root,,);
-    # As t3 contains random numbers, results are different from test to test. 
-    # That's okay, because we test only that select doesn't yield an
-    # error. Note, that --disable_result_log doesn't suppress error output.
-    --disable_result_log
-    send SELECT COUNT(DISTINCT a1.id) FROM t3 AS a1, t3 AS a2
-      WHERE a1.id = a2.id GROUP BY a2.grp;
+CREATE TABLE t1 (c1 REAL) ENGINE=MyISAM;
+CREATE TABLE t2 (c1 REAL) ENGINE=MyISAM;
+INSERT INTO t1 VALUES(0.1);
+INSERT INTO t2 VALUES(0.2);
+CREATE TABLE t3 (c1 REAL) ENGINE=MRG_MYISAM UNION=(t1,t2);
+#
+    --echo connection con1
+    connect (con1,localhost,root,,);
+    # When reaching acos(), send 'select' and wait for truncated.
+    SET TEST_SYNCHRONIZE= 'before_acos_function
+                           SIGNAL select WAIT_FOR truncated';
+    send SELECT ACOS(c1) FROM t3;
+#
+--echo connection default
 connection default;
-sleep 1;
+# Wait for con1 to reach acos().
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR select';
+#
+# With bug fix present, TRUNCATE runs into wait_for_locked_table_names().
+SET TEST_SYNCHRONIZE= 'before_wait_locked_tname SIGNAL truncated';
 TRUNCATE TABLE t1;
+#
+# With bug fix not present, we need to signal after TRUNCATE.
+SET TEST_SYNCHRONIZE= 'now SIGNAL truncated';
+#
+    --echo connection con1
     connection con1;
+    # In non-debug server, the order of select and truncate is undetermined.
+    # So we may have one or two rows here.
+    --disable_result_log
     reap;
     --enable_result_log
     disconnect con1;
+#
+--echo connection default
 connection default;
-SELECT COUNT(*) FROM t1;
-SELECT COUNT(*) FROM t2;
-SELECT COUNT(*) FROM t3;
+SELECT ACOS(c1) FROM t3;
+# Clear test_sync signal.
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
 DROP TABLE t1, t2, t3;
 
 #
@@ -1380,4 +1447,127 @@ OPTIMIZE TABLE t1;
 FLUSH TABLES m1, t1;
 UNLOCK TABLES;
 DROP TABLE t1, m1;
+#
+# In-depth test.
+CREATE TABLE t1 (c1 INT) ENGINE=MyISAM;
+CREATE TABLE m1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST;
+    --echo connection con1
+    connect (con1,localhost,root,,);
+    # Wait for flush before attaching children.
+    SET TEST_SYNCHRONIZE= 'before_myisammrg_attach
+                           SIGNAL attach WAIT_FOR flushed';
+    send INSERT INTO m1 VALUES (2);
+--echo connection default;
+connection default;
+#
+# Wait for con1 to reach attach_merge_children(), then flush and signal.
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR attach';
+SET TEST_SYNCHRONIZE= 'after_flush_unlock SIGNAL flushed';
+FLUSH TABLE m1;
+#
+    --echo connection con1
+    connection con1;
+    reap;
+    disconnect con1;
+--echo connection default;
+connection default;
+SELECT * FROM m1;
+# Clear test_sync signal.
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
+DROP TABLE m1, t1;
+#
+# Test derived from test program for
+# Bug#30273 - merge tables: Can't lock file (errno: 155)
+# Second test try to step in between lock_count() and store_lock().
+#
+CREATE TABLE t1 (c1 INT) ENGINE=MyISAM;
+CREATE TABLE m1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST;
+    --echo connection con1
+    connect (con1,localhost,root,,);
+    # When reaching attach_merge_children(), signal attach and
+    # wait for store_lock1 before attaching children.
+    # Then run through attach of children until store_lock().
+    # Signal store_lock2 and wait for flushed.
+    SET TEST_SYNCHRONIZE= 'before_myisammrg_attach
+                           SIGNAL attach WAIT_FOR store_lock1';
+    SET TEST_SYNCHRONIZE= 'before_myisammrg_store_lock
+                           SIGNAL store_lock2 WAIT_FOR flushed';
+    send INSERT INTO m1 VALUES (2);
+--echo connection default;
+connection default;
+#
+# Wait for con1 to reach attach.
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR attach';
+# Run until myisammrg store_lock(),
+# then signal store_lock1 and
+# wait for con1 to go through attach until store_lock() (store_lock2),
+# then flush and signal flushed.
+SET TEST_SYNCHRONIZE= 'before_myisammrg_store_lock
+                       SIGNAL store_lock1 WAIT_FOR store_lock2';
+SET TEST_SYNCHRONIZE= 'after_flush_unlock SIGNAL flushed';
+FLUSH TABLE m1;
+#
+    --echo connection con1
+    connection con1;
+    reap;
+    disconnect con1;
+--echo connection default;
+connection default;
+SELECT * FROM m1;
+# Clear test_sync signal.
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
+DROP TABLE m1, t1;
+
+#
+# Coverage test for mysql_lock_retry_limit().
+# Similar test as for Bug#26379, Problem #3.
+# But mysql_lock_retry limit set to 2 and FLUSH repeated
+# so that the INSERT results in ER_LOCK_WAIT_TIMEOUT.
+#
+CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
+LOCK TABLE t1 WRITE;
+#
+    --echo connection con1
+    connect (con1,localhost,root,,);
+    SET TEST_SYNCHRONIZE= 'mysql_lock_retry HIT_LIMIT 1';
+    # After open, wait for flush.
+    SET TEST_SYNCHRONIZE= 'before_lock_tables_takes_lock
+                           SIGNAL opened WAIT_FOR flushed';
+    # If bug is not fixed, INSERT will succeed. Pretend locked.
+    SET TEST_SYNCHRONIZE= 'after_insert SIGNAL locked';
+    # If bug is fixed, INSERT will go into wait_for_lock.
+    # Retain action after use. First used by general_log.
+    SET TEST_SYNCHRONIZE= 'wait_for_lock SIGNAL locked EXECUTE 2';
+    # Alternatively INSERT can wait for refresh in open_table().
+    SET TEST_SYNCHRONIZE= 'before_open_table_wait_refresh SIGNAL locked';
+    send INSERT INTO t1 VALUES (1);
+#
+--echo connection default;
+connection default;
+#
+# Wait until INSERT opened the table.
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR opened';
+#
+# Let INSERT exploit the gap when flush waits wthout lock
+# for other threads to close the tables.
+SET TEST_SYNCHRONIZE= 'after_flush_unlock SIGNAL flushed EXECUTE 2';
+FLUSH TABLES;
+#
+# Wait until INSERT is locked (bug fixed) or finished (bug not fixed).
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR locked';
+UNLOCK TABLES;
+#
+    --echo connection con1
+    connection con1;
+    # Succeeds if TEST_SYNCHRONIZE is disabled
+    --error 0, ER_LOCK_WAIT_TIMEOUT
+    reap;
+    disconnect con1;
+#
+--echo connection default;
+connection default;
+#
+# Clear test_sync signal.
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
+DROP TABLE t1;
 
diff -Nrup a/mysql-test/t/test_synchronize.test b/mysql-test/t/test_synchronize.test
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/test_synchronize.test	2008-01-09 17:11:39 +01:00
@@ -0,0 +1,411 @@
+###################### t/test_synchronize.test #########################
+#                                                                      #
+# Testing of the TEST SYNCHRONIZE facility.                            #
+#                                                                      #
+# There is important documentation within                              #
+#       sql/mysql_test_sync.cc                                         #
+#                                                                      #
+# Used objects in this test case:                                      #
+# p0 - synchronization point 0. Dummy sync point.                      #
+# s1 - signal 1.                                                       #
+# s2 - signal 2.                                                       #
+#                                                                      #
+# Creation:                                                            #
+# 2008-01-01 istruewing                                                #
+#                                                                      #
+########################################################################
+
+#
+# We are checking privileges, which the embedded server cannot do.
+#
+--source include/not_embedded.inc
+
+#
+# Preparative cleanup.
+#
+--disable_warnings
+SET TEST_SYNCHRONIZE= '- RESET';
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+
+#
+# Show the special system variable.
+# It shows ON or OFF depending on the command line option --test-synchronize.
+# The test case assumes it is ON (command line option present).
+#
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+
+#
+# Syntax. Valid forms.
+#
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6           HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2           EXECUTE 2 HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2           EXECUTE 2';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2                     HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1                       EXECUTE 2 HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1                       EXECUTE 2';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1                                 HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1';
+SET TEST_SYNCHRONIZE='p0           WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0           WAIT_FOR s2 TIMEOUT 6 EXECUTE 2';
+SET TEST_SYNCHRONIZE='p0           WAIT_FOR s2 TIMEOUT 6           HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0           WAIT_FOR s2 TIMEOUT 6';
+SET TEST_SYNCHRONIZE='p0           WAIT_FOR s2           EXECUTE 2 HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0           WAIT_FOR s2           EXECUTE 2';
+SET TEST_SYNCHRONIZE='p0           WAIT_FOR s2                     HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0           WAIT_FOR s2';
+SET TEST_SYNCHRONIZE='p0                                           HIT_LIMIT 3';
+SET TEST_SYNCHRONIZE='p0 CLEAR';
+# Pseudo actions RESET and WARN_ACTIONS ignore the sync point name.
+SET TEST_SYNCHRONIZE='p0 RESET';
+SET TEST_SYNCHRONIZE='xy RESET';
+SET TEST_SYNCHRONIZE='-- RESET';
+SET TEST_SYNCHRONIZE='p0 WARN_ACTIONS';
+SET TEST_SYNCHRONIZE='xy WARN_ACTIONS';
+SET TEST_SYNCHRONIZE='-- WARN_ACTIONS';
+SET TEST_SYNCHRONIZE='p0 TEST_SYNCHRONIZE';
+
+#
+# Syntax. Valid forms. Lower case.
+#
+set test_synchronize='p0 signal s1 wait_for s2 timeout 6 execute 2 hit_limit 3';
+set test_synchronize='p0 signal s1 wait_for s2 timeout 6 execute 2';
+set test_synchronize='p0 signal s1 wait_for s2 timeout 6           hit_limit 3';
+set test_synchronize='p0 signal s1 wait_for s2 timeout 6';
+set test_synchronize='p0 signal s1 wait_for s2           execute 2 hit_limit 3';
+set test_synchronize='p0 signal s1 wait_for s2           execute 2';
+set test_synchronize='p0 signal s1 wait_for s2                     hit_limit 3';
+set test_synchronize='p0 signal s1 wait_for s2';
+set test_synchronize='p0 signal s1                       execute 2 hit_limit 3';
+set test_synchronize='p0 signal s1                       execute 2';
+set test_synchronize='p0 signal s1                                 hit_limit 3';
+set test_synchronize='p0 signal s1';
+set test_synchronize='p0           wait_for s2 timeout 6 execute 2 hit_limit 3';
+set test_synchronize='p0           wait_for s2 timeout 6 execute 2';
+set test_synchronize='p0           wait_for s2 timeout 6           hit_limit 3';
+set test_synchronize='p0           wait_for s2 timeout 6';
+set test_synchronize='p0           wait_for s2           execute 2 hit_limit 3';
+set test_synchronize='p0           wait_for s2           execute 2';
+set test_synchronize='p0           wait_for s2                     hit_limit 3';
+set test_synchronize='p0           wait_for s2';
+set test_synchronize='p0                                           hit_limit 3';
+set test_synchronize='p0 clear';
+# Pseudo actions RESET and WARN_ACTIONS ignore the sync point name.
+set test_synchronize='p0 reset';
+set test_synchronize='xy reset';
+set test_synchronize='-- reset';
+set test_synchronize='p0 warn_actions';
+set test_synchronize='xy warn_actions';
+set test_synchronize='-- warn_actions';
+set test_synchronize='p0 test_synchronize';
+
+#
+# Syntax. Valid forms. Line wrap.
+#
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6
+                      EXECUTE 2 HIT_LIMIT 3';
+
+#
+# Syntax. Invalid forms.
+#
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0                                          EXECUTE 2';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0                                TIMEOUT 6 EXECUTE 2';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0                                TIMEOUT 6';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 WAIT_FOR s2 SIGNAL s1';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 WAIT_FOR s2 SIGNAL s1           EXECUTE 2';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 WAIT_FOR s2 SIGNAL s1 TIMEOUT 6 EXECUTE 2';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 WAIT_FOR s2 SIGNAL s1 TIMEOUT 6';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 WAIT_FOR s2 TIMEOUT 6 SIGNAL s1 EXECUTE 2';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 WAIT_FOR s2 TIMEOUT 6 SIGNAL s1';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 TIMEOUT 6 WAIT_FOR s2 EXECUTE 2';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 TIMEOUT 6 WAIT_FOR s2';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0                  SIGNAL s1 TIMEOUT 6 EXECUTE 2';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0                  SIGNAL s1 TIMEOUT 6';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 EXECUTE 2 SIGNAL s1 TIMEOUT 6';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 TIMEOUT 6 SIGNAL s1';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 EXECUTE 2 TIMEOUT 6 SIGNAL s1';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 CLEAR HIT_LIMIT 3';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='CLEAR';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 CLEAR p0';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='RESET';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 RESET p0';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='WARN_ACTIONS';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 WARN_ACTIONS p0';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='TEST_SYNCHRONIZE';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 TEST_SYNCHRONIZE p0';
+
+#
+# Syntax. Invalid keywords used.
+#
+--error ER_UNKNOWN_SYSTEM_VARIABLE
+SET TEST_SYNCHRONIZx='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 SIGNAx s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOx s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUx 0 EXECUTE 2 HIT_LIMIT 3';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTx 2 HIT_LIMIT 3';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIx 3';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 CLEARx';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='-- RESETx';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='-- WARN_ACTIONSx';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 TEST_SYNCHRONIZEx';
+
+#
+# Syntax. Invalid numbers. Decimal only.
+#
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 WAIT_FOR s2 TIMEOUT 0x6 EXECUTE 2 HIT_LIMIT 3';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 WAIT_FOR s2 TIMEOUT 6 EXECUTE 0x2 HIT_LIMIT 3';
+--error ER_PARSE_ERROR
+SET TEST_SYNCHRONIZE='p0 WAIT_FOR s2 TIMEOUT 7 EXECUTE 2 HIT_LIMIT 0x3';
+
+#
+# Syntax. Invalid value type.
+#
+--error ER_WRONG_TYPE_FOR_VAR
+SET TEST_SYNCHRONIZE= 7;
+
+#
+# Syntax. TEST_SYNCHRONIZE is a SESSION-only variable.
+#
+--error ER_WRONG_OBJECT
+SET GLOBAL TEST_SYNCHRONIZE= 'p0 CLEAR';
+
+#
+# Functional tests.
+#
+# NOTE: There is the special synchronization point 'now'. It is placed
+#       immediately after setting of the TEST_SYNCHRONIZE variable.
+#       So it is executed before the SET statement ends.
+#
+# NOTE: There is only on global signal (say "signal post" or "flag mast").
+#       A SIGNAL activity writes its signal into it ("sets a flag").
+#       The signal persists until explicitly overwritten.
+#       To avoid confusion for later tests, it is recommended to clear
+#       the signal by signalling "empty" ("setting the 'empty' flag"):
+#       SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
+#       Alternatively you can reset the whole facility with:
+#       SET TEST_SYNCHRONIZE= '- RESET';
+#       The signal is then '' (really empty) which connot be done otherwise.
+#
+
+#
+# Time out immediately. This gives just a warning.
+#
+SET TEST_SYNCHRONIZE= 'now SIGNAL something';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR nothing TIMEOUT 0';
+#
+# If signal is present already, TIMEOUT 0 does not give a warning.
+#
+SET TEST_SYNCHRONIZE= 'now SIGNAL nothing';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR nothing TIMEOUT 0';
+
+#
+# EXECUTE 0 is effectively a no-op.
+#
+SET TEST_SYNCHRONIZE= 'now SIGNAL something EXECUTE 0';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR anotherthing TIMEOUT 0 EXECUTE 0';
+
+#
+# Run into HIT_LIMIT. This gives an error.
+#
+--error ER_LOCK_WAIT_TIMEOUT
+SET TEST_SYNCHRONIZE= 'now HIT_LIMIT 1';
+
+#
+# List active actions as warnings.
+#
+SET TEST_SYNCHRONIZE= '=== WARN_ACTIONS';
+SET TEST_SYNCHRONIZE= 'now SIGNAL s1 EXECUTE 2';
+SET TEST_SYNCHRONIZE= '=== WARN_ACTIONS';
+SET TEST_SYNCHRONIZE= 'p0  CLEAR';
+SET TEST_SYNCHRONIZE= '=== WARN_ACTIONS';
+
+#
+# Dynamic actions.
+# Show that they are sorted by sync point name length, name.
+# They are allocated in bunches of four. So use 9 actions to force
+# one allocation and two re-allocations.
+# Use EXECUTE 2 so that each action remains active after it is
+# executed to show that it is found in the array.
+# After having 9 active actions, deactivate them in steps and check
+# if the array is still intact.
+#
+--echo #
+SET TEST_SYNCHRONIZE= '- RESET';
+SET TEST_SYNCHRONIZE= '- WARN_ACTIONS';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+--echo #
+SET TEST_SYNCHRONIZE= 'p1abcd SIGNAL s1 EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'p1abcd WARN_ACTIONS';
+SET TEST_SYNCHRONIZE= 'p1abcd TEST_SYNCHRONIZE';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+--echo #
+SET TEST_SYNCHRONIZE= 'p2abc SIGNAL s2 EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'p2abc WARN_ACTIONS';
+SET TEST_SYNCHRONIZE= 'p2abc TEST_SYNCHRONIZE';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+--echo #
+SET TEST_SYNCHRONIZE= 'p9abcdef SIGNAL s9 EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'p9abcdef WARN_ACTIONS';
+SET TEST_SYNCHRONIZE= 'p9abcdef TEST_SYNCHRONIZE';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+--echo #
+SET TEST_SYNCHRONIZE= 'p4a SIGNAL s4 EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'p4a WARN_ACTIONS';
+SET TEST_SYNCHRONIZE= 'p4a TEST_SYNCHRONIZE';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+--echo #
+SET TEST_SYNCHRONIZE= 'p5abcde SIGNAL s5 EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'p5abcde WARN_ACTIONS';
+SET TEST_SYNCHRONIZE= 'p5abcde TEST_SYNCHRONIZE';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+--echo #
+SET TEST_SYNCHRONIZE= 'p6ab SIGNAL s6 EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'p6ab WARN_ACTIONS';
+SET TEST_SYNCHRONIZE= 'p6ab TEST_SYNCHRONIZE';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+--echo #
+SET TEST_SYNCHRONIZE= 'p7 SIGNAL s7 EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'p7 WARN_ACTIONS';
+SET TEST_SYNCHRONIZE= 'p7 TEST_SYNCHRONIZE';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+--echo #
+SET TEST_SYNCHRONIZE= 'p8abcdef SIGNAL s8 EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'p8abcdef WARN_ACTIONS';
+SET TEST_SYNCHRONIZE= 'p8abcdef TEST_SYNCHRONIZE';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+--echo #
+SET TEST_SYNCHRONIZE= 'p3abcdef SIGNAL s3 EXECUTE 2';
+SET TEST_SYNCHRONIZE= 'p3abcdef WARN_ACTIONS';
+SET TEST_SYNCHRONIZE= 'p3abcdef TEST_SYNCHRONIZE';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+--echo #
+# Consume some actions (consume their second execution).
+SET TEST_SYNCHRONIZE= 'p4a TEST_SYNCHRONIZE';
+SET TEST_SYNCHRONIZE= 'p7 TEST_SYNCHRONIZE';
+SET TEST_SYNCHRONIZE= 'p3abcdef TEST_SYNCHRONIZE';
+SET TEST_SYNCHRONIZE= 'x WARN_ACTIONS';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+--echo #
+# Clear some actions.
+SET TEST_SYNCHRONIZE= 'p1abcd CLEAR';
+SET TEST_SYNCHRONIZE= 'p2abc CLEAR';
+SET TEST_SYNCHRONIZE= 'p5abcde CLEAR';
+SET TEST_SYNCHRONIZE= '- WARN_ACTIONS';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+--echo #
+# Clear the rest. All dynamic actions must vanish even without RESET.
+SET TEST_SYNCHRONIZE= 'p6ab CLEAR';
+SET TEST_SYNCHRONIZE= 'p8abcdef CLEAR';
+SET TEST_SYNCHRONIZE= 'p9abcdef CLEAR';
+SET TEST_SYNCHRONIZE= '- WARN_ACTIONS';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+# Now cleanup. Actions are clear already, but signal needs to be cleared.
+SET TEST_SYNCHRONIZE= '- RESET';
+SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+
+#
+# Facility requires SUPER privilege.
+#
+CREATE USER mysqltest_1@localhost;
+GRANT SUPER ON *.* TO mysqltest_1@localhost;
+--echo connection con1, mysqltest_1
+connect (con1,localhost,mysqltest_1,,);
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
+disconnect con1;
+--echo connection default
+connection default;
+DROP USER mysqltest_1@localhost;
+#
+CREATE USER mysqltest_2@localhost;
+GRANT ALL ON *.* TO mysqltest_2@localhost;
+REVOKE SUPER ON *.* FROM mysqltest_2@localhost;
+--echo connection con1, mysqltest_2
+connect (con1,localhost,mysqltest_2,,);
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+SET TEST_SYNCHRONIZE= 'now SIGNAL empty';
+disconnect con1;
+--echo connection default
+connection default;
+DROP USER mysqltest_2@localhost;
+
+#
+# Example.
+#
+# Preparative cleanup.
+--disable_warnings
+SET TEST_SYNCHRONIZE= '- RESET';
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+#
+# Test.
+CREATE TABLE t1 (c1 INT);
+    --echo connection con1
+    connect (con1,localhost,root,,);
+    SET TEST_SYNCHRONIZE= 'before_lock_tables_takes_lock
+      SIGNAL opened WAIT_FOR flushed';
+    send INSERT INTO t1 VALUES(1);
+--echo connection default
+connection default;
+SET TEST_SYNCHRONIZE= 'now WAIT_FOR opened';
+SET TEST_SYNCHRONIZE= 'after_flush_unlock SIGNAL flushed';
+FLUSH TABLE t1;
+    --echo connection con1
+    connection con1;
+    reap;
+    disconnect con1;
+--echo connection default
+connection default;
+DROP TABLE t1;
+
+#
+# Cleanup after test case.
+# Otherwise signal would contain 'flushed' here,
+# which could confuse the next test.
+#
+SET TEST_SYNCHRONIZE= '- RESET';
+
diff -Nrup a/mysys/thr_lock.c b/mysys/thr_lock.c
--- a/mysys/thr_lock.c	2007-11-15 20:25:40 +01:00
+++ b/mysys/thr_lock.c	2008-01-09 17:11:38 +01:00
@@ -386,6 +386,12 @@ static inline my_bool have_specific_lock
 
 static void wake_up_waiters(THR_LOCK *lock);
 
+/**
+  Global pointer to be set if callback function is defined
+  (e.g. in mysqld). See mysql_test_sync.cc.
+*/
+void (*test_sync_wait_for_lock_callback_ptr)(void);
+
 
 static enum enum_thr_lock_result
 wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
@@ -397,6 +403,13 @@ wait_for_lock(struct st_lock_list *wait,
   enum enum_thr_lock_result result= THR_LOCK_ABORTED;
   my_bool can_deadlock= test(data->owner->info->n_cursors);
   DBUG_ENTER("wait_for_lock");
+
+  /*
+    One can use this to signal when a thread is going to wait for a lock.
+    See mysql_test_sync.cc.
+  */
+  if (test_sync_wait_for_lock_callback_ptr)
+    (*test_sync_wait_for_lock_callback_ptr)();
 
   if (!in_wait_list)
   {
diff -Nrup a/sql/CMakeLists.txt b/sql/CMakeLists.txt
--- a/sql/CMakeLists.txt	2007-11-14 16:11:41 +01:00
+++ b/sql/CMakeLists.txt	2008-01-09 17:11:38 +01:00
@@ -75,6 +75,7 @@ ADD_EXECUTABLE(mysqld
                partition_info.cc rpl_utility.cc rpl_injector.cc sql_locale.cc
                rpl_rli.cc rpl_mi.cc sql_servers.cc
                sql_connect.cc scheduler.cc 
+               mysql_test_sync.cc mysql_test_sync.h
                ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
                ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h
                ${PROJECT_SOURCE_DIR}/include/mysqld_error.h
diff -Nrup a/sql/Makefile.am b/sql/Makefile.am
--- a/sql/Makefile.am	2007-10-08 20:55:16 +02:00
+++ b/sql/Makefile.am	2008-01-09 17:11:38 +01:00
@@ -74,7 +74,7 @@ noinst_HEADERS =	item.h item_func.h item
 			sql_plugin.h authors.h \
 			event_data_objects.h event_scheduler.h \
 			sql_partition.h partition_info.h partition_element.h \
-			contributors.h sql_servers.h
+			contributors.h sql_servers.h mysql_test_sync.h
 
 mysqld_SOURCES =	sql_lex.cc sql_handler.cc sql_partition.cc \
 			item.cc item_sum.cc item_buff.cc item_func.cc \
@@ -117,7 +117,7 @@ mysqld_SOURCES =	sql_lex.cc sql_handler.
                         event_queue.cc event_db_repository.cc events.cc \
 			sql_plugin.cc sql_binlog.cc \
 			sql_builtin.cc sql_tablespace.cc partition_info.cc \
-			sql_servers.cc
+			sql_servers.cc mysql_test_sync.cc
 
 nodist_mysqld_SOURCES =	mini_client_errors.c pack.c client.c my_time.c my_user.c 
 
diff -Nrup a/sql/item_func.cc b/sql/item_func.cc
--- a/sql/item_func.cc	2007-12-14 14:01:09 +01:00
+++ b/sql/item_func.cc	2008-01-09 17:11:38 +01:00
@@ -1682,6 +1682,8 @@ double Item_func_pow::val_real()
 double Item_func_acos::val_real()
 {
   DBUG_ASSERT(fixed == 1);
+  /* One can use this to defer SELECT processing. */
+  TEST_SYNCHRONIZE(current_thd, BEFORE_ACOS_FUNCTION);
   // the volatile's for BUG #2338 to calm optimizer down (because of gcc's bug)
   volatile double value= args[0]->val_real();
   if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0))))
diff -Nrup a/sql/lock.cc b/sql/lock.cc
--- a/sql/lock.cc	2007-12-11 21:38:05 +01:00
+++ b/sql/lock.cc	2008-01-09 17:11:38 +01:00
@@ -318,6 +318,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, 
     thd->locked=0;
 retry:
     sql_lock=0;
+    TEST_SYNCHRONIZE(thd, MYSQL_LOCK_RETRY);
     if (flags & MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN)
     {
       *need_reopen= TRUE;
@@ -1109,6 +1110,7 @@ bool wait_for_locked_table_names(THD *th
       result=1;
       break;
     }
+    TEST_SYNCHRONIZE(thd, BEFORE_WAIT_LOCKED_TNAME);
     wait_for_condition(thd, &LOCK_open, &COND_refresh);
     pthread_mutex_lock(&LOCK_open);
   }
diff -Nrup a/sql/mysql_priv.h b/sql/mysql_priv.h
--- a/sql/mysql_priv.h	2007-12-14 14:01:09 +01:00
+++ b/sql/mysql_priv.h	2008-01-09 17:11:38 +01:00
@@ -2383,6 +2383,9 @@ bool load_collation(MEM_ROOT *mem_root,
                     CHARSET_INFO *dflt_cl,
                     CHARSET_INFO **cl);
 
+/* Test synchronization facility. See mysql_test_sync.cc. */
+#include "mysql_test_sync.h"
+
 #endif /* MYSQL_SERVER */
 #endif /* MYSQL_CLIENT */
 
diff -Nrup a/sql/mysql_test_sync.cc b/sql/mysql_test_sync.cc
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/mysql_test_sync.cc	2008-01-09 17:11:39 +01:00
@@ -0,0 +1,1604 @@
+/* Copyright (C) 2007 MySQL AB
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+/*
+  Test synchronization Facility.
+
+  This facility can be used in the test suite to make race conditions
+  repeatable and get rid of sleeps.
+
+  The facility allows to place "synchronization points" in the code like this:
+
+    ...
+    if (open_tables(...))
+      ...;
+
+    TEST_SYNCHRONIZE(thd, AFTER_OPEN_TABLES);
+
+    if (!lock_tables(...))
+     ...
+
+  When activated, a sync point can
+
+    - Send a signal and/or
+    - Wait for a signal
+
+  Nomenclature:
+    - signal:             A global value that keeps its value until
+                          overwritten by a new signal. One may also say
+                          "signal post" or "flag mast".
+    - send a signal:      Set the global value ("set a flag") and
+                          broadcast a global condition.
+    - wait for a signal:  Loop over waiting for the global condition until
+                          the global value matches the wait for signal.
+
+  By setting a system variable one can control the behavior of the
+  synchronization points. The system variable 'TEST_SYNCHRONIZE' is a
+  special variable in the sense that it does not adopt the value it is
+  assigned. Instead it parses an action from each assigned string value
+  and assigns them to named synchronization points.
+
+  By default, all sync points are inactive. They do nothing (except of
+  burning a couple of CPU cycles for checking if it is inactive). Here
+  is an example, how to activate and use the sync points:
+
+    --connection con1
+    SET TEST_SYNCHRONIZE= 'after_open_tables SIGNAL opened WAIT_FOR flushed';
+    INSERT INTO t1 VALUES(1);
+
+    --connection con2
+    SET TEST_SYNCHRONIZE= 'now WAIT_FOR opened';
+    SET TEST_SYNCHRONIZE= 'after_abort_locks SIGNAL flushed';
+    FLUSH TABLE t1;
+
+  When con1 runs through the INSERT statement it hits the sync point
+  'after_open_tables'. It notices that it is active and executes its
+  actions. It sends the signal 'opened' and waits for another thread to
+  send the signal 'flushed'.
+
+  con2 waits immediately at the special sync point 'now' for another
+  thread to send the 'opened' signal.
+
+  A signal remains in effect until it is overwritten. If con1 signals
+  'opened' before con2 reaches 'now', it will still find the 'opened'
+  signal. It does not wait in this case.
+
+  When con2 reaches 'after_abort_locks', it signals 'flushed', which lets
+  con1 awake.
+
+  Normally the activation of a sync point is cleared when it has been
+  executed. Sometimes it is necessary to keep the sync point active for
+  another execution. You can add an execute count to the action:
+
+    SET TEST_SYNCHRONIZE= 'name SIGNAL sig EXECUTE 3';
+
+  This will set an activation counter to 3. Each execution decrements the
+  counter. After the third execution the sync point becomes inactive in
+  this example.
+
+  One of the primary goals of this facility is to eliminate all sleeps
+  from the test suite in the long run. In most cases it should be possible
+  to rewrite test cases so that they do not need to sleep. (But this
+  facility cannot synchronize multiple processes.) However to support
+  developing of the tests, and as a last resort, sync point waiting times
+  out. There is a default timeout, but it can be overridden:
+
+    SET TEST_SYNCHRONIZE= 'name WAIT_FOR sig TIMEOUT 10 EXECUTE 2';
+
+  TIMEOUT 0 is special: If the signal is not present, the wait times out
+  immediately.
+
+  When a timeout happens, a warning is generated so that it shows up in
+  the test result.
+
+  You can throw an error message and kill the query when a synchronization
+  point is hit a certain number of times:
+
+    SET TEST_SYNCHRONIZE= 'name HIT_LIMIT 3';
+
+  Or combine it with signal and/or wait:
+
+    SET TEST_SYNCHRONIZE= 'name SIGNAL sig EXECUTE 2 HIT_LIMIT 3';
+
+  Here the first two hits send the signal, the third hit returns the error
+  message and kills the query.
+
+  For cases where you are not sure that an action is taken and thus
+  cleared in any case, you can force to clear a sync point:
+
+    SET TEST_SYNCHRONIZE= 'name CLEAR';
+
+  If you want to clear all actions and clear the global signal, use:
+
+    SET TEST_SYNCHRONIZE= "ignored_name RESET";
+
+  This is the only way to reset the global signal to ''.
+
+  For testing the facility itself there are the following two pseudo
+  actions:
+
+    SET TEST_SYNCHRONIZE= "ignored_name WARN_ACTIONS";
+
+  This "lists" the active actions by generating a warning per action like
+  so:
+
+    SHOW WARNINGS;
+    Note 1105 dynamc action point1 SIGNAL s2 EXECUTE 2
+    Note 1105 dynamc action point2 SIGNAL s1
+
+  Pseudo action 2:
+
+    SET TEST_SYNCHRONIZE= 'sync_point_to_test TEST_SYNCHRONIZE';
+
+  This executes the actions for 'sync_point_to_test' just as if the sync
+  point had been hit right now. Currently this works only for dynamic sync
+  points (string type sync points, see implementation).
+
+  Another way to diagnose the facility is to retrieve the current signal
+  (the "flag" on the "flag post"):
+
+  SHOW VARIABLES LIKE 'TEST_SYNCHRONIZE';
+  test_synchronize	ON - current signal: 's1'
+
+  ---------------------------------------------
+  See also the test file test_synchronize.test.
+  ---------------------------------------------
+
+
+  Implementation
+  ==============
+
+  Monty suggested to have an enum with synchronization point identifiers
+  and an array of actions per sync point referenced from THD.
+
+  The facility is to be enabled by a mysqld command line option:
+
+    --test-synchronize[=default_wait_timeout_value_in_seconds]
+
+  'default_wait_timeout_value_in_seconds' is the default timeout for the
+  WAIT_FOR action. If set to less that zero (<= -1) the facility stays
+  disabled.
+
+  This is set by default in the test suite, but can be disabled with:
+
+    mysql-test-run.pl ... --test-synchronize=-1 ...
+
+  Likewise the default wait timeout can be set:
+
+    mysql-test-run.pl ... --test-synchronize=10 ...
+
+  So we have in pseudo code:
+
+    #define TEST_SYNCHRONIZE(thd, sync_point_identifier)
+              if (unlikely(opt_test_synchronize))
+                test_synchronize(thd, sync_point_identifier)
+
+    void test_synchronize(thd, sync_point_identifier)
+    {
+      if (!thd ||
+          !thd->test_sync_action[sync_point_identifier].activation_count)
+        return;
+      execute test sync point actions
+    }
+
+  The TEST_SYNCHRONIZE system variable assignment searches for the array
+  slot of the sync point by using a typelib. It does then modify the array
+  slot.
+
+  The advantage is that inactive sync points have little overhead. However
+  to add a new sync point one must:
+
+    1. Edit the enum in mysql_test_sync.h
+    2. Edit the string array in mysql_test_sync.cc
+    3. Edit the file where the sync point is to be put.
+
+  Then the make compiles almost all of the server. Alternatively one
+  could split the enum in an extra header file and include this just in
+  those files that needs it. But then there is another step:
+
+    4. Verify that the file with the new sync point includes
+       mysql_test_sync.h
+
+  "Dynamic" string type sync points
+  =================================
+
+  Use strings as sync point identifiers:
+
+    TEST_SYNCHRONIZE_STR(thd, "after_open_tables");
+
+  No enum and no string array need to be edited.
+
+  The macro would look like:
+
+    #define TEST_SYNCHRONIZE_STR(thd, sync_point_name)
+              if (unlikely(opt_test_synchronize))
+                test_synchronize(thd, STRING_WITH_LEN(sync_point_name))
+
+  The TEST_SYNCHRONIZE system variable assignment adds the action to an
+  dynamic array and keeps it sorted by string length and string. In my
+  prototype implementation only 2 of 12 sync point names have the same
+  length. String comparison will rarely be done for searching, but just
+  for the final verification.
+
+  The test_synchronize() function does a binary search. In my prototype
+  implementation for merge.test, I don't have more than 3 actions active
+  at a time per thread. Even a very complicated case with 4-7 active
+  actions would find each action in at most three steps.
+
+  When the TEST SYNCHRONIZE facility is disabled, dynamic sync points
+  have the same overhead as static ones. The command line option
+  is checked first in any case.
+
+  Both types of sync points are implemented. The developer can first
+  play with the string identifiers. When his sync points prove useful and
+  he wants to keep them, he could turn them into "static" sync points.
+
+  This would mean that the TEST_SYNCHRONIZE system variable assignment
+  does first try to find the sync point in the typelib and then in the
+  dynamic array.
+
+  The developer would have to add
+
+    TEST_SYNCHRONIZE_STR(thd, "after_update");
+
+  And change it later to
+
+    TEST_SYNCHRONIZE(thd, AFTER_UPDATE);
+
+  plus editing the enum (mysql_test_sync.h) and the string array (this file).
+*/
+
+#include "mysql_priv.h"
+
+/*
+  Test synchronization point names must match enum_test_sync_point in
+  mysql_test_sync.h.
+*/
+static const char *test_sync_point_names[] =
+{
+  /* Special sync point not executed, for syntax checks */
+  "p0",
+
+  /* Special sync point for immediate execution */
+  "now",
+
+  /* Normal sync points. */
+  "before_open_table_wait_refresh",
+  "before_lock_tables_takes_lock",
+  "wait_for_lock",
+  "mysql_lock_retry",
+  "after_insert",
+  "after_flush_unlock",
+  "after_admin_flush",
+  "before_acos_function",
+  "before_wait_locked_tname",
+  "before_myisammrg_attach",
+  "before_myisammrg_store_lock",
+
+  /* Do not add sync points below this line. */
+  NullS
+};
+
+static TYPELIB test_sync_point_typelib=
+{
+  array_elements(test_sync_point_names) - 1,
+  "test_sync_point_typelib",
+  test_sync_point_names,
+  NULL
+};
+
+/* Collect information from system variable string. */
+struct st_test_sync_request
+{
+  ulong         hit_limit;
+  ulong         execute;
+  ulong         timeout;
+  LEX_STRING    signal;
+  LEX_STRING    wait_for;
+  LEX_STRING    sync_point;
+};
+
+/* Action to perform at a synchronization point. */
+struct st_test_sync_action
+{
+  ulong         activation_count;
+  ulong         hit_limit;
+  ulong         execute;
+  ulong         timeout;
+  String        signal;
+  String        wait_for;
+  String        sync_point;
+};
+
+/* Test synchronize control. Referenced by THD. */
+struct st_test_sync_control
+{
+  st_test_sync_action   ts_static_action[NUMBER_OF_SYNC_POINTS];
+  st_test_sync_action   *ts_dynamic_action;     /* array of dynamic actions */
+  uint                  ts_dynamic_allocated;   /* # allocated dyn actions */
+  uint                  ts_dynamic_active;      /* # active dynamic actions */
+
+};
+
+
+/**
+  Definitions for the test synchronization facility.
+  1. Global string variable to hold a "signal" ("signal post", "flag mast").
+  2. Global condition variable for signalling and waiting.
+  3. Global mutex to synchronize access to the above.
+  4. Global default timeout value for the WAIT_FOR action.
+*/
+
+static String SIGNAL_test_sync;
+static pthread_cond_t COND_test_sync;
+static pthread_mutex_t LOCK_test_sync;
+static ulong test_sync_default_wait_timeout;
+
+/**
+  Callback pointer from thr_lock.cc
+*/
+extern void (*test_sync_wait_for_lock_callback_ptr)(void);
+
+
+/**
+  Callback from wait_for_lock() for test synchronization. See thr_lock.c.
+
+  @description
+    One can use this to signal when a thread is going to wait for a lock.
+
+  @note
+    Beware of waiting for a signal here. The lock has aquired its mutex.
+    While waiting on a signal here, the locking thread could not aquire
+    the mutex to release the lock. One could lock up the table
+    completely.
+
+  @note
+    The callback pointer in thr_lock.c is set only if test
+    synchronization is initialized. And this is done only if
+    opt_test_synchronize is set.
+*/
+
+static void test_sync_wait_for_lock_callback(void)
+{
+  test_synchronize(current_thd, WAIT_FOR_LOCK);
+}
+
+
+/**
+  Initialize the test synchronization facility at server start.
+
+  @return status
+    @retval     0       ok
+    @retval     != 0    error
+
+  @note
+    This is called only if opt_test_synchronize is set.
+*/
+
+int test_sync_init(void)
+{
+  DBUG_ENTER("test_sync_init");
+  DBUG_ASSERT(opt_test_synchronize);
+
+  /*
+    Initialize the global variables before the consistency check
+    so that they can be destroyed by server clean up.
+  */
+  SIGNAL_test_sync.length(0);
+  /*
+    @todo Alternatively:
+    SIGNAL_test_sync.copy(STRING_WITH_LEN("empty"), system_charset_info);
+  */
+  (void) pthread_cond_init(&COND_test_sync, NULL);
+  (void) pthread_mutex_init(&LOCK_test_sync, MY_MUTEX_INIT_FAST);
+
+  /*
+    Compute default timeout for WAIT_FOR actions.
+    If the commad line option value is set to zero, the facility is
+    disabled and we won't come here.
+    If the value is set to negative, timeout value is zero.
+    See mysqld_get_one_option() in mysqld.cc.
+  */
+  test_sync_default_wait_timeout= ((opt_test_synchronize < 0) ? 0 :
+                                   opt_test_synchronize);
+  DBUG_PRINT("test_sync", ("default_wait_timeout: %lu",
+                           test_sync_default_wait_timeout));
+
+  /* Consistency check. */
+  if (array_elements(test_sync_point_names) - 1 != NUMBER_OF_SYNC_POINTS)
+  {
+    my_printf_error(ER_UNKNOWN_ERROR,
+                    "Internal error:  "
+                    "number of synchronization point names: %d  "
+                    "does not match number of enums: %d  "
+                    "run without --test-synchronize",
+                    MYF(0), array_elements(test_sync_point_names),
+                    NUMBER_OF_SYNC_POINTS);
+    DBUG_RETURN(1);
+  }
+
+  /* Set the call back pointer in thr_lock.c. */
+  test_sync_wait_for_lock_callback_ptr=
+    test_sync_wait_for_lock_callback;
+
+  DBUG_RETURN(0);
+}
+
+
+/**
+  End the test synchronization facility at server shutdown.
+
+  @note
+    This is called only if opt_test_synchronize is set.
+*/
+
+void test_sync_end(void)
+{
+  DBUG_ENTER("test_sync_end");
+  DBUG_ASSERT(opt_test_synchronize);
+
+  /* Clear the call back pointer in thr_lock.c. */
+  test_sync_wait_for_lock_callback_ptr= NULL;
+
+  /* Destroy the global variables. */
+  SIGNAL_test_sync.free();
+  (void) pthread_cond_destroy(&COND_test_sync);
+  (void) pthread_mutex_destroy(&LOCK_test_sync);
+
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Initialize the test synchronization facility at thread start.
+
+  @param[in]    thd             thread handle
+*/
+
+void test_sync_init_thread(THD *thd)
+{
+  DBUG_ENTER("test_sync_init_thread");
+  /*
+    Do not allocate thd->test_sync_control for every thread.
+    1. Do this only for threads that use TEST_SYNCHRONIZE.
+    2. We cannot return an error code from this function. So better
+       allocate the memory on the first activation, where a proper
+       error can be returned.
+    Allocation is done on the first SET TEST_SYNCHRONIZE in a thread.
+    See test_sync_set_action().
+  */
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Free sync point actions.
+
+  @param[in]    action          first action
+  @param[in]    count           number of actions to free
+*/
+
+static void test_sync_free_actions(st_test_sync_action *action, uint count)
+{
+  st_test_sync_action *action_end= action + count;
+  DBUG_ENTER("test_sync_free_actions");
+
+  for (; action < action_end; action++)
+  {
+    action->signal.free();
+    action->wait_for.free();
+    action->sync_point.free();
+  }
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  End the test synchronization facility at thread end.
+
+  @param[in]    thd             thread handle
+*/
+
+void test_sync_end_thread(THD *thd)
+{
+  DBUG_ENTER("test_sync_end_thread");
+
+  if (thd->test_sync_control)
+  {
+    st_test_sync_control *ts_control= thd->test_sync_control;
+
+    test_sync_free_actions(ts_control->ts_static_action,
+                           NUMBER_OF_SYNC_POINTS);
+    if (ts_control->ts_dynamic_action)
+    {
+      test_sync_free_actions(ts_control->ts_dynamic_action,
+                             ts_control->ts_dynamic_allocated);
+      my_free(ts_control->ts_dynamic_action, MYF(0));
+    }
+    my_free(ts_control, MYF(0));
+    thd->test_sync_control= NULL;
+  }
+
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Reset the test synchronization facility.
+
+  @param[in]    thd             thread handle
+
+  @description
+
+    Clear all static actions.
+    Remove all dynamic actions.
+    Clear the global signal.
+*/
+
+void test_sync_reset(THD *thd)
+{
+  DBUG_ENTER("test_sync_reset");
+
+  if (thd->test_sync_control)
+  {
+    st_test_sync_control *ts_control= thd->test_sync_control;
+    st_test_sync_action  *action= ts_control->ts_static_action;
+    st_test_sync_action  *action_end= action + NUMBER_OF_SYNC_POINTS;
+
+    /* Clear all static actions. */
+    for (; action < action_end; action++)
+      action->activation_count= 0;
+
+    /* Remove all dynamic actions. */
+    ts_control->ts_dynamic_active= 0;
+
+    /* Clear the global signal. */
+    SIGNAL_test_sync.length(0);
+    /*
+      @todo Alternatively:
+      SIGNAL_test_sync.copy(STRING_WITH_LEN("empty"), system_charset_info);
+    */
+  }
+
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Compare two strings by length, string.
+
+  @param[in]    length1         length of string1
+  @param[in]    length2         length of string2
+  @param[in]    string1         string1
+  @param[in]    string2         string2
+
+  @return       difference
+    @retval     == 0            length1/string1 is same as length2/string2
+    @retval     < 0             length1/string1 is smaller
+    @retval     > 0             length1/string1 is bigger
+*/
+
+static int test_sync_cmp_str(uint length1, uint length2,
+                             const char* string1, const char* string2)
+{
+  int diff;
+
+  diff= length1 - length2;
+  if (!diff)
+    diff= memcmp(string1, string2, length1);
+
+  return diff;
+}
+
+
+/**
+  Compare two actions by sync point name length, string.
+
+  @param[in]    arg1            reference to action1
+  @param[in]    arg2            reference to action2
+
+  @return       difference
+    @retval     == 0            length1/string1 is same as length2/string2
+    @retval     < 0             length1/string1 is smaller
+    @retval     > 0             length1/string1 is bigger
+*/
+
+static int test_sync_qsort_cmp(const void* arg1, const void* arg2)
+{
+  st_test_sync_action *action1= (st_test_sync_action*) arg1;
+  st_test_sync_action *action2= (st_test_sync_action*) arg2;
+
+  return test_sync_cmp_str(action1->sync_point.length(),
+                           action2->sync_point.length(),
+                           action1->sync_point.ptr(),
+                           action2->sync_point.ptr());
+}
+
+
+/**
+  Find a test synchronization action in the dynamic array.
+
+  @param[in]    actionarr       array of test sync actions
+  @param[in]    quantity        number of actions in array
+  @param[in]    tsp_name        name of test sync point to find
+  @param[in]    tsp_length      length of name of test sync point
+
+  @return       array index
+    @retval     >= 0            found sync point in array
+    @retval     -1              not found
+
+  @description
+
+    Binary search. Array needs to be sorted by length, sync point name.
+*/
+
+static int test_sync_find(st_test_sync_action *actionarr, int quantity,
+                          const char* tsp_name, uint tsp_length)
+{
+  st_test_sync_action   *action;
+  int                   low ;
+  int                   high ;
+  int                   mid ;
+  int                   diff ;
+
+  low= 0;
+  high= quantity;
+
+  while (low < high)
+  {
+    mid= (low + high) / 2;
+    action= actionarr + mid;
+    diff= test_sync_cmp_str(tsp_length, action->sync_point.length(),
+                            tsp_name,   action->sync_point.ptr());
+    if (diff == 0)
+      return mid;
+    if (diff > 0)
+      low= mid + 1;
+    else
+      high= mid - 1;
+  }
+
+  if (low < quantity)
+  {
+    action= actionarr + low;
+    if (!test_sync_cmp_str(tsp_length, action->sync_point.length(),
+                           tsp_name,   action->sync_point.ptr()))
+      return low ;
+  }
+
+  return -1 ;
+}
+
+
+/**
+  Remove a dynamic test synchronization action.
+
+  @param[in]    ts_control      control object
+  @param[in]    tsp_index       dynamic sync point index
+*/
+
+static void test_sync_remove_action(st_test_sync_control *ts_control,
+                                    st_test_sync_action *action)
+{
+  uint tsp_idx= action - ts_control->ts_dynamic_action;
+  DBUG_ENTER("test_sync_remove_action");
+  DBUG_ASSERT(tsp_idx < ts_control->ts_dynamic_active);
+
+  /* Decrement the number of currently active dynamic actions. */
+  ts_control->ts_dynamic_active--;
+
+  /*
+    If this was not the last active action in the array, we need to
+    shift remaining active actions down to keep the array gap free.
+    Otherwise binary search might fail or take longer than necessary at
+    least. Also new actions are always put to the end of the array.
+  */
+  if (ts_control->ts_dynamic_active > tsp_idx)
+  {
+    /* Free strings before copying them over. */
+    test_sync_free_actions(action, 1);
+
+    /* Move actions down. */
+    memmove(ts_control->ts_dynamic_action + tsp_idx,
+            ts_control->ts_dynamic_action + tsp_idx + 1,
+            (ts_control->ts_dynamic_active - tsp_idx) *
+            sizeof(st_test_sync_action));
+
+    /* Remove double references of String pointers. */
+    bzero(ts_control->ts_dynamic_action +
+          ts_control->ts_dynamic_active, sizeof(st_test_sync_action));
+  }
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Set a test synchronization action.
+
+  @param[in]    thd             thread handle
+  @param[in]    request         synchronization request
+
+  @return       status
+    @retval     FALSE           ok
+    @retval     TRUE            error
+
+  @description
+
+    Make a test sync action from a test sync request.
+*/
+
+static bool test_sync_set_action(THD *thd, st_test_sync_request *request)
+{
+  st_test_sync_control  *ts_control;
+  st_test_sync_action   *action;
+  int                   tsp_idx; /* Test Sync Point Index */
+  bool                  need_sort= FALSE;
+  bool                  is_clear_request;
+  DBUG_ENTER("test_sync_set_action");
+
+  if (!opt_test_synchronize)
+  {
+    DBUG_PRINT("test_sync", ("facility is not enabled"));
+    /*
+      Do not raise error or warning. Test result should not
+      unnecessarily differ from enabled facility.
+    */
+    goto end;
+  }
+
+  /*
+    If we have a CLEAR request, we do not need to allocate something,
+    just clear an existing action, and remove it, if it is dynamic.
+  */
+  is_clear_request= (!request->signal.length &&
+                     !request->wait_for.length &&
+                     !request->hit_limit);
+
+  if (!thd->test_sync_control)
+  {
+    if (is_clear_request)
+    {
+      /* No need to allocate the control object. */
+      goto end;
+    }
+
+    thd->test_sync_control= (st_test_sync_control*)
+      my_malloc(sizeof(st_test_sync_control), MYF(MY_WME | MY_ZEROFILL));
+    if (!thd->test_sync_control)
+      goto err; /* Error is reported by my_malloc(). */
+  }
+  ts_control= thd->test_sync_control;
+
+  /*
+    Find synchronisation point array index by its name.
+    find_type() options:
+      1 accept only whole names
+      2 don't expand string argument (arg #1) if incomplete name given
+  */
+  if ((tsp_idx= find_type(request->sync_point.str,
+                          &test_sync_point_typelib, 1+2)) > 0)
+  {
+    tsp_idx--; /* find_type() returns offset + 1. */
+    DBUG_ASSERT(tsp_idx < NUMBER_OF_SYNC_POINTS);
+    action= ts_control->ts_static_action + tsp_idx;
+  }
+  else if (ts_control->ts_dynamic_action && ts_control->ts_dynamic_active
&&
+           ((tsp_idx= test_sync_find(ts_control->ts_dynamic_action,
+                                     ts_control->ts_dynamic_active,
+                                     request->sync_point.str,
+                                     request->sync_point.length)) >= 0))
+  {
+    /* Overwrite an already active sync point action. */
+    DBUG_ASSERT((uint) tsp_idx < ts_control->ts_dynamic_active);
+    action= ts_control->ts_dynamic_action + tsp_idx;
+
+    if (is_clear_request)
+    {
+      test_sync_remove_action(ts_control, action);
+      goto end;
+    }
+  }
+  else if (is_clear_request)
+  {
+    /* No need to allocate a new action. */
+    goto end;
+  }
+  else
+  {
+    tsp_idx= ts_control->ts_dynamic_active++;
+    if (ts_control->ts_dynamic_active > ts_control->ts_dynamic_allocated)
+    {
+      uint new_alloc= ts_control->ts_dynamic_active + 3;
+      void *point= my_realloc(ts_control->ts_dynamic_action,
+                              new_alloc * sizeof(st_test_sync_action),
+                              MYF(MY_WME | MY_ALLOW_ZERO_PTR));
+      if (!point)
+        goto err; /* Error is reported by my_malloc(). */
+      ts_control->ts_dynamic_action= (st_test_sync_action*) point;
+      ts_control->ts_dynamic_allocated= new_alloc;
+      bzero((uchar*) (ts_control->ts_dynamic_action + tsp_idx),
+            (new_alloc - tsp_idx) * sizeof(st_test_sync_action));
+    }
+    need_sort= TRUE;
+    action= ts_control->ts_dynamic_action + tsp_idx;
+  }
+
+  /* Create the action struct from the request. */
+  action->activation_count= max(request->hit_limit, request->execute);
+  if (action->activation_count)
+  {
+    action->hit_limit=  request->hit_limit;
+    action->execute=    request->execute;
+    action->timeout=    request->timeout;
+    action->signal.copy(request->signal.str, request->signal.length,
+                        system_charset_info);
+    action->wait_for.copy(request->wait_for.str, request->wait_for.length,
+                          system_charset_info);
+    action->sync_point.copy(request->sync_point.str, request->sync_point.length,
+                            system_charset_info);
+    DBUG_PRINT("test_sync", ("sync_point: '%s'  activation_count: %lu  "
+                             "hit_limit: %lu  execute: %lu  timeout: %lu  "
+                             "signal: '%s'  wait_for: '%s'",
+                             request->sync_point.str, action->activation_count,
+                             action->hit_limit, action->execute,
+                             action->timeout, action->signal.c_ptr(),
+                             action->wait_for.c_ptr()));
+  }
+
+  if (need_sort)
+  {
+    /* Sort dynamic actions by sync point name length, name. */
+    my_qsort(ts_control->ts_dynamic_action, ts_control->ts_dynamic_active,
+             sizeof(st_test_sync_action), test_sync_qsort_cmp);
+  }
+
+  /* Execute the special sync point 'now' if it is active. */
+  TEST_SYNCHRONIZE(thd, NOW);
+  /* If HIT_LIMIT is 1, return error to suppress send_ok(). */
+  if (thd->killed)
+    goto err;
+
+ end:
+  DBUG_RETURN(FALSE);
+
+ err:
+  DBUG_RETURN(TRUE);
+}
+
+
+/**
+  Print a warning with action values.
+
+*/
+
+static void test_sync_print_warn_action(THD *thd, const char *prefix,
+                                        st_test_sync_action *action)
+{
+  char  warntext[1024];
+  char  *wtxt= warntext;
+  char  *wend= warntext + sizeof(warntext) - 1; /* Allow emergency '\0'. */
+  DBUG_ENTER("test_sync_print_warn_action");
+
+  wtxt= strxnmov(wtxt, wend - wtxt, prefix, action->sync_point.c_ptr(), NullS);
+  if (action->signal.length())
+  {
+    wtxt= strxnmov(wtxt, wend - wtxt, " SIGNAL ", action->signal.c_ptr(),
+                   NullS);
+  }
+  if (action->wait_for.length())
+  {
+    wtxt= strxnmov(wtxt, wend - wtxt, " WAIT_FOR ", action->wait_for.c_ptr(),
+                   NullS);
+    if (action->timeout != test_sync_default_wait_timeout)
+    {
+      wtxt+= my_snprintf(wtxt, wend - wtxt, " TIMEOUT %lu", action->timeout);
+    }
+  }
+  if (!action->signal.length() && !action->wait_for.length())
+  {
+    wtxt= strnmov(wtxt, " CLEAR", wend - wtxt);
+  }
+  else
+  {
+    if (action->execute != 1)
+    {
+      wtxt+= my_snprintf(wtxt, wend - wtxt, " EXECUTE %lu", action->execute);
+    }
+    if (action->hit_limit != 0)
+    {
+      wtxt+= my_snprintf(wtxt, wend - wtxt, " HIT_LIMIT %lu",
+                         action->hit_limit);
+    }
+  }
+  /*
+    If (wtxt == wend) string may not be terminated.
+    There is one byte left for an emergency termination.
+  */
+  *wtxt= '\0';
+  push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR,
+               warntext);
+
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Print a warning with action values.
+
+*/
+
+static void test_sync_warn_actions(THD *thd)
+{
+  st_test_sync_control  *ts_control= thd->test_sync_control;
+  uint                  idx;
+  DBUG_ENTER("test_sync_warn_actions");
+
+  if (!ts_control)
+    return;
+
+  for (idx= 0; idx < NUMBER_OF_SYNC_POINTS; idx++)
+    if (ts_control->ts_static_action[idx].activation_count)
+      test_sync_print_warn_action(thd, "static action ",
+                                  ts_control->ts_static_action + idx);
+
+  for (idx= 0; idx < ts_control->ts_dynamic_active; idx++)
+    if (ts_control->ts_dynamic_action[idx].activation_count)
+      test_sync_print_warn_action(thd, "dynamc action ",
+                                  ts_control->ts_dynamic_action + idx);
+
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Extract a token from a string.
+
+  @param[out]   token_p         returns start of token
+  @param[out]   token_length_p  returns length of token
+  @param[in]    ptr             current string pointer
+
+  @return       string pointer or NULL
+    @retval     != NULL         ptr behind token terminator or at string end
+    @retval     NULL            no token found in remainder of string
+
+  @note
+    This function assumes that the string is in system_charset_info,
+    that this charset is single byte for ASCII NUL ('\0'), that no
+    character except of ASCII NUL ('\0') contains a byte with value 0,
+    and that ASCII NUL ('\0') is used as the string terminator.
+
+  @description
+
+    This function skips space characters at string begin.
+
+    It returns a pointer to the first non-space character in *token_p.
+
+    If no non-space character is found before the string terminator
+    ASCII NUL ('\0'), the function returns NULL. *token_p and
+    *token_length_p remain unchanged in this case (they are not set).
+
+    The function takes a space character or an ASCII NUL ('\0') as a
+    terminator of the token. The space character could be multi-byte.
+
+    It returns the length of the token in bytes, excluding the
+    terminator, in *token_length_p.
+
+    If the terminator of the token is ASCII NUL ('\0'), it returns a
+    pointer to the terminator (string end).
+
+    If the terminator is a space character, it replaces the the first
+    byte of the terminator character by ASCII NUL ('\0'), skips the (now
+    corrupted) terminator character, and skips all following space
+    characters. It returns a pointer to the next non-space character or
+    to the string terminator ASCII NUL ('\0').
+*/
+
+static char *test_sync_token(char **token_p, uint *token_length_p, char *ptr)
+{
+  /* Skip leading space */
+  while (my_isspace(system_charset_info, *ptr))
+    ptr+= my_mbcharlen(system_charset_info, (uchar) *ptr);
+
+  if (!*ptr)
+  {
+    ptr= NULL;
+    goto end;
+  }
+
+  /* Get token start. */
+  *token_p= ptr;
+
+  /* Find token end. */
+  while (*ptr && !my_isspace(system_charset_info, *ptr))
+    ptr+= my_mbcharlen(system_charset_info, (uchar) *ptr);
+
+  /* Get token length. */
+  *token_length_p= ptr - *token_p;
+
+  /* If necessary, terminate token. */
+  if (*ptr)
+  {
+    /* Get terminator character length. */
+    uint mbspacelen= my_mbcharlen(system_charset_info, (uchar) *ptr);
+
+    /* Terminate token. */
+    *ptr= '\0';
+
+    /* Skip the terminator. */
+    ptr+= mbspacelen;
+
+    /* Skip trailing space */
+    while (my_isspace(system_charset_info, *ptr))
+      ptr+= my_mbcharlen(system_charset_info, (uchar) *ptr);
+  }
+
+ end:
+  return ptr;
+}
+
+
+/**
+  Extract a number from a string.
+
+  @param[out]   number_p        returns number
+  @param[in]    ptr             current string pointer
+
+  @return       string pointer or NULL
+    @retval     != NULL         ptr behind token terminator or at string end
+    @retval     NULL            no token found or token is not valid number
+
+  @note
+    The same assumptions about charset apply as for test_sync_token().
+
+  @description
+
+    This function fetches a token from the string and converts it
+    into a number.
+
+    If there is no token left in the string, or the token is not a valid
+    decimal number, NULL is returned. The result in *number_p is
+    undefined in this case.
+*/
+
+static char *test_sync_number(ulong *number_p, char *ptr)
+{
+  char                  *ept;
+  char                  *token;
+  uint                  token_length;
+
+  /* Get token from string. */
+  if (!(ptr= test_sync_token(&token, &token_length, ptr)))
+    goto end;
+
+  *number_p= strtoul(token, &ept, 10);
+  if (*ept)
+    ptr= NULL;
+
+ end:
+  return ptr;
+}
+
+
+/**
+  Evaluate a test synchronization action string.
+
+  @param[in]        thd             thread handle
+  @param[in,out]    action_str      action string to receive '\0' terminators
+
+  @return           status
+    @retval         FALSE           ok
+    @retval         TRUE            error
+
+  @description
+
+    Parse action string, build a test sync request, set an action from it.
+*/
+
+bool test_sync_eval_action(THD *thd, char *action_str)
+{
+  const char            *errmsg;
+  char                  *ptr;
+  char                  *token;
+  uint                  token_length;
+  st_test_sync_request  request;
+  DBUG_ENTER("test_sync_eval_action");
+
+  bzero(&request, sizeof(request));
+
+  /*
+    Get test synchronization point name.
+  */
+  if (!(ptr= test_sync_token(&token, &token_length, action_str)))
+  {
+    errmsg= "Missing synchronization point name";
+    goto err;
+  }
+  request.sync_point.str= token;
+  request.sync_point.length= token_length;
+
+  /*
+    Get action.
+  */
+  if (!(ptr= test_sync_token(&token, &token_length, ptr)))
+  {
+    /* token unchanged. It still contains sync point name. */
+    errmsg= "Missing action after synchronization point name '%.64s'";
+    goto err;
+  }
+
+  /*
+    Check for pseudo actions first.
+  */
+
+  /*
+    Try CLEAR.
+  */
+  if (!my_strcasecmp(system_charset_info, token, "CLEAR"))
+  {
+    /* It is CLEAR. Nothing must follow it. */
+    if (*ptr)
+    {
+      errmsg= "Nothing must follow action CLEAR";
+      goto err;
+    }
+
+    /* Set action. */
+    goto set_action;
+  }
+
+  /*
+    Try RESET.
+  */
+  if (!my_strcasecmp(system_charset_info, token, "RESET"))
+  {
+    /* It is RESET. Nothing must follow it. */
+    if (*ptr)
+    {
+      errmsg= "Nothing must follow action RESET";
+      goto err;
+    }
+
+    /* Reset all actions and global signal. */
+    test_sync_reset(thd);
+    goto end;
+  }
+
+  /*
+    Try WARN_ACTIONS.
+  */
+  if (!my_strcasecmp(system_charset_info, token, "WARN_ACTIONS"))
+  {
+    /* It is WARN_ACTIONS. Nothing must follow it. */
+    if (*ptr)
+    {
+      errmsg= "Nothing must follow action WARN_ACTIONS";
+      goto err;
+    }
+
+    /* Print a warning per action. */
+    test_sync_warn_actions(thd);
+    goto end;
+  }
+
+  /*
+    Try TEST_SYNCHRONIZE.
+  */
+  if (!my_strcasecmp(system_charset_info, token, "TEST_SYNCHRONIZE"))
+  {
+    /* It is TEST_SYNCHRONIZE. Nothing must follow it. */
+    if (*ptr)
+    {
+      errmsg= "Nothing must follow action TEST_SYNCHRONIZE";
+      goto err;
+    }
+
+    /* Execute the dynamic synchronization point. */
+    test_synchronize(thd, request.sync_point.str, request.sync_point.length);
+    goto end;
+  }
+
+  /*
+    Now check for real sync point actions.
+  */
+
+  /*
+    Try SIGNAL.
+  */
+  if (!my_strcasecmp(system_charset_info, token, "SIGNAL"))
+  {
+    /* It is SIGNAL. Signal name must follow. */
+    if (!(ptr= test_sync_token(&token, &token_length, ptr)))
+    {
+      errmsg= "Missing signal name after action SIGNAL";
+      goto err;
+    }
+    request.signal.str= token;
+    request.signal.length= token_length;
+
+    /* Set default for EXECUTE option. */
+    request.execute= 1;
+
+    /* Get next token. If none follows, set action. */
+    if (!(ptr= test_sync_token(&token, &token_length, ptr)))
+      goto set_action;
+  }
+
+  /*
+    Try WAIT_FOR.
+  */
+  if (!my_strcasecmp(system_charset_info, token, "WAIT_FOR"))
+  {
+    /* It is WAIT_FOR. Wait_for signal name must follow. */
+    if (!(ptr= test_sync_token(&token, &token_length, ptr)))
+    {
+      errmsg= "Missing signal name after action WAIT_FOR";
+      goto err;
+    }
+    request.wait_for.str= token;
+    request.wait_for.length= token_length;
+
+    /* Set default for EXECUTE and TIMEOUT options. */
+    request.execute= 1;
+    request.timeout= test_sync_default_wait_timeout;
+
+    /* Get next token. If none follows, set action. */
+    if (!(ptr= test_sync_token(&token, &token_length, ptr)))
+      goto set_action;
+
+    /*
+      Try TIMEOUT.
+    */
+    if (!my_strcasecmp(system_charset_info, token, "TIMEOUT"))
+    {
+      /* It is TIMEOUT. Number must follow. */
+      if (!(ptr= test_sync_number(&request.timeout, ptr)))
+      {
+        errmsg= "Missing valid number after TIMEOUT";
+        goto err;
+      }
+
+      /* Get next token. If none follows, set action. */
+      if (!(ptr= test_sync_token(&token, &token_length, ptr)))
+        goto set_action;
+    }
+  }
+
+  /*
+    Try EXECUTE.
+  */
+  if (!my_strcasecmp(system_charset_info, token, "EXECUTE"))
+  {
+    /* EXECUTE requires either SIGNAL and/or WAIT_FOR to be present. */
+    if (!request.signal.length && !request.wait_for.length)
+    {
+      errmsg= "Missing action before EXECUTE";
+      goto err;
+    }
+
+    /* Number must follow. */
+    if (!(ptr= test_sync_number(&request.execute, ptr)))
+    {
+      errmsg= "Missing valid number after EXECUTE";
+      goto err;
+    }
+
+    /* Get next token. If none follows, set action. */
+    if (!(ptr= test_sync_token(&token, &token_length, ptr)))
+      goto set_action;
+  }
+
+  /*
+    Try HIT_LIMIT.
+  */
+  if (!my_strcasecmp(system_charset_info, token, "HIT_LIMIT"))
+  {
+    /* Number must follow. */
+    if (!(ptr= test_sync_number(&request.hit_limit, ptr)))
+    {
+      errmsg= "Missing valid number after HIT_LIMIT";
+      goto err;
+    }
+
+    /* Get next token. If none follows, set action. */
+    if (!(ptr= test_sync_token(&token, &token_length, ptr)))
+      goto set_action;
+  }
+
+  errmsg= "Illegal or out of order stuff: '%.64s'";
+
+ err:
+  my_printf_error(ER_PARSE_ERROR, errmsg, MYF(0), token);
+  DBUG_RETURN(TRUE);
+
+ set_action:
+  DBUG_RETURN(test_sync_set_action(thd, &request));
+
+ end:
+  DBUG_RETURN(FALSE);
+}
+
+
+/**
+  Check if the system variable 'test_synchronize' can be set.
+
+  @param[in]    thd             thread handle
+  @param[in]    var             set variable request
+
+  @return       status
+    @retval     FALSE           ok, variable can be set
+    @retval     TRUE            error, variable cannot be set
+*/
+
+bool sys_var_test_synchronize::check(THD *thd, set_var *var)
+{
+  DBUG_ENTER("sys_var_test_synchronize::check");
+
+  /* Variable can be set for the session only. */
+  if (var->type == OPT_GLOBAL)
+  {
+    my_error(ER_WRONG_OBJECT, MYF(0), "SESSION", name, "global variable");
+    DBUG_RETURN(TRUE);
+  }
+
+  /*
+    Do not check for disabled facility. Test result should not
+    unnecessarily differ from enabled facility.
+  */
+
+  /*
+    Facility requires SUPER privilege. Sync points could be inside
+    global mutexes (e.g. LOCK_open). Waiting there forever would
+    stall the whole server.
+  */
+  DBUG_RETURN(check_global_access(thd, SUPER_ACL));
+}
+
+
+/**
+  Set the system variable 'test_synchronize'.
+
+  @param[in]    thd             thread handle
+  @param[in]    var             set variable request
+
+  @return       status
+    @retval     FALSE           ok, variable is set
+    @retval     TRUE            error, variable could not be set
+
+  @note
+
+    "Setting" of the system variable 'test_synchronize' does not mean to
+    assign a value to it as usual. Instead a test synchronization action
+    is parsed from the input string and stored apart from the variable
+    value.
+
+    The value of the system variable 'test_synchronize'
+*/
+
+bool sys_var_test_synchronize::update(THD *thd, set_var *var)
+{
+  char empty= '\0';
+  char *val_str= var ? var->value->str_value.c_ptr() : &empty;
+  DBUG_ENTER("sys_var_test_synchronize::update");
+
+  DBUG_PRINT("test_sync", ("set action: '%s'", val_str));
+
+  DBUG_RETURN(test_sync_eval_action(thd, val_str));
+}
+
+/**
+  Retrieve the value of the system variable 'test_synchronize'.
+
+  @param[in]    thd             thread handle
+  @param[in]    type            variable type, unused
+  @param[in]    base            variable base, unused
+
+  @return       string
+
+  @note
+
+    The value of the system variable 'test_synchronize' reflects if
+    the facility is enabled ("ON") or disabled (default, "OFF").
+
+    When "ON", the current signal is added.
+*/
+
+uchar *sys_var_test_synchronize::value_ptr(THD *thd,
+                                           enum_var_type type
+                                           __attribute__((unused)),
+                                           LEX_STRING *base
+                                           __attribute__((unused)))
+{
+  char *value;
+  DBUG_ENTER("sys_var_test_synchronize::value_ptr");
+
+  if (opt_test_synchronize)
+  {
+    static char on[]= "ON - current signal: '";
+
+    value= (char*) alloc_root(thd->mem_root, sizeof(on) /* includes '\0' */ +
+                              SIGNAL_test_sync.length() + 1 /* for '\'' */ );
+    strxmov(value, on, SIGNAL_test_sync.c_ptr(), "'", NullS);
+  }
+  else
+    value= strmake_root(thd->mem_root, STRING_WITH_LEN("OFF"));
+
+  DBUG_RETURN((uchar*) value);
+}
+
+
+/**
+  Execute requested actions at an synchronization point.
+
+  @param[in]    thd                 thread handle
+  @param[in]    action              action to be executed
+
+  @note
+    This is to be called only if activation count > 0.
+*/
+
+static void test_sync_execute(THD *thd, st_test_sync_action *action)
+{
+  const char *tsp_name= action->sync_point.c_ptr();
+  DBUG_ENTER("test_sync_execute");
+  DBUG_PRINT("test_sync", ("sync_point: '%s'  activation_count: %lu  "
+                           "hit_limit: %lu  execute: %lu  timeout: %lu  "
+                           "signal: '%s'  wait_for: '%s'",
+                           tsp_name, action->activation_count,
+                           action->hit_limit, action->execute, action->timeout,
+                           action->signal.c_ptr(), action->wait_for.c_ptr()));
+  DBUG_ASSERT(action->activation_count);
+
+  if (action->execute)
+  {
+    if (action->signal.length())
+    {
+      /*
+        Protect copying of the signal string to the global string
+        to avoid race conditions during test case development.
+        After approaching a clean test case it should not happen
+        that two threads try to signal at the same time.
+      */
+      pthread_mutex_lock(&LOCK_test_sync);
+      /* Copy the signal to the global variable. */
+      SIGNAL_test_sync.copy(action->signal);
+      /* Wake threads waiting in a sync point. */
+      pthread_cond_broadcast(&COND_test_sync);
+      DBUG_PRINT("test_sync_exec", ("signal '%s'  at: '%s'",
+                                    action->signal.c_ptr(), tsp_name));
+      pthread_mutex_unlock(&LOCK_test_sync);
+    }
+
+    if (action->wait_for.length())
+    {
+      const char      *old_proc_info;
+      int             error= 0;
+      struct timespec abstime;
+
+      pthread_mutex_lock(&LOCK_test_sync);
+      old_proc_info= thd->enter_cond(&COND_test_sync, &LOCK_test_sync,
+                                     tsp_name);
+
+      set_timespec(abstime, action->timeout);
+      DBUG_PRINT("test_sync_exec", ("wait for '%s'  at: '%s'  curr: '%s'",
+                                    action->wait_for.c_ptr(), tsp_name,
+                                    SIGNAL_test_sync.c_ptr()));
+
+      /* Wait until global signal string matches the wait_for string. */
+      while (stringcmp(&SIGNAL_test_sync, &action->wait_for) &&
+             !thd->killed)
+      {
+        error= pthread_cond_timedwait(&COND_test_sync, &LOCK_test_sync,
+                                      &abstime);
+        if (error == ETIMEDOUT || error == ETIME)
+        {
+          push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+                       "TEST_SYNCHRONIZE sync point timed out");
+          break;
+        }
+        error= 0;
+      }
+      DBUG_PRINT("test_sync_exec", ("%s from '%s'  at: '%s'",
+                                    error ? "timeout" : "resume",
+                                    action->wait_for.c_ptr(), tsp_name));
+
+      /*
+        @todo Maybe we could do here: SIGNAL_test_sync.length(0); or
+        SIGNAL_test_sync.copy(STRING_WITH_LEN("empty"), system_charset_info);
+      */
+      thd->exit_cond(old_proc_info);
+    }
+
+    action->execute--;
+  }
+
+  /* hit_limit is zero for infinite. Don't decrement unconditionally. */
+  if (action->hit_limit)
+  {
+    if (!--action->hit_limit)
+    {
+      thd->killed= THD::KILL_QUERY;
+      my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
+      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_UNKNOWN_ERROR,
+                   "TEST_SYNCHRONIZE sync point reached hit limit");
+    }
+    DBUG_PRINT("test_sync_exec", ("hit_limit: %lu  at: '%s'",
+                                  action->hit_limit, tsp_name));
+  }
+
+  action->activation_count--;
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Execute requested actions at an synchronization point.
+
+  @param[in]    thd                 thread handle
+  @param[in]    test_sync_point     synchronization point identifier
+*/
+
+void test_synchronize(THD *thd, enum enum_test_sync_point test_sync_point)
+{
+  DBUG_ASSERT(test_sync_point < NUMBER_OF_SYNC_POINTS);
+
+  /* During bootstrap or in non-THD threads, thd can be NULL. */
+  if (thd && thd->test_sync_control)
+  {
+    st_test_sync_action *action=
+      thd->test_sync_control->ts_static_action + test_sync_point;
+
+    if (action->activation_count)
+      test_sync_execute(thd, action);
+  }
+}
+
+
+/**
+  Execute requested actions at an synchronization point.
+
+  @param[in]    thd                 thread handle
+  @param[in]    user_var_name       name of user variable
+  @param[in]    name_len            length of name of user variable
+*/
+
+void test_synchronize(THD *thd, const char *sync_point_name, size_t name_len)
+{
+  if (thd && thd->test_sync_control)
+  {
+    st_test_sync_control *ts_control= thd->test_sync_control;
+    int tsp_idx;
+    DBUG_ASSERT(ts_control->ts_dynamic_active <=
+                ts_control->ts_dynamic_allocated);
+
+    /* If any dynamic action exists, find it in the array. */
+    if (ts_control->ts_dynamic_action && ts_control->ts_dynamic_active
&&
+        ((tsp_idx= test_sync_find(ts_control->ts_dynamic_action,
+                                  ts_control->ts_dynamic_active,
+                                  sync_point_name, name_len)) >= 0))
+    {
+      /* Found action. */
+      st_test_sync_action *action= ts_control->ts_dynamic_action + tsp_idx;
+
+      /* Execute sync point action. */
+      if (action->activation_count)
+        test_sync_execute(thd, action);
+
+      /* Remove dynamic action when activation_count became zero. */
+      if (!action->activation_count)
+        test_sync_remove_action(ts_control, action);
+    }
+  }
+}
+
diff -Nrup a/sql/mysql_test_sync.h b/sql/mysql_test_sync.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/mysql_test_sync.h	2008-01-09 17:11:39 +01:00
@@ -0,0 +1,76 @@
+/* Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+/*
+  Test synchronization Facility. See mysql_test_sync.cc.
+*/
+
+#ifndef _mysql_test_sync_h_
+#define _mysql_test_sync_h_
+
+/*
+  Test synchronization enums must match test_sync_point_names in
+  mysql_test_sync.cc.
+*/
+enum enum_test_sync_point
+{
+  /* Special sync point. Never executed. For syntax checks. */
+  P0,
+
+  /* Special sync point for immediate execution. */
+  NOW,
+
+  /* Normal sync points. */
+  BEFORE_OPEN_TABLE_WAIT_REFRESH,
+  BEFORE_LOCK_TABLES_TAKES_LOCK,
+  WAIT_FOR_LOCK,
+  MYSQL_LOCK_RETRY,
+  AFTER_INSERT,
+  AFTER_FLUSH_UNLOCK,
+  AFTER_ADMIN_FLUSH,
+  BEFORE_ACOS_FUNCTION,
+  BEFORE_WAIT_LOCKED_TNAME,
+  BEFORE_MYISAMMRG_ATTACH,
+  BEFORE_MYISAMMRG_STORE_LOCK,
+
+  /* Do not add sync points below this line. */
+  NUMBER_OF_SYNC_POINTS
+};
+
+/* Macro to be put in the code at synchronization points. */
+#define TEST_SYNCHRONIZE(_thd_, _sync_point_id_)                        \
+          do { if (unlikely(opt_test_synchronize))                      \
+                 test_synchronize(_thd_, _sync_point_id_); } while (0)
+
+/* Macro to be put in the code at synchronization points. */
+#define TEST_SYNCHRONIZE_STR(_thd_, _sync_point_name_)                  \
+          do { if (unlikely(opt_test_synchronize))                      \
+                 test_synchronize(_thd_, STRING_WITH_LEN(_sync_point_name_)); \
+             } while (0)
+
+/* Command line option --test-synchronize. See mysqld.cc. */
+extern int opt_test_synchronize;
+
+extern int  test_sync_init(void);
+extern void test_sync_end(void);
+extern void test_sync_init_thread(THD *thd);
+extern void test_sync_end_thread(THD *thd);
+extern bool test_sync_eval_action(THD *thd, char *action_str);
+extern void test_synchronize(THD *thd,
+                             enum enum_test_sync_point test_sync_point);
+extern void test_synchronize(THD *thd, const char *sync_point_name,
+                             size_t name_len);
+
+#endif /* _mysql_test_sync_h_ */
diff -Nrup a/sql/mysqld.cc b/sql/mysqld.cc
--- a/sql/mysqld.cc	2007-12-15 14:59:38 +01:00
+++ b/sql/mysqld.cc	2008-01-09 17:11:38 +01:00
@@ -426,6 +426,7 @@ my_bool lower_case_file_system= 0;
 my_bool opt_large_pages= 0;
 my_bool opt_myisam_use_mmap= 0;
 uint    opt_large_page_size= 0;
+int     opt_test_synchronize= 0;
 my_bool opt_old_style_user_limits= 0, trust_function_creators= 0;
 /*
   True if there is at least one per-hour limit for some user, so we should
@@ -1260,6 +1261,9 @@ void clean_up(bool print_message)
 #ifdef USE_REGEX
   my_regex_end();
 #endif
+  /* End the test synchronization facility. See mysql_test_sync.cc */
+  if (opt_test_synchronize)
+    test_sync_end();
 
 #if !defined(EMBEDDED_LIBRARY)
   if (!opt_bootstrap)
@@ -3017,6 +3021,10 @@ static int init_common_variables(const c
   sys_var_slow_log_path.value= my_strdup(s, MYF(0));
   sys_var_slow_log_path.value_length= strlen(s);
 
+  /* Initialize the test synchronization facility. See mysql_test_sync.cc */
+  if (opt_test_synchronize && test_sync_init())
+    return 1; /* purecov: tested */
+
   if (use_temp_pool && bitmap_init(&temp_pool,0,1024,1))
     return 1;
   if (my_database_names_init())
@@ -5142,7 +5150,8 @@ enum options_mysqld
   OPT_SECURE_FILE_PRIV,
   OPT_MIN_EXAMINED_ROW_LIMIT,
   OPT_LOG_SLOW_SLAVE_STATEMENTS,
-  OPT_OLD_MODE
+  OPT_OLD_MODE,
+  OPT_TEST_SYNCHRONIZE
 };
 
 
@@ -5884,6 +5893,12 @@ log and this option does nothing anymore
    "Decision to use in heuristic recover process. Possible values are COMMIT or
ROLLBACK.",
    (uchar**) &opt_tc_heuristic_recover, (uchar**) &opt_tc_heuristic_recover,
    0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  {"test-synchronize", OPT_TEST_SYNCHRONIZE,
+   "Enable the TEST SYNCHRONIZE facility "
+   "and optionally specify a default wait timeout in seconds. "
+   "A negative value keeps the facility disabled.",
+   (uchar**) &opt_test_synchronize, 0,
+   0, GET_INT, OPT_ARG, 0, -1, INT_MAX, 0, 0, 0},
   {"temp-pool", OPT_TEMP_POOL,
    "Using this option will cause most temporary files created to use a small set of
names, rather than a unique name for each new file.",
    (uchar**) &use_temp_pool, (uchar**) &use_temp_pool, 0, GET_BOOL, NO_ARG, 1,
@@ -7134,6 +7149,7 @@ static void mysql_init_variables(void)
   bzero((uchar*) &mysql_tmpdir_list, sizeof(mysql_tmpdir_list));
   bzero((char *) &global_status_var, sizeof(global_status_var));
   opt_large_pages= 0;
+  opt_test_synchronize= 0;
   key_map_full.set_all();
 
   /* Character sets */
@@ -7803,6 +7819,27 @@ mysqld_get_one_option(int optid,
   case OPT_LOWER_CASE_TABLE_NAMES:
     lower_case_table_names= argument ? atoi(argument) : 1;
     lower_case_table_names_used= 1;
+    break;
+
+  case OPT_TEST_SYNCHRONIZE:
+    /* Test Synchronization Facility. See mysql_test_sync.cc. */
+    if (!argument)
+      opt_test_synchronize= 300; /* Default timeout for WAIT_FOR action. */
+    else
+    {
+      /*
+        External and internal representations for the option differ:
+        External        Internal        Comment
+        > 0             > 0             timeout 1 .. INT_MAX
+        no value        300             default timeout
+        == 0            -1              timeout 0
+        < 0             0               facility disabled
+      */
+      if (!opt_test_synchronize)                /* timeout 0 */
+        opt_test_synchronize= -1;
+      else if (opt_test_synchronize < 0)        /* disabled */
+        opt_test_synchronize= 0;
+    }
     break;
   }
   return 0;
diff -Nrup a/sql/set_var.cc b/sql/set_var.cc
--- a/sql/set_var.cc	2007-12-14 14:01:10 +01:00
+++ b/sql/set_var.cc	2008-01-09 17:11:39 +01:00
@@ -452,6 +452,10 @@ static sys_var_long_ptr	sys_table_cache_
 					     &table_cache_size);
 static sys_var_long_ptr	sys_table_lock_wait_timeout(&vars, "table_lock_wait_timeout",
                                                     &table_lock_wait_timeout);
+
+/* Test synchronization facility. Implemented in mysql_test_sync.cc. */
+static sys_var_test_synchronize sys_test_synchronize(&vars, "test_synchronize");
+
 static sys_var_long_ptr	sys_thread_cache_size(&vars, "thread_cache_size",
 					      &thread_cache_size);
 #if HAVE_POOL_OF_THREADS == 1
diff -Nrup a/sql/set_var.h b/sql/set_var.h
--- a/sql/set_var.h	2007-12-14 14:01:10 +01:00
+++ b/sql/set_var.h	2008-01-09 17:11:39 +01:00
@@ -559,6 +559,19 @@ public:
   uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *b);
 };
 
+/* Test synchronization facility. Implemented in mysql_test_sync.cc. */
+class sys_var_test_synchronize :public sys_var_thd
+{
+public:
+  sys_var_test_synchronize(sys_var_chain *chain, const char *name_arg)
+    :sys_var_thd(name_arg)
+  { chain_sys_var(chain); }
+  bool check(THD *thd, set_var *var);
+  bool update(THD *thd, set_var *var);
+  SHOW_TYPE show_type() { return SHOW_CHAR; }
+  bool check_update_type(Item_result type) { return type != STRING_RESULT; }
+  uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+};
 
 
 /* some variables that require special handling */
diff -Nrup a/sql/sql_base.cc b/sql/sql_base.cc
--- a/sql/sql_base.cc	2007-12-13 19:33:01 +01:00
+++ b/sql/sql_base.cc	2008-01-09 17:11:39 +01:00
@@ -1025,6 +1025,8 @@ bool close_cached_tables(THD *thd, TABLE
     close_old_data_files(thd,thd->open_tables,1,1);
     mysql_ha_flush(thd);
 
+    TEST_SYNCHRONIZE(thd, AFTER_FLUSH_UNLOCK);
+
     bool found=1;
     /* Wait until all threads has closed all the tables we had locked */
     DBUG_PRINT("info",
@@ -2897,6 +2899,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *
       */
       if (table->in_use != thd)
       {
+        TEST_SYNCHRONIZE(thd, BEFORE_OPEN_TABLE_WAIT_REFRESH);
+
         /* wait_for_conditionwill unlock LOCK_open for us */
         wait_for_condition(thd, &LOCK_open, &COND_refresh);
       }
@@ -5169,6 +5173,8 @@ int lock_tables(THD *thd, TABLE_LIST *ta
         thd->set_current_stmt_binlog_row_based_if_mixed();
       }
     }
+
+    TEST_SYNCHRONIZE(thd, BEFORE_LOCK_TABLES_TAKES_LOCK);
 
     if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start),
                                         lock_flag, need_reopen)))
diff -Nrup a/sql/sql_class.cc b/sql/sql_class.cc
--- a/sql/sql_class.cc	2007-12-14 14:01:10 +01:00
+++ b/sql/sql_class.cc	2008-01-09 17:11:39 +01:00
@@ -493,7 +493,7 @@ THD::THD()
    bootstrap(0),
    derived_tables_processing(FALSE),
    spcont(NULL),
-   m_lip(NULL)
+   m_lip(NULL), test_sync_control(0)
 {
   ulong tmp;
 
@@ -720,6 +720,9 @@ void THD::init(void)
   update_charset();
   reset_current_stmt_binlog_row_based();
   bzero((char *) &status_var, sizeof(status_var));
+
+  /* Initialize the test synchronization facility. See mysql_test_sync.cc */
+  test_sync_init_thread(this);
 }
 
 
@@ -795,6 +798,10 @@ void THD::cleanup(void)
     lock=locked_tables; locked_tables=0;
     close_thread_tables(this);
   }
+
+  /* End the test synchronization facility. See mysql_test_sync.cc */
+  test_sync_end_thread(this);
+
   mysql_ha_cleanup(this);
   delete_dynamic(&user_var_events);
   hash_free(&user_vars);
diff -Nrup a/sql/sql_class.h b/sql/sql_class.h
--- a/sql/sql_class.h	2007-12-14 14:01:10 +01:00
+++ b/sql/sql_class.h	2008-01-09 17:11:39 +01:00
@@ -1679,6 +1679,9 @@ public:
   partition_info *work_part_info;
 #endif
 
+  /* Test synchronization facility. */
+  struct st_test_sync_control *test_sync_control;
+
   THD();
   ~THD();
 
diff -Nrup a/sql/sql_parse.cc b/sql/sql_parse.cc
--- a/sql/sql_parse.cc	2007-12-17 09:02:20 +01:00
+++ b/sql/sql_parse.cc	2008-01-09 17:11:39 +01:00
@@ -2877,6 +2877,7 @@ end_with_restore_list:
       thd->first_successful_insert_id_in_cur_stmt=
         thd->first_successful_insert_id_in_prev_stmt;
 
+    TEST_SYNCHRONIZE(thd, AFTER_INSERT);
     break;
   }
   case SQLCOM_REPLACE_SELECT:
diff -Nrup a/sql/sql_table.cc b/sql/sql_table.cc
--- a/sql/sql_table.cc	2007-12-24 13:06:58 +01:00
+++ b/sql/sql_table.cc	2008-01-09 17:11:39 +01:00
@@ -4221,6 +4221,7 @@ static bool mysql_admin_table(THD* thd, 
                               RTFC_WAIT_OTHER_THREAD_FLAG |
                               RTFC_CHECK_KILLED_FLAG);
       thd->exit_cond(old_message);
+      TEST_SYNCHRONIZE(thd, AFTER_ADMIN_FLUSH);
       DBUG_EXECUTE_IF("wait_in_mysql_admin_table", wait_for_kill_signal(thd););
       if (thd->killed)
 	goto err;
diff -Nrup a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc
--- a/storage/myisammrg/ha_myisammrg.cc	2007-12-13 12:56:00 +01:00
+++ b/storage/myisammrg/ha_myisammrg.cc	2008-01-09 17:11:39 +01:00
@@ -456,6 +456,8 @@ int ha_myisammrg::attach_children(void)
   DBUG_PRINT("myrg", ("test_if_locked: %u", this->test_if_locked));
   DBUG_ASSERT(!this->file->children_attached);
 
+  TEST_SYNCHRONIZE(current_thd, BEFORE_MYISAMMRG_ATTACH);
+
   /*
     Initialize variables that are used, modified, and/or set by
     myisammrg_attach_children_callback().
@@ -922,6 +924,8 @@ THR_LOCK_DATA **ha_myisammrg::store_lock
 					 enum thr_lock_type lock_type)
 {
   MYRG_TABLE *open_table;
+
+  TEST_SYNCHRONIZE(thd, BEFORE_MYISAMMRG_STORE_LOCK);
 
   /*
     This method can be called while another thread is attaching the
Thread
bk commit into 5.1 tree (istruewing:1.2650)Ingo Struewing9 Jan