Try these...it was the broadcast that caused the deadlock.
> -----Original Message-----
> From: Rafal Somla [mailto:rsomla@stripped]
> Sent: Wednesday, November 14, 2007 12:25 PM
> To: Chuck Bell
> Cc: commits@stripped
> Subject: Re: bk commit into 6.0 tree (cbell:1.2660) BUG#31383
>
> Chuck,
>
> I think the best solution is the one I proposed before:
>
> - kill_locking_thread() *always* sets lock_state to LOCK_SIGNAL
> - destructor calls kill_locking_thread()
>
> Cancel() can do nothing before after it the destructor will
> be called and do the cleaning job.
>
> This will solve the issue and we don't need to introduce new
> states and think again about all possible race conditions.
>
> Rafal
>
> Chuck Bell wrote:
> > Rafal,
> >
> > Here's my changes for the race condition.
> >
> > ===== sql/backup/be_default.h 1.3 vs edited =====
> > --- 1.3/sql/backup/be_default.h 2007-11-14 11:33:11 -05:00
> > +++ edited/sql/backup/be_default.h 2007-11-14 11:26:52 -05:00
> > @@ -99,7 +99,11 @@
> > result_t get_data(Buffer &buf);
> > result_t lock() { return backup::OK; };
> > result_t unlock() { return backup::OK; };
> > - result_t cancel() { return backup::OK; };
> > + result_t cancel()
> > + {
> > + lock_state= LOCK_ABORT;
> > + return backup::OK;
> > + };
> > TABLE_LIST *get_table_list() { return all_tables; }
> > void free() { delete this; };
> > result_t prelock();
> > ===== sql/backup/be_thread.cc 1.1 vs edited =====
> > --- 1.1/sql/backup/be_thread.cc 2007-11-14 11:33:11 -05:00
> > +++ edited/sql/backup/be_thread.cc 2007-11-14 11:30:15 -05:00
> > @@ -140,6 +140,9 @@
> > goto end2;
> > }
> >
> > + if (drv->lock_state == LOCK_ABORT)
> > + goto end2;
> > +
> > /*
> > As locking tables can be a long operation, we need to support
> > killing the thread. In this case, we need to close the
> tables @@
> > -158,16 +161,19 @@
> > goto end;
> > }
> >
> > - drv->lock_state= LOCK_ACQUIRED;
> > + if (drv->lock_state == LOCK_ABORT)
> > + goto end;
> >
> > /*
> > Part of work is done. Rest until woken up.
> > We wait if the thread is not killed and the driver has
> not signaled us.
> > */
> > pthread_mutex_lock(&drv->THR_LOCK_driver_thread);
> > + drv->lock_state= LOCK_ACQUIRED;
> > thd->enter_cond(&drv->COND_driver_thread_wait,
> > &drv->THR_LOCK_driver_thread,
> > "Online backup driver thread: holding table
> > locks");
> > - while (!thd->killed && (drv->lock_state != LOCK_SIGNAL))
> > + while (!thd->killed && (drv->lock_state != LOCK_SIGNAL)
> &&
> > + (drv->lock_state != LOCK_ABORT))
> > pthread_cond_wait(&drv->COND_driver_thread_wait,
> > &drv->THR_LOCK_driver_thread);
> > thd->exit_cond("Online backup driver thread: terminating");
> >
> > @@ -220,6 +226,12 @@
> > Backup_thread_driver::~Backup_thread_driver()
> > {
> > /*
> > + If the locking thread is still alive, tell it to terminate.
> > + */
> > + if (lock_thd)
> > + kill_locking_thread();
> > +
> > + /*
> > If the locking thread is not finished, we need to wait until
> > it is finished so that we can destroy the mutexes
> safely knowing
> > the locking thread won't access them.
> > @@ -229,7 +241,7 @@
> > pthread_mutex_lock(&THR_LOCK_driver);
> > m_thd->enter_cond(&COND_driver_wait, &THR_LOCK_driver,
> > "Online backup driver: waiting until locking
> > thread is done");
> > - while (lock_state != LOCK_DONE)
> > + while ((lock_state != LOCK_DONE) && (lock_state != LOCK_ABORT))
> > pthread_cond_wait(&COND_driver_wait, &THR_LOCK_driver);
> > m_thd->exit_cond("Online backup driver: terminating");
> >
> > ===== sql/backup/be_thread.h 1.1 vs edited =====
> > --- 1.1/sql/backup/be_thread.h 2007-11-14 11:33:12 -05:00
> > +++ edited/sql/backup/be_thread.h 2007-11-14 11:10:22 -05:00
> > @@ -23,7 +23,8 @@
> > LOCK_ACQUIRED,
> > LOCK_DONE,
> > LOCK_ERROR,
> > - LOCK_SIGNAL
> > + LOCK_SIGNAL,
> > + LOCK_ABORT,
> > } LOCK_STATE;
> >
> > using backup::result_t;
> >
> > Chuck
> >
> >> -----Original Message-----
> >> From: Rafal Somla [mailto:rsomla@stripped]
> >> Sent: Wednesday, November 14, 2007 8:36 AM
> >> To: cbell@stripped
> >> Cc: commits@stripped
> >> Subject: Re: bk commit into 6.0 tree (cbell:1.2660) BUG#31383
> >>
> >> Hi Chuck,
> >>
> >> I checked the test and the rest of the code and everything
> looks OK.
> >> after resolving the last issue with race condition in
> >> kill_locking_thread() it will be good for push.
> >>
> >> Rafal
> >>
> >> cbell@stripped wrote:
> >>> Below is the list of changes that have just been committed into a
> >>> local 6.0 repository of cbell. When cbell 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, 2007-11-13 09:43:18-05:00,
> >> cbell@mysql_cab_desk. +13 -0
> >>> BUG#31383 : Consistent Snapshot driver not consistent
> >>>
> >>> This patch changes the default driver to use a separate
> >> thread to open and lock tables.
> >>> The default driver opens tables on the prelock() call
> >> from the kernel. The snapshot
> >>> driver initiates the CS read on the lock() call from the
> >> kernel and opens tables in the first
> >>> call to get_data() after the lock is taken. This is due
> >> to the fact that the default driver's
> >>> validity point is at open_and_lock_tables() while the
> >> snapshot driver's validity point
> >>> is at the start of the transaction.
> >>>
> >>> mysql-test/r/backup_snapshot.result@stripped, 2007-11-13
> >> 09:42:58-05:00, cbell@mysql_cab_desk. +75 -10
> >>> BUG#31383 : Consistent Snapshot driver not consistent
> >>>
> >>> New result file.
> >>>
> >>> mysql-test/t/backup_snapshot.test@stripped, 2007-11-13
> >> 09:42:59-05:00, cbell@mysql_cab_desk. +109 -9
> >>> BUG#31383 : Consistent Snapshot driver not consistent
> >>>
> >>> Test modified to use the get_lock, release_lock
> >> functions to stop the CS driver in
> >>> progress allowing simultaneous inserts from the second
> >> connection. This patch makes this test
> >>> accurately test the CS driver.
> >>>
> >>> sql/backup/CMakeLists.txt@stripped, 2007-11-13 09:43:01-05:00,
> >> cbell@mysql_cab_desk. +4 -1
> >>> BUG#31383 : Consistent Snapshot driver not consistent
> >>>
> >>> Added the new be_thread source file and dependency for backup.
> >>>
> >>> sql/backup/Makefile.am@stripped, 2007-11-13 09:43:02-05:00,
> >> cbell@mysql_cab_desk. +4 -2
> >>> BUG#31383 : Consistent Snapshot driver not consistent
> >>>
> >>> Added the new be_thread source file and dependency for backup.
> >>>
> >>> sql/backup/be_default.cc@stripped, 2007-11-13 09:43:02-05:00,
> >> cbell@mysql_cab_desk. +46 -5
> >>> BUG#31383 : Consistent Snapshot driver not consistent
> >>>
> >>> Added new methods to support using a separate thread to
> >> open and lock tables for
> >>> backup.
> >>>
> >>> sql/backup/be_default.h@stripped, 2007-11-13 09:43:03-05:00,
> >> cbell@mysql_cab_desk. +14 -12
> >>> BUG#31383 : Consistent Snapshot driver not consistent
> >>>
> >>> Added new methods to support using a separate thread to
> >> open and lock tables for
> >>> backup.
> >>>
> >>> sql/backup/be_snapshot.cc@stripped, 2007-11-13 09:43:04-05:00,
> >> cbell@mysql_cab_desk. +29 -18
> >>> BUG#31383 : Consistent Snapshot driver not consistent
> >>>
> >>> Modifies the CS driver to open and close its own tables
> >> while executing in the
> >>> kernel's thread.
> >>>
> >>> sql/backup/be_snapshot.h@stripped, 2007-11-13 09:43:05-05:00,
> >> cbell@mysql_cab_desk. +15 -4
> >>> BUG#31383 : Consistent Snapshot driver not consistent
> >>>
> >>> Added methods and variables for opening and closing tables.
> >>>
> >>> sql/backup/be_thread.cc@stripped, 2007-11-13 09:43:07-05:00,
> >> cbell@mysql_cab_desk. +295 -0
> >>> BUG#31383 : Consistent Snapshot driver not consistent
> >>>
> >>> Added new error message for error handling in threads
> >> in default and snapshot drivers.
> >>>
> >>>
> >>> sql/backup/be_thread.cc@stripped, 2007-11-13 09:43:07-05:00,
> >>> cbell@mysql_cab_desk. +0 -0
> >>>
> >>> sql/backup/be_thread.h@stripped, 2007-11-13 09:43:08-05:00,
> >> cbell@mysql_cab_desk. +79 -0
> >>> BUG#31383 : Consistent Snapshot driver not consistent
> >>>
> >>> New source file for mutex initialization and helper
> >> methods for using a thread to open
> >>> and lock tables in default and snapshot drivers.
> >>>
> >>>
> >>> sql/backup/be_thread.h@stripped, 2007-11-13 09:43:08-05:00,
> >>> cbell@mysql_cab_desk. +0 -0
> >>>
> >>> sql/backup/data_backup.cc@stripped, 2007-11-13 09:43:06-05:00,
> >> cbell@mysql_cab_desk. +0 -31
> >>> BUG#31383 : Consistent Snapshot driver not consistent
> >>>
> >>> Removed code to call open and lock tables from kernel.
> >>>
> >>> sql/share/errmsg.txt@stripped, 2007-11-13 09:43:06-05:00,
> >> cbell@mysql_cab_desk. +3 -0
> >>> BUG#31383 : Consistent Snapshot driver not consistent
> >>>
> >>> Added new error message for error handling in threads
> >> in the default driver.
> >>> sql/sql_class.h@stripped, 2007-11-13 09:43:00-05:00,
> >> cbell@mysql_cab_desk. +2 -1
> >>> BUG#31383 : Consistent Snapshot driver not consistent
> >>>
> >>> Added a new thread for backup.
> >>>
> >>> diff -Nrup a/mysql-test/r/backup_snapshot.result
> >> b/mysql-test/r/backup_snapshot.result
> >>> --- a/mysql-test/r/backup_snapshot.result 2007-11-09
> >> 16:32:09 -05:00
> >>> +++ b/mysql-test/r/backup_snapshot.result 2007-11-13
> >> 09:42:58 -05:00
> >>> @@ -11,21 +11,73 @@ INSERT INTO bup_snapshot.t1 VALUES
> ("07 INSERT
> >>> INTO bup_snapshot.t1 VALUES ("08 Some data to test"); INSERT INTO
> >>> bup_snapshot.t1 VALUES ("09 Some data to test"); INSERT INTO
> >>> bup_snapshot.t1 VALUES ("10 Some data to test");
> >>> +CREATE TABLE bup_snapshot.t2 (a int) ENGINE=MEMORY; INSERT INTO
> >>> +bup_snapshot.t2 VALUES (1), (2), (3), (4), (5);
> >>> con1: Show that the new data doesn't exist before backup.
> >>> SELECT * FROM bup_snapshot.t1 WHERE word LIKE '-%'; word SELECT
> >>> COUNT(*) FROM bup_snapshot.t1;
> >>> COUNT(*)
> >>> 10
> >>> -con1: Backing up database.
> >>> +SELECT COUNT(*) FROM bup_snapshot.t2;
> >>> +COUNT(*)
> >>> +5
> >>> +con2: Getting lock on driver.
> >>> +SELECT get_lock("backup_cs_locked", 100);
> >>> +get_lock("backup_cs_locked", 100)
> >>> +1
> >>> +con1: Backing up database. Spawn this and continue...
> >>> BACKUP DATABASE bup_snapshot TO "bup_snapshot.bak";
> >>> +con2: Wait until backup pauses then insert new data.
> >>> +INSERT INTO bup_snapshot.t1 VALUES("- Dave Mathews"); INSERT INTO
> >>> +bup_snapshot.t1 VALUES("- Yes"); INSERT INTO bup_snapshot.t1
> >>> +VALUES("- Jethro Tull"); DELETE FROM bup_snapshot.t1 WHERE
> >> word LIKE
> >>> +'10%';
> >>> +con2: Showing the data after inserts.
> >>> +SELECT * FROM bup_snapshot.t1 WHERE word LIKE '-%'; word
> >>> +- Dave Mathews
> >>> +- Yes
> >>> +- Jethro Tull
> >>> +SELECT COUNT(*) FROM bup_snapshot.t1;
> >>> +COUNT(*)
> >>> +12
> >>> +con2: Release lock on driver.
> >>> +SELECT release_lock("backup_cs_locked");
> >>> +release_lock("backup_cs_locked")
> >>> +1
> >>> Backup Summary
> >>> - header = 22 bytes
> >>> - meta-data = 95 bytes
> >>> - data = 270 bytes
> >>> + header = 29 bytes
> >>> + meta-data = 184 bytes
> >>> + data = 320 bytes
> >>> --------------
> >>> - total 387 bytes
> >>> -con2: Inserting new data.
> >>> + total 533 bytes
> >>> +con1: Dropping the database
> >>> +DROP TABLE bup_snapshot.t1;
> >>> +con1: Restoring the database
> >>> +RESTORE FROM "bup_snapshot.bak";
> >>> +Restore Summary
> >>> + header = 29 bytes
> >>> + meta-data = 184 bytes
> >>> + data = 320 bytes
> >>> + --------------
> >>> + total 533 bytes
> >>> +con1: Showing the data (no new data should be here).
> >>> +SELECT * FROM bup_snapshot.t1 WHERE word LIKE '-%'; word SELECT
> >>> +COUNT(*) FROM bup_snapshot.t1;
> >>> +COUNT(*)
> >>> +10
> >>> +SELECT COUNT(*) FROM bup_snapshot.t2;
> >>> +COUNT(*)
> >>> +5
> >>> +con2: Getting lock on driver.
> >>> +SELECT get_lock("backup_cs_reading", 100);
> >>> +get_lock("backup_cs_reading", 100)
> >>> +1
> >>> +con1: Backing up database. Spawn this and continue...
> >>> +BACKUP DATABASE bup_snapshot TO "bup_snapshot.bak";
> >>> +con2: Wait until backup pauses then insert new data.
> >>> INSERT INTO bup_snapshot.t1 VALUES("- Dave Mathews");
> INSERT INTO
> >>> bup_snapshot.t1 VALUES("- Yes"); INSERT INTO bup_snapshot.t1
> >>> VALUES("- Jethro Tull"); @@ -39,20 +91,33 @@ word SELECT
> COUNT(*)
> >>> FROM bup_snapshot.t1;
> >>> COUNT(*)
> >>> 12
> >>> +con2: Release lock on driver.
> >>> +SELECT release_lock("backup_cs_reading");
> >>> +release_lock("backup_cs_reading")
> >>> +1
> >>> +Backup Summary
> >>> + header = 29 bytes
> >>> + meta-data = 184 bytes
> >>> + data = 320 bytes
> >>> + --------------
> >>> + total 533 bytes
> >>> con1: Dropping the database
> >>> DROP TABLE bup_snapshot.t1;
> >>> con1: Restoring the database
> >>> RESTORE FROM "bup_snapshot.bak";
> >>> Restore Summary
> >>> - header = 22 bytes
> >>> - meta-data = 95 bytes
> >>> - data = 270 bytes
> >>> + header = 29 bytes
> >>> + meta-data = 184 bytes
> >>> + data = 320 bytes
> >>> --------------
> >>> - total 387 bytes
> >>> + total 533 bytes
> >>> con1: Showing the data (no new data should be here).
> >>> SELECT * FROM bup_snapshot.t1 WHERE word LIKE '-%'; word SELECT
> >>> COUNT(*) FROM bup_snapshot.t1;
> >>> COUNT(*)
> >>> 10
> >>> +SELECT COUNT(*) FROM bup_snapshot.t2;
> >>> +COUNT(*)
> >>> +5
> >>> DROP DATABASE bup_snapshot;
> >>> diff -Nrup a/mysql-test/t/backup_snapshot.test
> >> b/mysql-test/t/backup_snapshot.test
> >>> --- a/mysql-test/t/backup_snapshot.test 2007-11-06
> >> 13:32:28 -05:00
> >>> +++ b/mysql-test/t/backup_snapshot.test 2007-11-13
> >> 09:42:59 -05:00
> >>> @@ -3,9 +3,19 @@
> >>> # The test is designed to show that a consistent snapshot
> >> # backup
> >>> can be taken while data is being inserted and deleted.
> >>> #
> >>> -# TODO
> >>> -# - Make the test run the insert statements in parallel
> >> with the backup
> >>> -# command using --send and --reap and signals from
> backup code.
> >>> +# The test is testing the driver to ensure it is entering a #
> >>> +consistent read state during the backup. There are several #
> >>> +breakpoints in the code that can be used. The two most #
> >> useful ones
> >>> +are:
> >>> +#
> >>> +# backup_cs_unlock - occurs after consistent read
> >>> +# transaction has been started and before the open and
> >>> +# lock tables.
> >>> +#
> >>> +# backup_cs_reading - occurs after the open and lock
> >>> +# tables during the read tables portion.
> >>> +#
> >>> +# The following tests test these conditions.
> >>> #
> >>>
> >>> --source include/have_innodb.inc
> >>> @@ -22,8 +32,11 @@ connect (con2,localhost,root,,);
> >>>
> >>> connection con1;
> >>>
> >>> -# Create a table and load it with lots of data.
> >>> +#
> >>> +# Setup for tests.
> >>> +#
> >>>
> >>> +# Create a table and load it with lots of data.
> >>> CREATE TABLE bup_snapshot.t1 (word CHAR(20)) ENGINE=INNODB;
> >>>
> >>> INSERT INTO bup_snapshot.t1 VALUES ("01 Some data to test"); @@
> >>> -37,19 +50,42 @@ INSERT INTO bup_snapshot.t1 VALUES ("08
> >> INSERT INTO
> >>> bup_snapshot.t1 VALUES ("09 Some data to test"); INSERT INTO
> >>> bup_snapshot.t1 VALUES ("10 Some data to test");
> >>>
> >>> +# Use a non-CS supported table to show driver can coexist with
> >>> +default driver CREATE TABLE bup_snapshot.t2 (a int)
> ENGINE=MEMORY;
> >>> +INSERT INTO bup_snapshot.t2 VALUES (1), (2), (3), (4), (5);
> >>> +
> >>> --echo con1: Show that the new data doesn't exist before backup.
> >>> SELECT * FROM bup_snapshot.t1 WHERE word LIKE '-%';
> >> SELECT COUNT(*)
> >>> FROM bup_snapshot.t1;
> >>> +SELECT COUNT(*) FROM bup_snapshot.t2;
> >>> +
> >>> +connection con2;
> >>> +
> >>> +#
> >>> +# Test 1: Check for consistent read prior to open and
> lock tables #
> >>> +
> >>> +--echo con2: Getting lock on driver.
> >>> +SELECT get_lock("backup_cs_locked", 100);
> >>>
> >>> # While a consistent snapshot backup is executed, # no external
> >>> inserts should be visible to the transaction.
> >>>
> >>> ---echo con1: Backing up database.
> >>> -BACKUP DATABASE bup_snapshot TO "bup_snapshot.bak";
> >>> +connection con1;
> >>> +
> >>> +--echo con1: Backing up database. Spawn this and continue...
> >>> +send BACKUP DATABASE bup_snapshot TO "bup_snapshot.bak";
> >>>
> >>> connection con2;
> >>>
> >>> ---echo con2: Inserting new data.
> >>> +--echo con2: Wait until backup pauses then insert new data.
> >>> +
> >>> +# Must wait to know when backup has entered lock.
> >>> +let $wait_condition = SELECT state = "debug_sync_point:
> >> backup_cs_locked"
> >>> + FROM INFORMATION_SCHEMA.PROCESSLIST
> >>> + WHERE info LIKE "backup database %";
> >> --source
> >>> +include/wait_condition.inc
> >>> +
> >>> INSERT INTO bup_snapshot.t1 VALUES("- Dave Mathews");
> INSERT INTO
> >>> bup_snapshot.t1 VALUES("- Yes"); INSERT INTO bup_snapshot.t1
> >>> VALUES("- Jethro Tull"); @@ -59,8 +95,13 @@ DELETE FROM
> >>> bup_snapshot.t1 WHERE word L SELECT * FROM bup_snapshot.t1
> >> WHERE word
> >>> LIKE '-%'; SELECT COUNT(*) FROM bup_snapshot.t1;
> >>>
> >>> +--echo con2: Release lock on driver.
> >>> +SELECT release_lock("backup_cs_locked");
> >>> +
> >>> connection con1;
> >>>
> >>> +reap;
> >>> +
> >>> # Now restore the database and then check to make sure the
> >> new rows
> >>> # were not backed up.
> >>>
> >>> @@ -73,9 +114,68 @@ RESTORE FROM "bup_snapshot.bak";
> --echo con1:
> >>> Showing the data (no new data should be here).
> >>> SELECT * FROM bup_snapshot.t1 WHERE word LIKE '-%';
> >> SELECT COUNT(*)
> >>> FROM bup_snapshot.t1;
> >>> +SELECT COUNT(*) FROM bup_snapshot.t2;
> >>>
> >>> -DROP DATABASE bup_snapshot;
> >>> +remove_file $MYSQLTEST_VARDIR/master-data/bup_snapshot.bak;
> >>> +
> >>> +#
> >>> +# Test 2: Check for consistent read after open and lock tables #
> >>> +
> >>> +connection con2;
> >>> +
> >>> +--echo con2: Getting lock on driver.
> >>> +SELECT get_lock("backup_cs_reading", 100);
> >>> +
> >>> +# While a consistent snapshot backup is executed, # no external
> >>> +inserts should be visible to the transaction.
> >>> +
> >>> +connection con1;
> >>>
> >>> ---exec rm $MYSQLTEST_VARDIR/master-data/bup_snapshot.bak
> >>> +--echo con1: Backing up database. Spawn this and continue...
> >>> +send BACKUP DATABASE bup_snapshot TO "bup_snapshot.bak";
> >>> +
> >>> +connection con2;
> >>> +
> >>> +--echo con2: Wait until backup pauses then insert new data.
> >>> +
> >>> +# Must wait to know when backup has entered lock.
> >>> +let $wait_condition = SELECT state = "debug_sync_point:
> >> backup_cs_reading"
> >>> + FROM INFORMATION_SCHEMA.PROCESSLIST
> >>> + WHERE info LIKE "backup database %";
> >> --source
> >>> +include/wait_condition.inc
> >>> +
> >>> +INSERT INTO bup_snapshot.t1 VALUES("- Dave Mathews"); INSERT INTO
> >>> +bup_snapshot.t1 VALUES("- Yes"); INSERT INTO bup_snapshot.t1
> >>> +VALUES("- Jethro Tull"); DELETE FROM bup_snapshot.t1 WHERE
> >> word LIKE
> >>> +'10%';
> >>> +
> >>> +--echo con2: Showing the data after inserts.
> >>> +SELECT * FROM bup_snapshot.t1 WHERE word LIKE '-%'; SELECT
> >> COUNT(*)
> >>> +FROM bup_snapshot.t1;
> >>> +
> >>> +--echo con2: Release lock on driver.
> >>> +SELECT release_lock("backup_cs_reading");
> >>> +
> >>> +connection con1;
> >>> +
> >>> +reap;
> >>> +
> >>> +# Now restore the database and then check to make sure the
> >> new rows #
> >>> +were not backed up.
> >>> +
> >>> +--echo con1: Dropping the database
> >>> +DROP TABLE bup_snapshot.t1;
> >>> +
> >>> +--echo con1: Restoring the database RESTORE FROM
> >>> +"bup_snapshot.bak";
> >>> +
> >>> +--echo con1: Showing the data (no new data should be here).
> >>> +SELECT * FROM bup_snapshot.t1 WHERE word LIKE '-%'; SELECT
> >> COUNT(*)
> >>> +FROM bup_snapshot.t1; SELECT COUNT(*) FROM bup_snapshot.t2;
> >>> +
> >>> +DROP DATABASE bup_snapshot;
> >>>
> >>> +remove_file $MYSQLTEST_VARDIR/master-data/bup_snapshot.bak;
> >>>
> >>> diff -Nrup a/sql/backup/CMakeLists.txt b/sql/backup/CMakeLists.txt
> >>> --- a/sql/backup/CMakeLists.txt 2007-11-06 13:32:04 -05:00
> >>> +++ b/sql/backup/CMakeLists.txt 2007-11-13 09:43:01 -05:00
> >>> @@ -25,8 +25,11 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/
> >>> SET(BACKUP_SOURCES stream.cc logger.cc string_pool.cc
> >>> archive.cc meta_backup.cc data_backup.cc
> >>> sql_backup.cc be_default.cc buffer_iterator.cc
> >>> - be_snapshot.cc)
> >>> + be_snapshot.cc be_thread.cc)
> >>>
> >>> IF(NOT SOURCE_SUBLIBS)
> >>> ADD_LIBRARY(backup ${BACKUP_SOURCES}) ENDIF(NOT
> SOURCE_SUBLIBS)
> >>> +
> >>> +ADD_DEPENDENCIES(backup mysys)
> >>> +
> >>> diff -Nrup a/sql/backup/Makefile.am b/sql/backup/Makefile.am
> >>> --- a/sql/backup/Makefile.am 2007-11-06 13:32:05 -05:00
> >>> +++ b/sql/backup/Makefile.am 2007-11-13 09:43:02 -05:00
> >>> @@ -33,7 +33,8 @@ libbackup_la_SOURCES = \
> >>> sql_backup.cc \
> >>> be_default.cc \
> >>> be_snapshot.cc \
> >>> - buffer_iterator.cc
> >>> + buffer_iterator.cc \
> >>> + be_thread.cc
> >>>
> >>> noinst_HEADERS = \
> >>> api_types.h \
> >>> @@ -50,7 +51,8 @@ noinst_HEADERS = \
> >>> meta_backup.h \
> >>> be_default.h \
> >>> be_snapshot.h \
> >>> - buffer_iterator.h
> >>> + buffer_iterator.h \
> >>> + be_thread.h
> >>>
> >>> DEFS = \
> >>> -DMYSQL_SERVER \
> >>> diff -Nrup a/sql/backup/be_default.cc b/sql/backup/be_default.cc
> >>> --- a/sql/backup/be_default.cc 2007-11-12 15:58:17 -05:00
> >>> +++ b/sql/backup/be_default.cc 2007-11-13 09:43:02 -05:00
> >>> @@ -109,14 +109,14 @@ result_t Engine::get_backup(const uint32 }
> >>>
> >>> Backup::Backup(const Table_list &tables, THD *t_thd,
> >> thr_lock_type lock_type):
> >>> - Backup_driver(tables)
> >>> + Backup_thread_driver(tables)
> >>> {
> >>> DBUG_PRINT("default_backup",("Creating backup driver"));
> >>> m_thd= t_thd; /* save current thread */
> >>> cur_table= NULL; /* flag current table as null */
> >>> tbl_num= 0; /* set table number to 0 */
> >>> mode= INITIALIZE; /* initialize read */
> >>> - lock_called= FALSE; /* lock has not been called */
> >>> + lock_thd= NULL; /* set lock thread to 0 */
> >>> /*
> >>> Create a TABLE_LIST * list for iterating through the tables.
> >>> @@ -124,6 +124,21 @@ Backup::Backup(const Table_list &tables,
> >>> */
> >>> tables_in_backup= build_table_list(tables, lock_type);
> >>> all_tables= tables_in_backup;
> >>> + init_phase_complete= FALSE;
> >>> + locks_acquired= FALSE;
> >>> +}
> >>> +
> >>> +/**
> >>> + * @brief Prelock call to setup locking.
> >>> + *
> >>> + * Launches a separate thread ("locking thread") which will lock
> >>> + * tables. Locking in a separate thread is needed to have a
> >>> +non-blocking
> >>> + * prelock() (given that thr_lock() is blocking).
> >>> + */
> >>> +result_t Backup::prelock()
> >>> +{
> >>> + DBUG_ENTER("Default_backup::prelock()");
> >>> + DBUG_RETURN(start_locking_thread());
> >>> }
> >>>
> >>> /**
> >>> @@ -266,14 +281,33 @@ result_t Backup::get_data(Buffer &buf)
> >>> DBUG_ENTER("Default_backup::get_data(Buffer &buf)");
> >>>
> >>> /*
> >>> - If locks have not been obtained, wait until they have.
> >>> + Check the lock state. Take action based on the
> >> availability of the lock.
> >>> +
> >>> + @todo Refactor the following code to make this a new
> >> mode for the driver,
> >>> + e.g. INIT_PHASE, SYNCH_PHASE, ACQUIRING_LOCKS, etc.
> >>> */
> >>> - if (!lock_called)
> >>> + if (!locks_acquired)
> >>> {
> >>> buf.size= 0;
> >>> buf.table_no= 0;
> >>> buf.last= TRUE;
> >>> - DBUG_RETURN(READY);
> >>> + switch (lock_state) {
> >>> + case LOCK_ERROR: // Something ugly
> >> happened in locking
> >>> + DBUG_RETURN(ERROR);
> >>> + case LOCK_ACQUIRED: // First time lock ready
> >> for validity point
> >>> + {
> >>> + locks_acquired= TRUE;
> >>> + DBUG_RETURN(READY);
> >>> + }
> >>> + default: // If first call, signal
> >> end of init phase
> >>> + if (init_phase_complete)
> >>> + DBUG_RETURN(OK);
> >>> + else
> >>> + {
> >>> + init_phase_complete= TRUE;
> >>> + DBUG_RETURN(READY);
> >>> + }
> >>> + }
> >> OK.
> >>
> >>> }
> >>>
> >>> buf.table_no= tbl_num;
> >>> @@ -338,6 +372,13 @@ result_t Backup::get_data(Buffer &buf)
> >>> buf.size= 0;
> >>> buf.last= TRUE;
> >>> mode= GET_NEXT_TABLE;
> >>> +
> >>> + /*
> >>> + Optimization: If this is the last table to read,
> >> close the tables and
> >>> + kill the lock thread. This only applies iff we are
> >> using the thread.
> >>> + */
> >>> + if (tables_in_backup->next_global == NULL)
> >>> + kill_locking_thread();
> >>> }
> >>> else if (last_read_res != 0)
> >>> DBUG_RETURN(ERROR);
> >>> diff -Nrup a/sql/backup/be_default.h b/sql/backup/be_default.h
> >>> --- a/sql/backup/be_default.h 2007-11-09 16:32:11 -05:00
> >>> +++ b/sql/backup/be_default.h 2007-11-13 09:43:03 -05:00
> >>> @@ -6,6 +6,8 @@
> >>> #include "archive.h"
> >>> #include "buffer_iterator.h"
> >>> #include "backup_aux.h"
> >>> +#include "mysql_priv.h"
> >>> +#include "be_thread.h"
> >>>
> >>> namespace default_backup {
> >>>
> >>> @@ -78,32 +80,34 @@ class Engine: public Backup_engine
> >>> * a table scan on each table reading the rows and saving
> >> the data to the
> >>> * buffer from the backup algorithm.
> >>> *
> >>> - * @see <backup driver>
> >>> + * @see <backup driver> and <backup thread driver>
> >>> */
> >>> -class Backup: public Backup_driver
> >>> +class Backup: public Backup_thread_driver
> >>> {
> >>> public:
> >>> enum has_data_info { YES, WAIT, EOD };
> >>> Backup(const Table_list &tables, THD *t_thd,
> >> thr_lock_type lock_type);
> >>> - virtual ~Backup() { backup::free_table_list(all_tables); };
> >>> + virtual ~Backup()
> >>> + {
> >>> + kill_locking_thread();
> >>> + backup::free_table_list(all_tables);
> >>> + };
> >>> size_t size() { return UNKNOWN_SIZE; };
> >>> size_t init_size() { return 0; };
> >>> result_t begin(const size_t) { return backup::OK; };
> >>> result_t end() { return backup::OK; };
> >>> result_t get_data(Buffer &buf);
> >>> - result_t lock()
> >>> - {
> >>> - lock_called= TRUE;
> >>> - return backup::OK;
> >>> - };
> >>> + result_t lock() { return backup::OK; };
> >>> result_t unlock() { return backup::OK; };
> >>> result_t cancel() { return backup::OK; };
> >>> TABLE_LIST *get_table_list() { return all_tables; }
> >>> void free() { delete this; };
> >>> + result_t prelock();
> >>>
> >>> protected:
> >>> - my_bool lock_called; ///< Checks to see if
> >> locks have been reached.
> >>> - THD *m_thd; ///< Pointer to current
> >> thread struct.
> >>> + TABLE *cur_table; ///< The table
> >> currently being read.
> >>> + my_bool init_phase_complete; ///< Used to identify
> >> end of init phase.
> >>> + my_bool locks_acquired; ///< Used to help
> >> kernel synchronize drivers.
> >>>
> >>> private:
> >>> /*
> >>> @@ -126,7 +130,6 @@ class Backup: public Backup_driver
> >>> int next_table();
> >>> BACKUP_MODE mode; ///< Indicates which
> >> mode the code is in
> >>> int tbl_num; ///< The index of the
> >> current table.
> >>> - TABLE *cur_table; ///< The table
> >> currently being read.
> >>> handler *hdl; ///< Pointer to table handler.
> >>> uint *cur_blob; ///< The current blob field.
> >>> uint *last_blob_ptr; ///< Position of last
> >> blob field.
> >>> @@ -135,7 +138,6 @@ class Backup: public Backup_driver
> >>> Buffer_iterator blob_buffer; ///< Buffer iterator
> >> for windowing BLOB fields
> >>> byte *ptr; ///< Pointer to blob
> >> data from record.
> >>> TABLE_LIST *all_tables; ///< Reference to list
> >> of tables used.
> >>> - TABLE_LIST *tables_in_backup; ///< List of tables
> >> used in backup.
> >>>
> >>> uint pack(byte *rcd, byte *packed_row); }; diff -Nrup
> >>> a/sql/backup/be_snapshot.cc b/sql/backup/be_snapshot.cc
> >>> --- a/sql/backup/be_snapshot.cc 2007-11-06 13:32:11 -05:00
> >>> +++ b/sql/backup/be_snapshot.cc 2007-11-13 09:43:04 -05:00
> >>> @@ -77,20 +77,6 @@ result_t Engine::get_backup(const uint32
> >>> DBUG_RETURN(OK);
> >>> }
> >>>
> >>> -/**
> >>> - * @brief End backup process.
> >>> - *
> >>> - * This method unlocks all of the tables.
> >>> - *
> >>> - * @retval backup::OK all tables unlocked.
> >>> - */
> >>> -result_t Backup::end()
> >>> -{
> >>> - DBUG_ENTER("Snapshot_backup::end");
> >>> - end_active_trans(m_thd);
> >>> - DBUG_RETURN(OK);
> >>> -}
> >>> -
> >>> result_t Backup::lock()
> >>> {
> >>> DBUG_ENTER("Snapshot_backup::lock()");
> >>> @@ -104,14 +90,39 @@ result_t Backup::lock()
> >>> int res= begin_trans(m_thd);
> >>> if (res)
> >>> DBUG_RETURN(ERROR);
> >>> - lock_called= TRUE;
> >>> + lock_state= LOCK_ACQUIRED;
> >>> + BACKUP_BREAKPOINT("backup_cs_locked");
> >>> DBUG_RETURN(OK);
> >>> }
> >>>
> >>> -result_t Backup::unlock()
> >>> +result_t Backup::get_data(Buffer &buf)
> >>> {
> >>> - DBUG_ENTER("Snapshot_backup::unlock()");
> >>> - DBUG_RETURN(OK);
> >>> + result_t res;
> >>> +
> >>> + if (!tables_open && (lock_state == LOCK_ACQUIRED)) {
> >>> + BACKUP_BREAKPOINT("backup_cs_open_tables");
> >>> + open_and_lock_tables(m_thd, tables_in_backup);
> >>> + tables_open= TRUE;
> >>> + }
> >>> + if (lock_state == LOCK_ACQUIRED)
> >>> + BACKUP_BREAKPOINT("backup_cs_reading");
> >>> +
> >>> + res= default_backup::Backup::get_data(buf);
> >> This will eventually call the kill_locking_thread() function.
> >> Hope that this is safe.
> >>
> >>> +
> >>> + /*
> >>> + If this is the last table to be read, close the transaction
> >>> + and unlock the tables. This is indicated by the lock state
> >>> + being set to LOCK_SIGNAL from parent::get_data(). This is set
> >>> + after the last table is finished reading.
> >>> + */
> >>> + if (lock_state == LOCK_SIGNAL)
> >>> + {
> >>> + lock_state= LOCK_DONE; // set lock done so
> >> destructor won't wait
> >>> + end_active_trans(m_thd);
> >>> + close_thread_tables(m_thd);
> >>> + }
> >>> + return(res);
> >>> }
> >>>
> >>> /**
> >>> diff -Nrup a/sql/backup/be_snapshot.h b/sql/backup/be_snapshot.h
> >>> --- a/sql/backup/be_snapshot.h 2007-11-06 13:32:12 -05:00
> >>> +++ b/sql/backup/be_snapshot.h 2007-11-13 09:43:05 -05:00
> >>> @@ -59,13 +59,24 @@ class Backup: public default_backup::Bac {
> >>> public:
> >>> Backup(const Table_list &tables, THD *t_thd):
> >>> - default_backup::Backup(tables, t_thd, TL_READ) {};
> >>> - virtual ~Backup() {};
> >>> + default_backup::Backup(tables, t_thd, TL_READ) {
> >> tables_open= FALSE; };
> >>> + virtual ~Backup()
> >>> + {
> >>> + if (lock_state == LOCK_ACQUIRED)
> >>> + {
> >>> + end_active_trans(m_thd);
> >>> + close_thread_tables(m_thd);
> >>> + }
> >>> + };
> >>> result_t begin(const size_t) { return backup::OK; };
> >>> - result_t end();
> >>> + result_t end() { return backup::OK; };
> >
> >>> + result_t get_data(Buffer &buf);
> >>> result_t prelock() { return backup::READY; }
> >>> result_t lock();
> >>> - result_t unlock();
> >>> + result_t unlock() { return backup::OK; };
> >>> + result_t cancel() { return backup::OK; };
> >>> + private:
> >>> + my_bool tables_open; ///< Indicates if tables are open
> >>> };
> >>>
> >>> /**
> >>> diff -Nrup a/sql/backup/be_thread.cc b/sql/backup/be_thread.cc
> >>> --- /dev/null Wed Dec 31 16:00:00 196900
> >>> +++ b/sql/backup/be_thread.cc 2007-11-13 09:43:07 -05:00
> >>> @@ -0,0 +1,295 @@
> >>> +/* Copyright (C) 2004-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 */
> >>> +
> >>> +/**
> >>> + * @file
> >>> + *
> >>> + * @brief Contains the thread methods for online backup.
> >>> + *
> >>> + * The methods in this class are used to initialize the mutexes
> >>> + * for the backup threads. Helper methods are included to make
> >>> +thread
> >>> + * calls easier for the driver code.
> >>> + */
> >>> +
> >>> +#include "be_thread.h"
> >>> +
> >>> +/**
> >>> + * @brief Creates a new THD object.
> >>> + *
> >>> + * Creates a new THD object for use in running as a
> >> separate thread.
> >>> + *
> >>> + * @returns Pointer to new THD object or 0 if error.
> >>> + *
> >>> + * @TODO Move this method to a location where
> >> ha_ndbcluster_binlog.cc can
> >>> + * use it and replace code in
> >> ndb_binlog_thread_func(void *arg) to
> >>> + * call this function.
> >>> + *
> >>> + * @note my_net_init() this should be paired with
> my_net_end() on
> >>> + * close/kill of thread.
> >>> + */
> >>> +THD *create_new_thd()
> >>> +{
> >>> + THD *thd;
> >>> + DBUG_ENTER("Create new THD object");
> >>> +
> >>> + thd= new THD;
> >>> + if (unlikely(!thd))
> >>> + {
> >>> + delete thd;
> >>> + DBUG_RETURN(0);
> >>> + }
> >>> +
> >>> + thd->thread_stack = (char*)&thd; // remember where our
> stack is
> >>> + pthread_mutex_lock(&LOCK_thread_count);
> >>> + thd->thread_id= thread_id++;
> >>> + pthread_mutex_unlock(&LOCK_thread_count);
> >>> + if (unlikely(thd->store_globals())) // for a proper MEM_ROOT {
> >>> + delete thd;
> >>> + DBUG_RETURN(0);
> >>> + }
> >>> +
> >>> + thd->init_for_queries(); // opening tables needs a proper LEX
> >>> + thd->command= COM_DAEMON;
> >>> + thd->system_thread= SYSTEM_THREAD_BACKUP;
> >>> + thd->version= refresh_version;
> >>> + thd->set_time();
> >>> + thd->main_security_ctx.host_or_ip= "";
> >>> + thd->client_capabilities= 0;
> >>> + my_net_init(&thd->net, 0);
> >>> + thd->main_security_ctx.master_access= ~0;
> >>> + thd->main_security_ctx.priv_user= 0;
> >>> + thd->real_id= pthread_self();
> >>> + /*
> >>> + Making this thread visible to SHOW PROCESSLIST is useful for
> >>> + troubleshooting a backup job (why does it stall etc).
> >>> + */
> >>> + pthread_mutex_lock(&LOCK_thread_count);
> >>> + threads.append(thd);
> >>> + pthread_mutex_unlock(&LOCK_thread_count);
> >>> + lex_start(thd);
> >>> + mysql_reset_thd_for_next_command(thd);
> >>> + DBUG_RETURN(thd);
> >>> +}
> >>> +
> >>> +/**
> >>> + * @brief Lock tables in driver.
> >>> + *
> >>> + * This method creates a new THD for use in the new
> >> thread. It calls
> >>> + * the method to open and lock the tables.
> >>> + *
> >>> + * @note my_thread_init() should be paired with
> >> my_thread_end() on
> >>> + * close/kill of thread.
> >>> + */
> >>> +pthread_handler_t backup_thread_for_locking(void *arg) {
> >>> + Backup_thread_driver *drv= static_cast<Backup_thread_driver
> >>> +*>(arg);
> >>> +
> >>> + DBUG_PRINT("info", ("Default_backup -
> >>> + lock_tables_in_separate_thread"));
> >>> +
> >>> + /*
> >>> + Turn off condition variable check for lock.
> >>> + */
> >>> + drv->lock_state= LOCK_NOT_STARTED;
> >>> +
> >>> +#if !defined( __WIN__) /* Win32 calls this in pthread_create */
> >>> + my_thread_init();
> >>> +#endif
> >>> +
> >>> + pthread_detach_this_thread();
> >>> +
> >>> + /*
> >>> + First, create a new THD object.
> >>> + */
> >>> + DBUG_PRINT("info",("Online backup creating THD struct for
> >>> + thread")); THD *thd= create_new_thd(); drv->lock_thd=
> thd; if
> >>> + (thd == 0) {
> >>> + drv->lock_state= LOCK_ERROR;
> >>> + goto end2;
> >>> + }
> >>> +
> >>> + if (thd->killed)
> >>> + {
> >>> + drv->lock_state= LOCK_ERROR;
> >>> + goto end2;
> >>> + }
> >>> +
> >>> + /*
> >>> + Now open and lock the tables.
> >>> + */
> >>> + DBUG_PRINT("info",("Online backup open tables in thread")); if
> >>> + (!drv->tables_in_backup) {
> >>> + DBUG_PRINT("info",("Online backup locking error no
> >> tables to lock"));
> >>> + drv->lock_state= LOCK_ERROR;
> >>> + goto end2;
> >>> + }
> >>> +
> >>> + /*
> >>> + As locking tables can be a long operation, we need to support
> >>> + killing the thread. In this case, we need to close
> the tables
> >>> + and exit.
> >>> + */
> >>> + if (!thd->killed && open_and_lock_tables(thd,
> >>> + drv->tables_in_backup)) {
> >>> + DBUG_PRINT("info",("Online backup locking thread dying"));
> >>> + drv->lock_state= LOCK_ERROR;
> >>> + goto end;
> >>> + }
> >>> +
> >>> + if (thd->killed)
> >>> + {
> >>> + drv->lock_state= LOCK_ERROR;
> >>> + goto end;
> >>> + }
> >>> +
> >>> + drv->lock_state= LOCK_ACQUIRED;
> >>> +
> >>> + /*
> >>> + Part of work is done. Rest until woken up.
> >>> + We wait if the thread is not killed and the driver has
> >> not signaled us.
> >>> + */
> >>> + pthread_mutex_lock(&drv->THR_LOCK_driver_thread);
> >>> + thd->enter_cond(&drv->COND_driver_thread_wait,
> >> &drv->THR_LOCK_driver_thread,
> >>> + "Online backup driver thread: holding table
> >>> + locks"); while (!thd->killed && (drv->lock_state !=
> LOCK_SIGNAL))
> >>> + pthread_cond_wait(&drv->COND_driver_thread_wait,
> >>> + &drv->THR_LOCK_driver_thread); thd->exit_cond("Online
> >> backup driver
> >>> + thread: terminating");
> >>> +
> >>> + DBUG_PRINT("info",("Online backup driver thread locking thread
> >>> + terminating"));
> >>> +
> >>> + /*
> >>> + Cleanup and return.
> >>> + */
> >>> +end:
> >>> + close_thread_tables(thd);
> >>> +
> >>> +end2:
> >>> + pthread_mutex_lock(&drv->THR_LOCK_driver);
> >>> + net_end(&thd->net);
> >>> + my_thread_end();
> >>> + delete thd;
> >>> + drv->lock_thd= NULL;
> >>> + if (drv->lock_state != LOCK_ERROR)
> >>> + drv->lock_state= LOCK_DONE;
> >>> +
> >>> + /*
> >>> + Signal the driver thread that it's ok to proceed with
> >> destructor.
> >>> + */
> >>> + pthread_cond_broadcast(&drv->COND_driver_wait);
> >>> + pthread_mutex_unlock(&drv->THR_LOCK_driver);
> >>> + pthread_exit(0);
> >>> + return (0);
> >>> +}
> >>> +
> >>> +/*
> >>> + Constructor for backup_thread_driver class.
> >>> +*/
> >>> +Backup_thread_driver::Backup_thread_driver(const
> >> Table_list &tables):
> >>> +
> Backup_driver(tables) {
> >>> + /*
> >>> + Initialize the thread mutex and cond variable.
> >>> + */
> >>> + pthread_mutex_init(&THR_LOCK_driver_thread,
> MY_MUTEX_INIT_FAST);
> >>> + pthread_cond_init(&COND_driver_thread_wait, NULL);
> >>> + pthread_mutex_init(&THR_LOCK_driver, MY_MUTEX_INIT_FAST);
> >>> + pthread_cond_init(&COND_driver_wait, NULL); lock_state=
> >>> + LOCK_NOT_STARTED; lock_thd= NULL; // set to 0 as
> precaution for
> >>> + get_data
> >> being called
> >>> +too soon };
> >>> +
> >>> +/*
> >>> + Destructor for backup_thread_driver class.
> >>> +*/
> >>> +Backup_thread_driver::~Backup_thread_driver()
> >>> +{
> >>> + /*
> >>> + If the locking thread is not finished, we need to wait until
> >>> + it is finished so that we can destroy the mutexes
> >> safely knowing
> >>> + the locking thread won't access them.
> >>> + */
> >>> + if (lock_state != LOCK_DONE)
> >>> + {
> >>> + pthread_mutex_lock(&THR_LOCK_driver);
> >>> + m_thd->enter_cond(&COND_driver_wait, &THR_LOCK_driver,
> >>> + "Online backup driver: waiting until
> >> locking thread is done");
> >>> + while (lock_state != LOCK_DONE)
> >>> + pthread_cond_wait(&COND_driver_wait, &THR_LOCK_driver);
> >>> + m_thd->exit_cond("Online backup driver: terminating");
> >>> +
> >>> + DBUG_PRINT("info",("Online backup driver's locking thread
> >>> + terminated")); }
> >>> +
> >>> + /*
> >>> + Destroy the thread mutexes and cond variables.
> >>> + */
> >>> + pthread_mutex_destroy(&THR_LOCK_driver_thread);
> >>> + pthread_cond_destroy(&COND_driver_thread_wait);
> >>> + pthread_mutex_destroy(&THR_LOCK_driver);
> >>> + pthread_cond_destroy(&COND_driver_wait);
> >>> +}
> >>> +
> >>> +/**
> >>> + Start the driver's lock thread.
> >>> +
> >>> + Launches a separate thread ("locking thread") which will lock
> >>> + tables.
> >>> + */
> >>> +result_t Backup_thread_driver::start_locking_thread()
> >>> +{
> >>> + DBUG_ENTER("Backup_thread_driver::start_locking_thread");
> >>> + pthread_t th;
> >>> + if (pthread_create(&th, &connection_attrib,
> >>> + backup_thread_for_locking, this))
> >>> + SET_STATE_TO_ERROR_AND_DBUG_RETURN;
> >>> + DBUG_RETURN(backup::OK);
> >>> +}
> >>> +
> >>> +/**
> >>> + Kill the driver's lock thread.
> >>> +
> >>> + This method issues the awake and broadcast to kill the
> >> locking thread.
> >>> + A mutex is used to prevent the locking thread from
> >> deleting the THD
> >>> + structure until this operation is complete.
> >>> + */
> >>> +void Backup_thread_driver::kill_locking_thread()
> >>> +{
> >>> + DBUG_ENTER("Backup_thread_driver::kill_locking_thread");
> >>> + if (lock_state == LOCK_ACQUIRED)
> >>> + {
> >>> + pthread_mutex_lock(&THR_LOCK_driver);
> >>> + if (lock_thd && (lock_state != LOCK_DONE) &&
> >> (lock_state != LOCK_SIGNAL))
> >>> + {
> >>> + lock_state= LOCK_SIGNAL;
> >>> + pthread_mutex_lock(&lock_thd->LOCK_delete);
> >>> + lock_thd->awake(THD::KILL_CONNECTION);
> >>> + pthread_mutex_unlock(&lock_thd->LOCK_delete);
> >>> + pthread_cond_broadcast(&COND_driver_thread_wait);
> >>> + }
> >>> + /*
> >>> + Set the lock state to LOCK_SIGNAL to tell the CS
> driver that
> >>> + the locks are now freed.
> >>> + */
> >>> + else if (!lock_thd)
> >>> + lock_state= LOCK_SIGNAL;
> >>> + pthread_mutex_unlock(&THR_LOCK_driver);
> >>> + }
> >>> + DBUG_VOID_RETURN;
> >>> +}
> >>> +
> >>> diff -Nrup a/sql/backup/be_thread.h b/sql/backup/be_thread.h
> >>> --- /dev/null Wed Dec 31 16:00:00 196900
> >>> +++ b/sql/backup/be_thread.h 2007-11-13 09:43:08 -05:00
> >>> @@ -0,0 +1,79 @@
> >>> +#ifndef _BACKUP_THREAD_H
> >>> +#define _BACKUP_THREAD_H
> >>> +
> >>> +#include "../mysql_priv.h"
> >>> +#include "archive.h"
> >>> +#include "api_types.h"
> >>> +#include "backup_engine.h"
> >>> +
> >>> +/**
> >>> + Macro for error handling.
> >>> +*/
> >>> +#define SET_STATE_TO_ERROR_AND_DBUG_RETURN {
> >> \
> >>> + int state= backup::ERROR;
> >> \
> >>> + DBUG_PRINT("error",("driver got an error at
> >> %s:%d",__FILE__,__LINE__)); \
> >>> + DBUG_RETURN(backup::ERROR); }
> >>> +
> >>> +/**
> >>> + Locking of tables goes through several states.
> >>> +*/
> >>> +typedef enum {
> >>> + LOCK_NOT_STARTED,
> >>> + LOCK_IN_PROGRESS,
> >>> + LOCK_ACQUIRED,
> >>> + LOCK_DONE,
> >>> + LOCK_ERROR,
> >>> + LOCK_SIGNAL
> >>> +} LOCK_STATE;
> >>> +
> >>> +using backup::result_t;
> >>> +using backup::Table_list;
> >>> +
> >>> +/**
> >>> + create_new_thd
> >>> +
> >>> + This method creates a new THD object.
> >>> +*/
> >>> +THD *create_new_thd();
> >>> +
> >>> +/**
> >>> + backup_thread_for_locking
> >>> +
> >>> + This method creates a new thread and opens and locks
> the tables.
> >>> +*/
> >>> +pthread_handler_t backup_thread_for_locking(void *arg);
> >>> +
> >>> +/**
> >>> + * @class Backup_thread_driver
> >>> + *
> >>> + * @brief Adds variables for using a locking thread for
> >> opening tables.
> >>> + *
> >>> + * The Backup_thread_driver class extends the
> >> Backup_driver class by
> >>> +adding
> >>> + * a mutex and condition variable for using a thread to
> >> open and lock
> >>> +the
> >>> + * tables.
> >>> + *
> >>> + * @see <backup driver> and <backup thread driver> */
> class
> >>> +Backup_thread_driver : public Backup_driver {
> >>> +public:
> >>> +
> >>> + Backup_thread_driver(const backup::Table_list &tables);
> >>> + ~Backup_thread_driver();
> >>> +
> >>> + pthread_mutex_t THR_LOCK_driver_thread; ///< mutex for thread
> >>> + variables pthread_cond_t COND_driver_thread_wait; ///<
> condition
> >>> + variable for wait pthread_mutex_t THR_LOCK_driver; ///<
> >> mutex for
> >>> + thread variables pthread_cond_t COND_driver_wait; ///<
> >> condition variable for wait
> >>> + TABLE_LIST *tables_in_backup; ///< List of
> >> tables used in backup
> >>> + THD *lock_thd; ///< Locking
> >> thread pointer
> >>> + LOCK_STATE lock_state; ///< Current
> >> state of the lock call
> >>> + THD *m_thd; ///< Pointer to current
> >> thread struct.
> >>> +
> >>> + backup::result_t start_locking_thread(); void
> >>> + kill_locking_thread();
> >>> +
> >>> +}; // Backup_thread_driver class
> >>> +
> >>> +#endif
> >>> +
> >>> diff -Nrup a/sql/backup/data_backup.cc b/sql/backup/data_backup.cc
> >>> --- a/sql/backup/data_backup.cc 2007-11-12 10:07:00 -05:00
> >>> +++ b/sql/backup/data_backup.cc 2007-11-13 09:43:06 -05:00
> >>> @@ -384,9 +384,6 @@ int write_table_data(THD*, Backup_info &
> >>>
> >>> DBUG_PRINT("backup/data",("initializing scheduler"));
> >>>
> >>> - TABLE_LIST *table_list= 0;
> >>> - TABLE_LIST *table_list_last= 0;
> >>> -
> >>> // add unknown "at end" drivers to scheduler, rest to
> >> inactive list
> >>>
> >>> for (uint no=0; no < info.img_count; ++no) @@ -418,12
> >> +415,6 @@ int
> >>> write_table_data(THD*, Backup_info &
> >>>
> >>> inactive.push_back(p);
> >>> }
> >>> - if (!def_or_snap_used)
> >>> - def_or_snap_used= ((i->type() ==
> >> Image_info::DEFAULT_IMAGE) ||
> >>> - (i->type() ==
> >> Image_info::SNAPSHOT_IMAGE));
> >>> - if (def_or_snap_used)
> >>> - get_default_snapshot_tables(&p->drv(), NULL,
> >>> - &table_list,
> &table_list_last);
> >>> }
> >>>
> >>> /*
> >>> @@ -508,22 +499,6 @@ int write_table_data(THD*, Backup_info &
> >>> if (sch.prepare())
> >>> goto error;
> >>>
> >>> - /*
> >>> - Open tables for default and snapshot drivers.
> >>> - */
> >>> - if (table_list)
> >>> - {
> >>> - if (open_and_lock_tables(::current_thd, table_list))
> >>> - {
> >>> - DBUG_PRINT("backup",
> >>> - ( "error on open tables for default and snapshot
> >> drivers!" ));
> >>> - info.report_error(ER_BACKUP_OPEN_TABLES, "backup");
> >>> - DBUG_RETURN(ERROR);
> >>> - }
> >>> - if (table_list_last)
> >>> - table_list_last->next_global= NULL; // break lists
> >>> - }
> >>> -
> >>> while (sch.prepare_count > 0)
> >>> if (sch.step())
> >>> goto error;
> >>> @@ -568,12 +543,6 @@ int write_table_data(THD*, Backup_info &
> >>>
> >>> DBUG_PRINT("backup/data",("-- DONE --"));
> >>> }
> >>> -
> >>> - /*
> >>> - If the default or snapshot drivers are used, close
> the tables.
> >>> - */
> >>> - if (def_or_snap_used)
> >>> - close_thread_tables(::current_thd);
> >>>
> >>> info.data_size= s.bytes - start_bytes;
> >>>
> >>> diff -Nrup a/sql/share/errmsg.txt b/sql/share/errmsg.txt
> >>> --- a/sql/share/errmsg.txt 2007-10-19 16:20:01 -04:00
> >>> +++ b/sql/share/errmsg.txt 2007-11-13 09:43:06 -05:00
> >>> @@ -6195,6 +6195,9 @@ ER_BACKUP_SEND_DATA_RETRY
> >> ER_BACKUP_OPEN_TABLES
> >>> eng "Open and lock tables failed in %-.64s"
> >>>
> >>> +ER_BACKUP_THREAD_INIT
> >>> + eng "Backup driver's table locking thread can not
> >> be initialized."
> >>> +
> >>> ER_VIEW_NO_CREATION_CTX
> >>> eng "View `%-.64s`.`%-.64s` has no creation context"
> >>> ER_VIEW_INVALID_CREATION_CTX
> >>> diff -Nrup a/sql/sql_class.h b/sql/sql_class.h
> >>> --- a/sql/sql_class.h 2007-10-31 17:45:33 -04:00
> >>> +++ b/sql/sql_class.h 2007-11-13 09:43:00 -05:00
> >>> @@ -936,7 +936,8 @@ enum enum_thread_type
> >>> SYSTEM_THREAD_SLAVE_SQL= 4,
> >>> SYSTEM_THREAD_NDBCLUSTER_BINLOG= 8,
> >>> SYSTEM_THREAD_EVENT_SCHEDULER= 16,
> >>> - SYSTEM_THREAD_EVENT_WORKER= 32
> >>> + SYSTEM_THREAD_EVENT_WORKER= 32,
> >>> + SYSTEM_THREAD_BACKUP= 64
> >>> };
> >>>
> >>>
> >>>
> >>>
> >> --
> >> MySQL Code Commits Mailing List
> >> For list archives: http://lists.mysql.com/commits
> >> To unsubscribe:
> >> http://lists.mysql.com/commits?unsub=1
> >>
> >
>
> --
> MySQL Code Commits Mailing List
> For list archives: http://lists.mysql.com/commits
> To unsubscribe:
> http://lists.mysql.com/commits?unsub=1
>
#ifndef _BACKUP_THREAD_H
#define _BACKUP_THREAD_H
#include "../mysql_priv.h"
#include "archive.h"
#include "api_types.h"
#include "backup_engine.h"
/**
Macro for error handling.
*/
#define SET_STATE_TO_ERROR_AND_DBUG_RETURN { \
int state= backup::ERROR; \
DBUG_PRINT("error",("driver got an error at %s:%d",__FILE__,__LINE__)); \
DBUG_RETURN(backup::ERROR); }
/**
Locking of tables goes through several states.
*/
typedef enum {
LOCK_NOT_STARTED,
LOCK_IN_PROGRESS,
LOCK_ACQUIRED,
LOCK_DONE,
LOCK_ERROR,
LOCK_SIGNAL
} LOCK_STATE;
using backup::result_t;
using backup::Table_list;
/**
create_new_thd
This method creates a new THD object.
*/
THD *create_new_thd();
/**
backup_thread_for_locking
This method creates a new thread and opens and locks the tables.
*/
pthread_handler_t backup_thread_for_locking(void *arg);
/**
* @class Backup_thread_driver
*
* @brief Adds variables for using a locking thread for opening tables.
*
* The Backup_thread_driver class extends the Backup_driver class by adding
* a mutex and condition variable for using a thread to open and lock the
* tables.
*
* @see <backup driver> and <backup thread driver>
*/
class Backup_thread_driver : public Backup_driver
{
public:
Backup_thread_driver(const backup::Table_list &tables);
~Backup_thread_driver();
pthread_mutex_t THR_LOCK_driver_thread; ///< mutex for thread variables
pthread_cond_t COND_driver_thread_wait; ///< condition variable for wait
pthread_mutex_t THR_LOCK_driver; ///< mutex for thread variables
pthread_cond_t COND_driver_wait; ///< condition variable for wait
TABLE_LIST *tables_in_backup; ///< List of tables used in backup
THD *lock_thd; ///< Locking thread pointer
LOCK_STATE lock_state; ///< Current state of the lock call
THD *m_thd; ///< Pointer to current thread struct.
backup::result_t start_locking_thread();
void kill_locking_thread();
}; // Backup_thread_driver class
#endif
/* Copyright (C) 2004-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
*/
/**
* @file
*
* @brief Contains the thread methods for online backup.
*
* The methods in this class are used to initialize the mutexes
* for the backup threads. Helper methods are included to make thread
* calls easier for the driver code.
*/
#include "be_thread.h"
/**
* @brief Creates a new THD object.
*
* Creates a new THD object for use in running as a separate thread.
*
* @returns Pointer to new THD object or 0 if error.
*
* @TODO Move this method to a location where ha_ndbcluster_binlog.cc can
* use it and replace code in ndb_binlog_thread_func(void *arg) to
* call this function.
*
* @note my_net_init() this should be paired with my_net_end() on
* close/kill of thread.
*/
THD *create_new_thd()
{
THD *thd;
DBUG_ENTER("Create new THD object");
thd= new THD;
if (unlikely(!thd))
{
delete thd;
DBUG_RETURN(0);
}
thd->thread_stack = (char*)&thd; // remember where our stack is
pthread_mutex_lock(&LOCK_thread_count);
thd->thread_id= thread_id++;
pthread_mutex_unlock(&LOCK_thread_count);
if (unlikely(thd->store_globals())) // for a proper MEM_ROOT
{
delete thd;
DBUG_RETURN(0);
}
thd->init_for_queries(); // opening tables needs a proper LEX
thd->command= COM_DAEMON;
thd->system_thread= SYSTEM_THREAD_BACKUP;
thd->version= refresh_version;
thd->set_time();
thd->main_security_ctx.host_or_ip= "";
thd->client_capabilities= 0;
my_net_init(&thd->net, 0);
thd->main_security_ctx.master_access= ~0;
thd->main_security_ctx.priv_user= 0;
thd->real_id= pthread_self();
/*
Making this thread visible to SHOW PROCESSLIST is useful for
troubleshooting a backup job (why does it stall etc).
*/
pthread_mutex_lock(&LOCK_thread_count);
threads.append(thd);
pthread_mutex_unlock(&LOCK_thread_count);
lex_start(thd);
mysql_reset_thd_for_next_command(thd);
DBUG_RETURN(thd);
}
/**
* @brief Lock tables in driver.
*
* This method creates a new THD for use in the new thread. It calls
* the method to open and lock the tables.
*
* @note my_thread_init() should be paired with my_thread_end() on
* close/kill of thread.
*/
pthread_handler_t backup_thread_for_locking(void *arg)
{
Backup_thread_driver *drv= static_cast<Backup_thread_driver *>(arg);
DBUG_PRINT("info", ("Default_backup - lock_tables_in_separate_thread"));
/*
Turn off condition variable check for lock.
*/
drv->lock_state= LOCK_NOT_STARTED;
#if !defined( __WIN__) /* Win32 calls this in pthread_create */
my_thread_init();
#endif
pthread_detach_this_thread();
/*
First, create a new THD object.
*/
DBUG_PRINT("info",("Online backup creating THD struct for thread"));
THD *thd= create_new_thd();
drv->lock_thd= thd;
if (thd == 0)
{
drv->lock_state= LOCK_ERROR;
goto end2;
}
if (thd->killed)
{
drv->lock_state= LOCK_ERROR;
goto end2;
}
/*
Now open and lock the tables.
*/
DBUG_PRINT("info",("Online backup open tables in thread"));
if (!drv->tables_in_backup)
{
DBUG_PRINT("info",("Online backup locking error no tables to lock"));
drv->lock_state= LOCK_ERROR;
goto end2;
}
/*
As locking tables can be a long operation, we need to support
killing the thread. In this case, we need to close the tables
and exit.
*/
if (!thd->killed && open_and_lock_tables(thd, drv->tables_in_backup))
{
DBUG_PRINT("info",("Online backup locking thread dying"));
drv->lock_state= LOCK_ERROR;
goto end;
}
if (thd->killed)
{
drv->lock_state= LOCK_ERROR;
goto end;
}
drv->lock_state= LOCK_ACQUIRED;
/*
Part of work is done. Rest until woken up.
We wait if the thread is not killed and the driver has not signaled us.
*/
pthread_mutex_lock(&drv->THR_LOCK_driver_thread);
thd->enter_cond(&drv->COND_driver_thread_wait,
&drv->THR_LOCK_driver_thread,
"Online backup driver thread: holding table locks");
while (!thd->killed && (drv->lock_state != LOCK_SIGNAL))
pthread_cond_wait(&drv->COND_driver_thread_wait,
&drv->THR_LOCK_driver_thread);
thd->exit_cond("Online backup driver thread: terminating");
DBUG_PRINT("info",("Online backup driver thread locking thread terminating"));
/*
Cleanup and return.
*/
end:
close_thread_tables(thd);
end2:
pthread_mutex_lock(&drv->THR_LOCK_driver);
net_end(&thd->net);
my_thread_end();
delete thd;
drv->lock_thd= NULL;
if (drv->lock_state != LOCK_ERROR)
drv->lock_state= LOCK_DONE;
/*
Signal the driver thread that it's ok to proceed with destructor.
*/
pthread_cond_broadcast(&drv->COND_driver_wait);
pthread_mutex_unlock(&drv->THR_LOCK_driver);
pthread_exit(0);
return (0);
}
/*
Constructor for backup_thread_driver class.
*/
Backup_thread_driver::Backup_thread_driver(const Table_list &tables):
Backup_driver(tables)
{
/*
Initialize the thread mutex and cond variable.
*/
pthread_mutex_init(&THR_LOCK_driver_thread, MY_MUTEX_INIT_FAST);
pthread_cond_init(&COND_driver_thread_wait, NULL);
pthread_mutex_init(&THR_LOCK_driver, MY_MUTEX_INIT_FAST);
pthread_cond_init(&COND_driver_wait, NULL);
lock_state= LOCK_NOT_STARTED;
lock_thd= NULL; // set to 0 as precaution for get_data being called too soon
};
/*
Destructor for backup_thread_driver class.
*/
Backup_thread_driver::~Backup_thread_driver()
{
/*
If the locking thread is not finished, we need to wait until
it is finished so that we can destroy the mutexes safely knowing
the locking thread won't access them.
*/
if (lock_state != LOCK_DONE)
{
kill_locking_thread();
pthread_mutex_lock(&THR_LOCK_driver);
m_thd->enter_cond(&COND_driver_wait, &THR_LOCK_driver,
"Online backup driver: waiting until locking thread is done");
while (lock_state != LOCK_DONE)
pthread_cond_wait(&COND_driver_wait, &THR_LOCK_driver);
m_thd->exit_cond("Online backup driver: terminating");
DBUG_PRINT("info",("Online backup driver's locking thread terminated"));
}
/*
Destroy the thread mutexes and cond variables.
*/
pthread_mutex_destroy(&THR_LOCK_driver_thread);
pthread_cond_destroy(&COND_driver_thread_wait);
pthread_mutex_destroy(&THR_LOCK_driver);
pthread_cond_destroy(&COND_driver_wait);
}
/**
Start the driver's lock thread.
Launches a separate thread ("locking thread") which will lock
tables.
*/
result_t Backup_thread_driver::start_locking_thread()
{
DBUG_ENTER("Backup_thread_driver::start_locking_thread");
pthread_t th;
if (pthread_create(&th, &connection_attrib,
backup_thread_for_locking, this))
SET_STATE_TO_ERROR_AND_DBUG_RETURN;
DBUG_RETURN(backup::OK);
}
/**
Kill the driver's lock thread.
This method issues the awake and broadcast to kill the locking thread.
A mutex is used to prevent the locking thread from deleting the THD
structure until this operation is complete.
*/
void Backup_thread_driver::kill_locking_thread()
{
DBUG_ENTER("Backup_thread_driver::kill_locking_thread");
pthread_mutex_lock(&THR_LOCK_driver);
if (lock_thd && (lock_state != LOCK_DONE) && (lock_state !=
LOCK_SIGNAL))
{
lock_state= LOCK_SIGNAL;
pthread_mutex_lock(&lock_thd->LOCK_delete);
lock_thd->awake(THD::KILL_CONNECTION);
pthread_mutex_unlock(&lock_thd->LOCK_delete);
}
pthread_mutex_unlock(&THR_LOCK_driver);
if (lock_thd && (lock_state == LOCK_SIGNAL))
pthread_cond_broadcast(&COND_driver_thread_wait);
/*
This tells the CS driver that we're finished with the tables.
*/
if (!lock_thd && (lock_state == LOCK_ACQUIRED))
lock_state= LOCK_SIGNAL;
DBUG_VOID_RETURN;
}