List:Commits« Previous MessageNext Message »
From:ahristov Date:May 15 2006 9:10pm
Subject:bk commit into 5.1 tree (andrey:1.2398) BUG#17619
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of andrey. When andrey 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
  1.2398 06/05/15 23:10:30 andrey@lmy004. +17 -0
  small fixes for the fix for bug #17619 Scheduler race conditions
  fix problem when resuming the scheduler - if there are events in the queue
  then all execution times has to be recalculated before it continues
  operation because et->execute_at could be in the past and we don't
  want this.
  removed SHOW TEST EVENTS

  sql/sql_yacc.yy
    1.477 06/05/15 23:10:21 andrey@lmy004. +6 -15
    remove show test_events

  sql/sql_parse.cc
    1.547 06/05/15 23:10:21 andrey@lmy004. +1 -6
    remove show test_events

  sql/sql_lex.h
    1.230 06/05/15 23:10:20 andrey@lmy004. +2 -2
    remove show test_events

  sql/mysqld.cc
    1.555 06/05/15 23:10:20 andrey@lmy004. +4 -4
    don't kill the scheduler, it has no meaning. we shut it down a bit later.

  sql/lex.h
    1.162 06/05/15 23:10:20 andrey@lmy004. +1 -2
    remove show events

  sql/event_timed.cc
    1.53 06/05/15 23:10:20 andrey@lmy004. +9 -4
    - more debug info
    - set status DISABLED whenever needed.

  sql/event_scheduler.h
    1.2 06/05/15 23:10:20 andrey@lmy004. +24 -68
    returns true of we have waited
    remove COND_last_worker_exit and COND_bulk_drop_finished.

  sql/event_scheduler.cc
    1.2 06/05/15 23:10:20 andrey@lmy004. +428 -303
    more docs
    fix event shutdown when the scheduler is suspended
    add documentation

  sql/event.cc
    1.41 06/05/15 23:10:20 andrey@lmy004. +18 -7
    more debug info

  mysql-test/t/events_scheduling.test
    1.4 06/05/15 23:10:20 andrey@lmy004. +3 -3
    improve the test (covers more)

  mysql-test/t/events_microsec.test
    1.3 06/05/15 23:10:20 andrey@lmy004. +5 -45
    the events are loaded during server start from disk so on disk changes
    make no sense during runtime. save 5s execution time

  mysql-test/t/events_logs_tests.test
    1.7 06/05/15 23:10:20 andrey@lmy004. +8 -7
    save 5s execution time

  mysql-test/t/events.test
    1.31 06/05/15 23:10:20 andrey@lmy004. +4 -4
    remove FULL from show events

  mysql-test/r/events_scheduling.result
    1.3 06/05/15 23:10:20 andrey@lmy004. +6 -3
    update result

  mysql-test/r/events_microsec.result
    1.4 06/05/15 23:10:20 andrey@lmy004. +0 -46
    update result

  mysql-test/r/events_logs_tests.result
    1.7 06/05/15 23:10:20 andrey@lmy004. +9 -8
    update result

  mysql-test/r/events.result
    1.35 06/05/15 23:10:19 andrey@lmy004. +4 -7
    update result

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	andrey
# Host:	lmy004.
# Root:	/work/mysql-5.1-bug17619-new-with_system_thread_fixed_bulk_drop

--- 1.161/sql/lex.h	2006-05-11 16:41:05 +02:00
+++ 1.162/sql/lex.h	2006-05-15 23:10:20 +02:00
@@ -453,6 +453,7 @@ static SYMBOL symbols[] = {
   { "RTREE",		SYM(RTREE_SYM)},
   { "SAVEPOINT",	SYM(SAVEPOINT_SYM)},
   { "SCHEDULE",		SYM(SCHEDULE_SYM)},
+  { "SCHEDULER",	SYM(SCHEDULER_SYM)},
   { "SCHEMA",		SYM(DATABASE)},
   { "SCHEMAS",          SYM(DATABASES)},
   { "SECOND",		SYM(SECOND_SYM)},
@@ -503,7 +504,6 @@ static SYMBOL symbols[] = {
   { "STARTING",		SYM(STARTING)},
   { "STARTS",		SYM(STARTS_SYM)},
   { "STATUS",		SYM(STATUS_SYM)},
-  { "STATUS_EVENTS",	SYM(STATUS_EVENTS_SYM)},
   { "STOP",		SYM(STOP_SYM)},
   { "STORAGE",		SYM(STORAGE_SYM)},
   { "STRAIGHT_JOIN",	SYM(STRAIGHT_JOIN)},
@@ -519,7 +519,6 @@ static SYMBOL symbols[] = {
   { "TEMPORARY",	SYM(TEMPORARY)},
   { "TEMPTABLE",	SYM(TEMPTABLE_SYM)},
   { "TERMINATED",	SYM(TERMINATED)},
-  { "TEST_EVENTS",	SYM(TEST_EVENTS_SYM)},
   { "TEXT",		SYM(TEXT_SYM)},
   { "THAN",             SYM(THAN_SYM)},
   { "THEN",		SYM(THEN_SYM)},

--- 1.554/sql/mysqld.cc	2006-05-11 16:41:06 +02:00
+++ 1.555/sql/mysqld.cc	2006-05-15 23:10:20 +02:00
@@ -863,8 +863,8 @@ static void close_connections(void)
   {
     DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
 		       tmp->thread_id));
-    /* We skip slave threads on this first loop through. */
-    if (tmp->slave_thread)
+    /* We skip slave threads & scheduler on this first loop through. */
+    if (tmp->slave_thread || tmp->system_thread == SYSTEM_THREAD_EVENT_SCHEDULER)
       continue;
 
     tmp->killed= THD::KILL_CONNECTION;
@@ -4992,8 +4992,8 @@ Disable with --skip-bdb (will save memor
    (gptr*) &global_system_variables.engine_condition_pushdown,
    0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
   {"event-scheduler", OPT_EVENT_EXECUTOR, "Enable/disable the event scheduler.",
-   (gptr*) &opt_event_executor, (gptr*) &opt_event_executor, 0, GET_BOOL, NO_ARG,
-   1/*default*/, 0/*min-value*/, 1/*max-value*/, 0, 0, 0},
+   (gptr*) &opt_event_executor, (gptr*) &opt_event_executor, 0, GET_LONG, REQUIRED_ARG,
+   2/*default*/, 0/*min-value*/, 2/*max-value*/, 0, 0, 0},
   {"exit-info", 'T', "Used for debugging;  Use at your own risk!", 0, 0, 0,
    GET_LONG, OPT_ARG, 0, 0, 0, 0, 0, 0},
   {"external-locking", OPT_USE_LOCKING, "Use system (external) locking.  With this option enabled you can run myisamchk to test (not repair) tables while the MySQL server is running.",

--- 1.229/sql/sql_lex.h	2006-05-11 16:41:07 +02:00
+++ 1.230/sql/sql_lex.h	2006-05-15 23:10:20 +02:00
@@ -111,8 +111,8 @@ enum enum_sql_command {
   SQLCOM_SHOW_AUTHORS, SQLCOM_BINLOG_BASE64_EVENT,
   SQLCOM_SHOW_PLUGINS,
   SQLCOM_CREATE_EVENT, SQLCOM_ALTER_EVENT, SQLCOM_DROP_EVENT,
-  SQLCOM_SHOW_CREATE_EVENT, SQLCOM_SHOW_EVENTS, SQLCOM_SHOW_EVENTS_TEST,
-  SQLCOM_SHOW_EVENTS_INTERNAL_STATUS,
+  SQLCOM_SHOW_CREATE_EVENT, SQLCOM_SHOW_EVENTS, 
+  SQLCOM_SHOW_SCHEDULER_STATUS,
 
   /* This should be the last !!! */
 

--- 1.546/sql/sql_parse.cc	2006-05-11 16:41:08 +02:00
+++ 1.547/sql/sql_parse.cc	2006-05-15 23:10:21 +02:00
@@ -3881,12 +3881,7 @@ end_with_restore_list:
     break;
   }
 #ifndef DBUG_OFF
-  case SQLCOM_SHOW_EVENTS_TEST:
-  {
-    res= Event_scheduler::tests_run(thd);
-    break;
-  }
-  case SQLCOM_SHOW_EVENTS_INTERNAL_STATUS:
+  case SQLCOM_SHOW_SCHEDULER_STATUS:
   {
     res= Event_scheduler::dump_internal_status(thd);
     break;

--- 1.476/sql/sql_yacc.yy	2006-05-11 16:41:08 +02:00
+++ 1.477/sql/sql_yacc.yy	2006-05-15 23:10:21 +02:00
@@ -567,6 +567,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  RTREE_SYM
 %token  SAVEPOINT_SYM
 %token  SCHEDULE_SYM
+%token  SCHEDULER_SYM
 %token  SECOND_MICROSECOND_SYM
 %token  SECOND_SYM
 %token  SECURITY_SYM
@@ -608,7 +609,6 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  START_SYM
 %token  STARTS_SYM
 %token  STATUS_SYM
-%token  STATUS_EVENTS_SYM
 %token  STD_SYM
 %token  STDDEV_SAMP_SYM
 %token  STOP_SYM
@@ -631,7 +631,6 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  TEMPORARY
 %token  TEMPTABLE_SYM
 %token  TERMINATED
-%token  TEST_EVENTS_SYM
 %token  TEXT_STRING
 %token  TEXT_SYM
 %token  TIMESTAMP
@@ -8051,28 +8050,19 @@ show_param:
              if (prepare_schema_table(YYTHD, lex, 0, SCH_TRIGGERS))
                YYABORT;
            }
-         | opt_full EVENTS_SYM opt_db wild_and_where
+         | EVENTS_SYM opt_db wild_and_where
            {
              LEX *lex= Lex;
              lex->sql_command= SQLCOM_SELECT;
              lex->orig_sql_command= SQLCOM_SHOW_EVENTS;
-             lex->select_lex.db= $3;
+             lex->select_lex.db= $2;
              if (prepare_schema_table(YYTHD, lex, 0, SCH_EVENTS))
                YYABORT;
            }
-         | TEST_EVENTS_SYM
-           {
-#ifndef DBUG_OFF
-             Lex->sql_command= SQLCOM_SHOW_EVENTS_TEST;
-#else
-             yyerror(ER(ER_SYNTAX_ERROR));
-             YYABORT;           
-#endif
-           }         
-         | STATUS_EVENTS_SYM
+         | SCHEDULER_SYM STATUS_SYM
            {
 #ifndef DBUG_OFF
-             Lex->sql_command= SQLCOM_SHOW_EVENTS_INTERNAL_STATUS;
+             Lex->sql_command= SQLCOM_SHOW_SCHEDULER_STATUS;
 #else
              yyerror(ER(ER_SYNTAX_ERROR));
              YYABORT;           
@@ -9508,6 +9498,7 @@ keyword_sp:
 	| ROW_SYM		{}
 	| RTREE_SYM		{}
 	| SCHEDULE_SYM		{}	
+	| SCHEDULER_SYM		{}	
 	| SECOND_SYM		{}
 	| SERIAL_SYM		{}
 	| SERIALIZABLE_SYM	{}

--- 1.34/mysql-test/r/events.result	2006-05-11 19:07:21 +02:00
+++ 1.35/mysql-test/r/events.result	2006-05-15 23:10:19 +02:00
@@ -324,18 +324,18 @@ events_test	one_event	ev_test@localhost	
 events_test	three_event	ev_test@localhost	RECURRING	NULL	20	SECOND	#	#	ENABLED
 events_test	two_event	ev_test@localhost	RECURRING	NULL	20	SECOND	#	#	ENABLED
 "This should show us only 3 events:";
-SHOW FULL EVENTS;
+SHOW EVENTS;
 Db	Name	Definer	Type	Execute at	Interval value	Interval field	Starts	Ends	Status
 events_test	one_event	ev_test@localhost	RECURRING	NULL	20	SECOND	#	#	ENABLED
 events_test	three_event	ev_test@localhost	RECURRING	NULL	20	SECOND	#	#	ENABLED
 events_test	two_event	ev_test@localhost	RECURRING	NULL	20	SECOND	#	#	ENABLED
 "This should show us only 2 events:";
-SHOW FULL EVENTS LIKE 't%event';
+SHOW EVENTS LIKE 't%event';
 Db	Name	Definer	Type	Execute at	Interval value	Interval field	Starts	Ends	Status
 events_test	three_event	ev_test@localhost	RECURRING	NULL	20	SECOND	#	#	ENABLED
 events_test	two_event	ev_test@localhost	RECURRING	NULL	20	SECOND	#	#	ENABLED
 "This should show us no events:";
-SHOW FULL EVENTS FROM test LIKE '%';
+SHOW EVENTS FROM test LIKE '%';
 Db	Name	Definer	Type	Execute at	Interval value	Interval field	Starts	Ends	Status
 DROP DATABASE events_test2;
 "should see 1 event:";
@@ -343,11 +343,8 @@ SHOW EVENTS;
 Db	Name	Definer	Type	Execute at	Interval value	Interval field	Starts	Ends	Status
 events_test	one_event	root@localhost	RECURRING	NULL	10	SECOND	#	#	ENABLED
 "we should see 4 events now:";
-SHOW FULL EVENTS;
+SHOW EVENTS;
 Db	Name	Definer	Type	Execute at	Interval value	Interval field	Starts	Ends	Status
-events_test	one_event	ev_test@localhost	RECURRING	NULL	20	SECOND	#	#	ENABLED
-events_test	three_event	ev_test@localhost	RECURRING	NULL	20	SECOND	#	#	ENABLED
-events_test	two_event	ev_test@localhost	RECURRING	NULL	20	SECOND	#	#	ENABLED
 events_test	one_event	root@localhost	RECURRING	NULL	10	SECOND	#	#	ENABLED
 SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT from information_schema.events;
 EVENT_CATALOG	EVENT_SCHEMA	EVENT_NAME	DEFINER	EVENT_BODY	EVENT_TYPE	EXECUTE_AT	INTERVAL_VALUE	INTERVAL_FIELD	STATUS	ON_COMPLETION	EVENT_COMMENT

--- 1.3/mysql-test/r/events_microsec.result	2006-05-11 16:31:44 +02:00
+++ 1.4/mysql-test/r/events_microsec.result	2006-05-15 23:10:20 +02:00
@@ -10,50 +10,4 @@ CREATE EVENT micro_test ON SCHEDULE EVER
 ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND'
 CREATE EVENT micro_test ON SCHEDULE EVERY 100 SECOND_MICROSECOND DO SELECT 1;
 ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND'
-"Now create normal event and change it on SQL level"
-CREATE EVENT micro_test2 ON SCHEDULE EVERY 1 MONTH DO SELECT 1;
-UPDATE mysql.event SET interval_field='MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2';
-SHOW CREATE EVENT micro_test2;
-ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND'
-SET GLOBAL event_scheduler=0;
-"Should not be running:"
-SHOW VARIABLES like 'event_scheduler';
-Variable_name	Value
-event_scheduler	OFF
-UPDATE mysql.event SET interval_field='DAY_MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2';
-SHOW CREATE EVENT micro_test2;
-ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND'
-SET GLOBAL event_scheduler=0;
-"Should not be running:"
-SHOW VARIABLES like 'event_scheduler';
-Variable_name	Value
-event_scheduler	OFF
-UPDATE mysql.event SET interval_field='SECOND_MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2';
-SHOW CREATE EVENT micro_test2;
-ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND'
-SET GLOBAL event_scheduler=0;
-"Should not be running:"
-SHOW VARIABLES like 'event_scheduler';
-Variable_name	Value
-event_scheduler	OFF
-UPDATE mysql.event SET interval_field='HOUR_MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2';
-SHOW CREATE EVENT micro_test2;
-ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND'
-SET GLOBAL event_scheduler=0;
-"Should not be running:"
-SHOW VARIABLES like 'event_scheduler';
-Variable_name	Value
-event_scheduler	OFF
-UPDATE mysql.event SET interval_field='MINUTE_MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2';
-SHOW CREATE EVENT micro_test2;
-ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND'
-SET GLOBAL event_scheduler=0;
-"Should not be running:"
-SHOW VARIABLES like 'event_scheduler';
-Variable_name	Value
-event_scheduler	OFF
-SELECT COUNT(*) FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER='event_scheduler';
-COUNT(*)
-1
-DROP EVENT micro_test2;
 drop database events_test;

--- 1.2/mysql-test/r/events_scheduling.result	2006-04-24 11:12:04 +02:00
+++ 1.3/mysql-test/r/events_scheduling.result	2006-05-15 23:10:20 +02:00
@@ -14,7 +14,7 @@ ENDS NOW() + INTERVAL 6 SECOND
 ON COMPLETION PRESERVE
 DO INSERT INTO table_2 VALUES(1);
 CREATE EVENT only_one_time ON SCHEDULE EVERY 2 SECOND ENDS NOW() + INTERVAL 1 SECOND DO INSERT INTO table_3 VALUES(1);
-CREATE EVENT two_time ON SCHEDULE EVERY 1 SECOND ENDS NOW() + INTERVAL 1 SECOND DO INSERT INTO table_4 VALUES(1);
+CREATE EVENT two_time ON SCHEDULE EVERY 1 SECOND ENDS NOW() + INTERVAL 1 SECOND ON COMPLETION PRESERVE DO INSERT INTO table_4 VALUES(1);
 SELECT IF(SUM(a) >= 4, 'OK', 'ERROR') FROM table_1;
 IF(SUM(a) >= 4, 'OK', 'ERROR')
 OK
@@ -38,9 +38,12 @@ DROP EVENT start_n_end;
 "Already dropped because ended. Therefore an error."
 DROP EVENT only_one_time;
 ERROR HY000: Unknown event 'only_one_time'
-"Already dropped because ended. Therefore an error."
+"Should be preserved"
+SELECT EVENT_NAME, STATUS FROM INFORMATION_SCHEMA.EVENTS;
+EVENT_NAME	STATUS
+E19170	ENABLED
+two_time	DISABLED
 DROP EVENT two_time;
-ERROR HY000: Unknown event 'two_time'
 DROP TABLE table_1;
 DROP TABLE table_2;
 DROP TABLE table_3;

--- 1.6/mysql-test/r/events_logs_tests.result	2006-04-24 11:12:04 +02:00
+++ 1.7/mysql-test/r/events_logs_tests.result	2006-05-15 23:10:20 +02:00
@@ -36,14 +36,14 @@ SELECT user_host, query_time, db, sql_te
 user_host	query_time	db	sql_text
 "Set new values"
 SET GLOBAL long_query_time=4;
-SET SESSION long_query_time=2;
+SET SESSION long_query_time=1;
 "Check that logging is working"
-SELECT SLEEP(3);
-SLEEP(3)
+SELECT SLEEP(2);
+SLEEP(2)
 0
 SELECT user_host, query_time, db, sql_text FROM mysql.slow_log;
 user_host	query_time	db	sql_text
-root[root] @ localhost []	SLEEPVAL	events_test	SELECT SLEEP(3)
+root[root] @ localhost []	SLEEPVAL	events_test	SELECT SLEEP(2)
 TRUNCATE mysql.slow_log;
 CREATE TABLE slow_event_test (slo_val tinyint, val tinyint);
 "This won't go to the slow log"
@@ -64,18 +64,19 @@ SELECT user_host, query_time, db, sql_te
 user_host	query_time	db	sql_text
 "This should go to the slow log"
 SET SESSION long_query_time=10;
+SET GLOBAL long_query_time=1;
 DROP EVENT long_event;
-CREATE EVENT long_event2 ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(5);
+CREATE EVENT long_event2 ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(2);
 "Sleep some more time than the actual event run will take"
 "Check our table. Should see 2 rows"
 SELECT * FROM slow_event_test;
 slo_val	val
 4	0
-4	0
-"Check slow log. Should see 1 row because 5 is over the threshold of 4 for GLOBAL, though under SESSION which is 10"
+1	0
+"Check slow log. Should see 1 row because 4 is over the threshold of 3 for GLOBAL, though under SESSION which is 10"
 SELECT user_host, query_time, db, sql_text FROM mysql.slow_log;
 user_host	query_time	db	sql_text
-root[root] @ localhost [localhost]	SLEEPVAL	events_test	INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(5)
+root[root] @ localhost [localhost]	SLEEPVAL	events_test	INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(2)
 DROP EVENT long_event2;
 SET GLOBAL  long_query_time =@old_global_long_query_time;
 SET SESSION long_query_time =@old_session_long_query_time;

--- 1.30/mysql-test/t/events.test	2006-05-11 19:07:21 +02:00
+++ 1.31/mysql-test/t/events.test	2006-05-15 23:10:20 +02:00
@@ -279,15 +279,15 @@ SHOW EVENTS;
 
 --echo "This should show us only 3 events:";
 --replace_column 8 # 9 #
-SHOW FULL EVENTS;
+SHOW EVENTS;
 
 --echo "This should show us only 2 events:";
 --replace_column 8 # 9 #
-SHOW FULL EVENTS LIKE 't%event';
+SHOW EVENTS LIKE 't%event';
 
 --echo "This should show us no events:";
 --replace_column 8 # 9 #
-SHOW FULL EVENTS FROM test LIKE '%';
+SHOW EVENTS FROM test LIKE '%';
 #ok, we are back
 connection default;
 DROP DATABASE events_test2;
@@ -298,7 +298,7 @@ SHOW EVENTS;
 
 --echo "we should see 4 events now:";
 --replace_column 8 # 9 #
-SHOW FULL EVENTS;
+SHOW EVENTS;
 SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT from information_schema.events;
 
 connection ev_con1;

--- 1.2/mysql-test/t/events_microsec.test	2006-02-24 15:28:10 +01:00
+++ 1.3/mysql-test/t/events_microsec.test	2006-05-15 23:10:20 +02:00
@@ -1,55 +1,15 @@
 create database if not exists events_test;
 use events_test;
 
---error 1235
+--error ER_NOT_SUPPORTED_YET
 CREATE EVENT micro_test ON SCHEDULE EVERY 100 MICROSECOND DO SELECT 1;
---error 1235
+--error ER_NOT_SUPPORTED_YET
 CREATE EVENT micro_test ON SCHEDULE EVERY 100 DAY_MICROSECOND DO SELECT 1;
---error 1235
+--error ER_NOT_SUPPORTED_YET
 CREATE EVENT micro_test ON SCHEDULE EVERY 100 HOUR_MICROSECOND DO SELECT 1;
---error 1235
+--error ER_NOT_SUPPORTED_YET
 CREATE EVENT micro_test ON SCHEDULE EVERY 100 MINUTE_MICROSECOND DO SELECT 1;
---error 1235
+--error ER_NOT_SUPPORTED_YET
 CREATE EVENT micro_test ON SCHEDULE EVERY 100 SECOND_MICROSECOND DO SELECT 1;
-
---echo "Now create normal event and change it on SQL level"
-CREATE EVENT micro_test2 ON SCHEDULE EVERY 1 MONTH DO SELECT 1;
-UPDATE mysql.event SET interval_field='MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2';
---error 1235
-SHOW CREATE EVENT micro_test2;
-SET GLOBAL event_scheduler=0;
---sleep 1
---echo "Should not be running:"
-SHOW VARIABLES like 'event_scheduler';
-UPDATE mysql.event SET interval_field='DAY_MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2';
---error 1235
-SHOW CREATE EVENT micro_test2;
-SET GLOBAL event_scheduler=0;
---sleep 1
---echo "Should not be running:"
-SHOW VARIABLES like 'event_scheduler';
-UPDATE mysql.event SET interval_field='SECOND_MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2';
---error 1235
-SHOW CREATE EVENT micro_test2;
-SET GLOBAL event_scheduler=0;
---sleep 1
---echo "Should not be running:"
-SHOW VARIABLES like 'event_scheduler';
-UPDATE mysql.event SET interval_field='HOUR_MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2';
---error 1235
-SHOW CREATE EVENT micro_test2;
-SET GLOBAL event_scheduler=0;
---sleep 1
---echo "Should not be running:"
-SHOW VARIABLES like 'event_scheduler';
-UPDATE mysql.event SET interval_field='MINUTE_MICROSECOND' WHERE db=database() AND definer=user() AND name='micro_test2';
---error 1235
-SHOW CREATE EVENT micro_test2;
-SET GLOBAL event_scheduler=0;
---sleep 1
---echo "Should not be running:"
-SHOW VARIABLES like 'event_scheduler';
-SELECT COUNT(*) FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER='event_scheduler';
-DROP EVENT micro_test2;
 
 drop database events_test;

--- 1.3/mysql-test/t/events_scheduling.test	2006-04-24 11:12:04 +02:00
+++ 1.4/mysql-test/t/events_scheduling.test	2006-05-15 23:10:20 +02:00
@@ -15,7 +15,7 @@ CREATE EVENT start_n_end
   DO INSERT INTO table_2 VALUES(1);
 --sleep 5
 CREATE EVENT only_one_time ON SCHEDULE EVERY 2 SECOND ENDS NOW() + INTERVAL 1 SECOND DO INSERT INTO table_3 VALUES(1);
-CREATE EVENT two_time ON SCHEDULE EVERY 1 SECOND ENDS NOW() + INTERVAL 1 SECOND DO INSERT INTO table_4 VALUES(1);
+CREATE EVENT two_time ON SCHEDULE EVERY 1 SECOND ENDS NOW() + INTERVAL 1 SECOND ON COMPLETION PRESERVE DO INSERT INTO table_4 VALUES(1);
 --sleep 5
 SELECT IF(SUM(a) >= 4, 'OK', 'ERROR') FROM table_1;
 SELECT IF(SUM(a) >= 5, 'OK', 'ERROR') FROM table_2;
@@ -28,8 +28,8 @@ DROP EVENT start_n_end;
 --echo "Already dropped because ended. Therefore an error."
 --error ER_EVENT_DOES_NOT_EXIST
 DROP EVENT only_one_time;
---echo "Already dropped because ended. Therefore an error."
---error ER_EVENT_DOES_NOT_EXIST
+--echo "Should be preserved"
+SELECT EVENT_NAME, STATUS FROM INFORMATION_SCHEMA.EVENTS;
 DROP EVENT two_time;
 DROP TABLE table_1;
 DROP TABLE table_2;

--- 1.6/mysql-test/t/events_logs_tests.test	2006-04-24 11:12:04 +02:00
+++ 1.7/mysql-test/t/events_logs_tests.test	2006-05-15 23:10:20 +02:00
@@ -53,10 +53,10 @@ TRUNCATE mysql.slow_log;
 SELECT user_host, query_time, db, sql_text FROM mysql.slow_log;
 --echo "Set new values"
 SET GLOBAL long_query_time=4;
-SET SESSION long_query_time=2;
+SET SESSION long_query_time=1;
 --echo "Check that logging is working"
-SELECT SLEEP(3);
---replace_regex /00:00:0[3-5]/SLEEPVAL/
+SELECT SLEEP(2);
+--replace_regex /00:00:0[2-4]/SLEEPVAL/
 SELECT user_host, query_time, db, sql_text FROM mysql.slow_log;
 TRUNCATE mysql.slow_log;
 CREATE TABLE slow_event_test (slo_val tinyint, val tinyint);
@@ -73,14 +73,15 @@ SELECT * FROM slow_event_test;
 SELECT user_host, query_time, db, sql_text FROM mysql.slow_log;
 --echo "This should go to the slow log"
 SET SESSION long_query_time=10;
+SET GLOBAL long_query_time=1;
 DROP EVENT long_event;
-CREATE EVENT long_event2 ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(5);
+CREATE EVENT long_event2 ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(2);
 --echo "Sleep some more time than the actual event run will take"
---sleep 7
+--sleep 3
 --echo "Check our table. Should see 2 rows"
 SELECT * FROM slow_event_test;
---echo "Check slow log. Should see 1 row because 5 is over the threshold of 4 for GLOBAL, though under SESSION which is 10"
---replace_regex /00:00:0[5-7]/SLEEPVAL/
+--echo "Check slow log. Should see 1 row because 4 is over the threshold of 3 for GLOBAL, though under SESSION which is 10"
+--replace_regex /00:00:0[2-4]/SLEEPVAL/
 SELECT user_host, query_time, db, sql_text FROM mysql.slow_log;
 DROP EVENT long_event2;
 SET GLOBAL  long_query_time =@old_global_long_query_time;

--- 1.40/sql/event.cc	2006-05-11 17:08:29 +02:00
+++ 1.41/sql/event.cc	2006-05-15 23:10:20 +02:00
@@ -412,17 +412,24 @@ bool events_initted= false;
 int
 events_init()
 {
+  int ret= 0;
   DBUG_ENTER("events_init");
 
   /* it should be an assignment! */
-  if (opt_event_executor && (events_initted= true) &&
-      Event_scheduler::get_instance()->start(true))
+  if (opt_event_executor)
   {
-    events_initted= false;
-    sql_print_error("SCHEDULER: Error while starting");
-    DBUG_RETURN(1);
+    Event_scheduler *scheduler= Event_scheduler::get_instance();
+    events_initted= true;
+    DBUG_ASSERT(opt_event_executor ==1 || opt_event_executor == 2);
+    ret= opt_event_executor==1? scheduler->start():
+                                scheduler->start_suspended();
+    if (ret)
+    {
+      events_initted= false;
+      sql_print_error("SCHEDULER: Error while starting");
+    }
   }
-  DBUG_RETURN(0);
+  DBUG_RETURN(ret? 1:0);
 }
 
 
@@ -443,7 +450,7 @@ events_shutdown()
 
   if (events_initted)
   {
-    Event_scheduler::get_instance()->shutdown(true);
+    Event_scheduler::get_instance()->shutdown();
 
     if (Event_scheduler::destroy_singleton())
     {
@@ -569,8 +576,12 @@ evex_db_find_event_by_name(THD *thd, con
 
   if (table->file->index_read_idx(table->record[0], 0, key,
                                  table->key_info->key_length,HA_READ_KEY_EXACT))
+  {
+    DBUG_PRINT("info", ("Row not fonud"));
     DBUG_RETURN(EVEX_KEY_NOT_FOUND);
+  }
 
+  DBUG_PRINT("info", ("Row found!"));
   DBUG_RETURN(0);
 }
 

--- 1.1/sql/event_scheduler.cc	2006-05-11 16:31:48 +02:00
+++ 1.2/sql/event_scheduler.cc	2006-05-15 23:10:20 +02:00
@@ -24,16 +24,247 @@
   2. Talk to Alik to get a check for configure.in for my_time_t and time_t
   3. Look at guardian.h|cc to see its life cycle, has similarities according
      to Kostja.
-  4. Has race with evex_create_event() and load_events_from_db().
-  5. Make possible SHOW STATUS_EVENTS to dump on which condition it's currently
+  4. Make possible SHOW STATUS_EVENTS to dump on which condition it's currently
      waiting -> putting all conditionals into an array, which is controlled by
      an enum. Using the enum to dump information and access the conditional.
 */
 
+
+/*
+  The scheduler is implemented as class Event_scheduler. Only one instance is
+  kept during the runtime of the server, by implementing the Singleton DP.
+  Object instance is always there because the memory is allocated statically
+  and initialized when the OS loader loads mysqld. This initialization is
+  bare. Extended initialization is done first time the scheduler is used 
+  (see opt_init). Currently this happens when the server starts in
+  events_init(), defined in event.cc and called in mysqld.cc . If the
+  mysqld is started with --event-scheduler=0 then no initialization takes
+  place and the scheduler is unavailable during this server run. The server
+  should be started with --event-scheduler=1 to have the scheduler initialized
+  and able to execute jobs. This starting always implies that the jobs
+  execution will start immediately. If the server is started with 
+  --event-scheduler=2 then the scheduler is started in suspended state.
+
+  The scheduler only manages execution of the events. Their creation,
+  alteration and deletion is delegated to other routines found in event.cc .
+  These routines interact with the scheduler :
+  - CREATE EVENT -> Event_scheduler::add_event()
+  - ALTER EVENT  -> Event_scheduler::replace_event()
+  - DROP EVENT   -> Event_scheduler::drop_event()
+
+  There is one mutex in the single Event_scheduler object which controls
+  the simultaneous access to the objects invariants. Using one lock makes
+  it easy to follow the workflow. This mutex is LOCK_scheduler_data. It is
+  initialized in the constructor of the object and thus performed when the OS
+  loader starts mysqld -> no concurrency at this point. It's destructed when
+  the OS unloads mysqld, then it calls the destructor of the object -> again
+  no problems with concurrency.
+
+  The full initialization is done in Event_scheduler::init_object_impl() called
+  from Event_scheduler::opt_init(). The call to the latter is guarded by the
+  LOCK_scheduler_data mutex (using LOCK_SCHEDULER_DATA() macro). Thus there
+  are no problems with the concurrency when two or more threads will try to
+  initialize the scheduler. ATM the initialization is performed during server
+  start in events_init(), so there is no implied concurrency but the code is
+  able to handle this.
+
+  The scheduler is started with Event_scheduler::start() and stopped with
+  Event_scheduler::shutdown(). It can be started or stoppped also with 
+  Event_scheduler::start_or_shutdown(bool new_state). When the scheduler starts
+  it loads all events from mysql.event table. Unfortunately, there is a
+  race condition between the event disk management functions and the
+  scheduler ones (add/replace/drop_event & load_events_from_db()), because
+  the opeations does not happen under one global lock but the disk operations
+  are guarded by the myisam lock on mysql.event, while the queue operations
+  by LOCK_scheduler_data. If the scheduler is start()-ed during server
+  startup and shutdown()-ed during server shutdown (in events_shutdown()
+  called by kill_server() in mysqld.cc) these races does not exist.
+
+  Since the user may want to temporarily inhibit execution of events the
+  scheduler can be suspended and then it can be forced to resume its
+  operations. The API call to perform these is
+  Event_scheduler::suspend_or_resume(enum enum_suspend_or_resume) .
+  When the scheduler is suspended the main scheduler thread, which ATM
+  happens to have thread_id 1, locks on a condition COND_suspend_or_resume.
+  When this is signal is sent for the reverse operation the main scheduler
+  loops continues to roll and execute events.
+
+  When the scheduler is suspended all add/replace/drop_event() operations
+  work as expected and the modify the queue but no events execution takes
+  place.
+
+  In contrast to the previous scheduler implementation, found in
+  event_executor.cc, the start, shutdown, suspend and resume are synchronous
+  operations. As a whole all operations are synchronized and no busy waits
+  are used except in stop_all_running_events(), which waits until all
+  running event worker threads have finished. It would have been nice to
+  use a conditional on which this method will wait and the last thread to
+  finish would signal it but this implies subclassing THD.
+
+  The scheduler does not keep a counter of how many event worker threads are
+  running, at any specific moment, because this will copy functionality
+  already existing in the server. Namely, all THDs are registered in the
+  global `threads` array. THD has member variable system_thread which
+  identifies the type of thread. Connection threads being NON_SYSTEM_THREAD,
+  all other have their enum value. Important for the scheduler are 
+  SYSTEM_THREAD_EVENT_SCHEDULER and SYSTEM_THREAD_EVENT_WORKER.
+
+  Class THD subclasses class ilink, which is the linked list of all threads.
+  When a THD instance is destroyed it's being removed from threads, thus
+  no manual intervention is needed. On the contrary registering is manual
+  with threads.append() . Traversing the threads array every time a subclass
+  of THD, for instance if we would have had THD_scheduler_worker to see
+  how many events we have and whether the scheduler is shutting down will
+  take much time and lead to a deadlock. stop_all_running_events() is called
+  under LOCK_scheduler_data. If the THD_scheduler_worker was aware of
+  the single Event_scheduler instance it will try to check
+  Event_scheduler::state but for this it would need to acquire 
+  LOCK_scheduler_data => deadlock. Thus stop_all_running_events() uses a
+  busy wait.
+
+  DROP DATABASE DDL should drop all events defined in a specific schema.
+  DROP USER also should drop all events who has as definer the user being
+  dropped (this one is not addressed at the moment but a hook exists). For
+  this specific needs Event_scheduler::drop_matching_events() is
+  implemented. Which expects a callback to be applied on every object in
+  the queue. Thus events that match specific schema or user, will be
+  removed from the queue. The exposed interface is :
+  - Event_scheduler::drop_schema_events()
+  - Event_scheduler::drop_user_events()
+
+  This bulk dropping happens under LOCK_scheduler_data, thus no two or
+  more threads can execute it in parallel. However, DROP DATABASE is also
+  synchronized, currently, in the server thus this does not impact the 
+  overall performance. In addition, DROP DATABASE is not that often
+  executed DDL.
+
+  Though the interface to the scheduler is only through the public methods
+  of class Event_scheduler, there are currently few functions which are
+  used during its operations. Namely :
+  - static evex_print_warnings()
+    After every event execution all errors/warnings are dumped, so the user
+    can see in case of a problem what the problem was.
+
+  - static init_event_thread()
+    This function is both used by event_scheduler_run_proxy() and
+    event_worker_thread(). It initializes the THD structure. The
+    initialization looks pretty similar to the one in slave.cc done for the
+    replication threads. However, though the similarities it cannot be
+    factored out to have one routine.
+
+  - static event_scheduler_run_proxy()
+    Because our way to register functions to be used by the threading library
+    does not allow usage of static methods this function is used to start the
+    scheduler in it. It does THD initialization and then calls
+    Event_scheduler::run(). 
+
+  - static event_worker_thread()
+    With already stated the reason for not being able to use methods, this
+    function executes the worker threads.
+
+  The execution of events is, to some extent, synchronized to inhibit race
+  conditions when Event_timed::thread_id is being updated with the thread_id of
+  the THD in which the event is being executed. The thread_id is in the
+  Event_timed object because we need to be able to kill quickly a specific
+  event during ALTER/DROP EVENT without traversing the global `threads` array.
+  However, this makes the scheduler's code more complicated. The event worker
+  thread is started by Event_timed::spawn_now(), which in turn calls
+  pthread_create(). The thread_id which will be associated in init_event_thread
+  is not known in advance thus the registering takes place in
+  event_worker_thread(). This registering has to be synchronized under
+  LOCK_scheduler_data, so no kill_event() on a object in
+  replace_event/drop_event/drop_matching_events() could take place.
+  
+  This synchronization is done through class Worker_thread_param that is
+  local to this file. Event_scheduler::execute_top() is called under
+  LOCK_scheduler_data. This method :
+  1. Creates an instance of Worker_thread_param on the stack
+  2. Locks Worker_thread_param::LOCK_started
+  3. Calls Event_timed::spawn_now() which in turn creates a new thread.
+  4. Locks on Worker_thread_param::COND_started and waits till the
+     worker thread send signal. The code is spurious wake-up safe because
+     Worker_thread_param::started is checked.
+  5. The worker thread initializes it's THD, then sets Event_timed::thread_id,
+     sets Worker_thread_param::started to true and sends back
+     Worker_thread_param::COND_started. From this moment on, the event
+     is being executed and could be killed by using Event_timed::thread_id.
+     When Event_timed::spawn_thread_finish() is called in the worker thread,
+     it sets thread_id to 0. From this moment on, the worker thread should not
+     touch the Event_timed instance.
+  
+  
+  The life-cycle of the server is a FSA.
+  enum enum_state Event_scheduler::state keeps the state of the scheduler.
+  One exception is when the scheduler is suspended, then
+  Event_scheduler::suspended is set to true. I think this can be integrated in
+  enum enum_state.
+
+  The states are:
+
+  |---UNINITIALIZED
+  |                                         
+  |                                         
+  --> INITIALIZED -> COMMENCING ---> RUNNING -----------|--> IN_SHUTDOWN 
+       ^ ^               |            | ^               |
+       | |- CANTSTART <--|            | |- SUSPENDED <--|
+       |______________________________|
+
+    - UNINITIALIZED :The object is created and only the mutex is initialized
+    - INITIALIZED   :All member variables are initialized
+    - COMMENCING    :The scheduler is starting, no other attempt to start 
+                     should succeed before the state is back to INITIALIZED.
+    - CANTSTART     :Set by the ::run() method in case it can't start for some
+                     reason. In this case the connection thread that tries to
+                     start the scheduler sees that some error has occurred and
+                     returns an error to the user. Finally, the connection
+                     thread sets the state to INITIALIZED, so further attempts
+                     to start the scheduler could be made.
+    - RUNNING       :The scheduler is running. New events could be added,
+                     dropped, altered. The scheduler could be stopped.
+    - SUSPENDED     :Like RUNNING but execution of events does not take place.
+                     Operations on the memory queue are possible.
+    - IN_SHUTDOWN   :The scheduler is shutting down, due to request by setting
+                     the global event_scheduler to 0/false, or because of a
+                     KILL command sent by a user to the master thread.
+
+  In every method the macros LOCK_SCHEDULER_DATA() and UNLOCK_SCHEDULER_DATA()
+  are used for (un)locking purposes.  They are used to save the programmer
+  from typing everytime
+  lock_data(__FUNCTION__, __LINE__); 
+  All locking goes through Event_scheduler::lock_data() and ::unlock_data().
+  These two functions then record in static class variables where for last time
+  LOCK_scheduler_data was locked and unlocked (two different variables). In
+  multithreaded environment, in some cases they make no sense but are useful for
+  inspecting deadlocks without having the server debug log turned on and the
+  server is still running.
+
+  The same strategy is used for conditional variables.
+  Event_scheduler::cond_wait() is invoked from all places with parameter
+  an enum enum_cond_vars. In this manner, it's possible to inspect the last
+  on which condition the last call to cond_wait() was waiting. If the server
+  was started with debug trace switched on, the trace file also holds information
+  about conditional variables used.
+*/
+
+
 #define LOCK_SCHEDULER_DATA()   lock_data(__FUNCTION__,__LINE__)
 #define UNLOCK_SCHEDULER_DATA() unlock_data(__FUNCTION__,__LINE__)
 
 
+#ifndef DBUG_OFF
+static
+LEX_STRING states_names[] =
+{
+  {(char*) STRING_WITH_LEN("UNINITIALIZED")},
+  {(char*) STRING_WITH_LEN("INITIALIZED")},
+  {(char*) STRING_WITH_LEN("COMMENCING")},
+  {(char*) STRING_WITH_LEN("CANTSTART")},
+  {(char*) STRING_WITH_LEN("RUNNING")},
+  {(char*) STRING_WITH_LEN("SUSPENDED")},
+  {(char*) STRING_WITH_LEN("IN_SHUTDOWN")}
+};
+#endif
+
 class Worker_thread_param
 {
 public:
@@ -67,8 +298,6 @@ Event_scheduler::cond_vars_names[Event_s
   "new work",
   "started",
   "shutdown",
-  "last worker to exit",
-  "bulk drop finished",
   "suspend or resume"
 };
 
@@ -363,8 +592,6 @@ event_worker_thread(void *arg)
     else if (ret == EVEX_MICROSECOND_UNSUP)
       sql_print_information("SCHEDULER: MICROSECOND is not supported");
     
-    /* Do we need that? We abandon the thread anyway */
-    event->restore_security_context(thd, save_ctx);
     DBUG_PRINT("info", ("master_access=%d db_access=%d",
                thd->security_ctx->master_access, thd->security_ctx->db_access));
     /*
@@ -381,7 +608,10 @@ event_worker_thread(void *arg)
       the in-memory queue and we hold the only reference
     */
     if (flags & EVENT_FREE_WHEN_FINISHED)
+    {
+      DBUG_PRINT("info", ("Freeing object pointer"));
       delete event;
+    }
   }
 
   if (thd)
@@ -412,7 +642,8 @@ event_worker_thread(void *arg)
 */
 
 Event_scheduler::Event_scheduler():state(UNINITIALIZED),
-                                   suspended(false), thread_id(0),
+                                   start_scheduler_suspended(false),
+                                   thread_id(0),
                                    mutex_last_locked_at_line(0),
                                    mutex_last_unlocked_at_line(0),
                                    mutex_last_locked_in_func(""),
@@ -477,7 +708,7 @@ Event_scheduler::opt_init()
   DBUG_PRINT("enter", ("this=%p", this));
   
   LOCK_SCHEDULER_DATA();
-  if (state == UNINITIALIZED)
+  if (unlikely(state == UNINITIALIZED))
     ret= init_object_impl();
   UNLOCK_SCHEDULER_DATA();
 
@@ -514,15 +745,6 @@ Event_scheduler::init_object_impl()
       DBUG_RETURN(true);
     }
 
-#ifndef DBUG_OFF
-  if (pthread_mutex_init(&LOCK_executed, MY_MUTEX_INIT_FAST) ||
-      pthread_cond_init(&COND_executed, NULL))  
-  {
-    sql_print_error("SCHEDULER: Unable to initalize mutexes or conditions");
-    DBUG_RETURN(true);
-  }
-#endif
-
   /* init memory root */
   init_alloc_root(&scheduler_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
 
@@ -591,10 +813,6 @@ Event_scheduler::destroy()
     int i;
     for (i= 0; i < COND_LAST; i++)
       pthread_cond_destroy(&cond_vars[i]);
-#ifndef DBUG_OFF
-    pthread_cond_destroy(&COND_executed);
-    pthread_mutex_destroy(&LOCK_executed);
-#endif
     state= UNINITIALIZED;
     break;
   default:
@@ -631,7 +849,7 @@ Event_scheduler::add_event(THD *thd, Eve
   DBUG_PRINT("enter", ("thd=%p et=%p lock=%p",thd,et,&LOCK_scheduler_data));
 
   LOCK_SCHEDULER_DATA();
-  if (state != RUNNING)
+  if (!is_running())
   {
     DBUG_PRINT("info", ("scheduler not running but %d. doing nothing", state));
     UNLOCK_SCHEDULER_DATA();
@@ -680,7 +898,7 @@ Event_scheduler::drop_event(THD *thd, Ev
   DBUG_PRINT("enter", ("thd=%p et=%p lock=%p",thd,et,&LOCK_scheduler_data));
   
   LOCK_SCHEDULER_DATA();
-  if (state != RUNNING)
+  if (!is_running())
   {
     DBUG_PRINT("info", ("scheduler not running but %d. doing nothing", state));
     UNLOCK_SCHEDULER_DATA();
@@ -755,7 +973,7 @@ Event_scheduler::replace_event(THD *thd,
              thd, et, et->dbname.str, et->name.str, &LOCK_scheduler_data));
 
   LOCK_SCHEDULER_DATA();
-  if (state != RUNNING)
+  if (!is_running())
   {
     DBUG_PRINT("info", ("scheduler not running but %d. doing nothing", state));
     UNLOCK_SCHEDULER_DATA();
@@ -907,7 +1125,7 @@ Event_scheduler::drop_matching_events(TH
   DBUG_ENTER("Event_scheduler::drop_matching_events");
   DBUG_PRINT("enter", ("pattern=%*s state=%d", pattern->length, pattern->str,
              state));
-  if (state == RUNNING)
+  if (is_running())
   {
     uint i= 0, dropped= 0;   
     while (i < queue.elements)
@@ -976,7 +1194,7 @@ Event_scheduler::drop_schema_events(THD 
   int ret;
   DBUG_ENTER("Event_scheduler::drop_schema_events");
   LOCK_SCHEDULER_DATA();
-  if (state == RUNNING)
+  if (is_running())
     drop_matching_events(thd, schema, event_timed_db_equal);
 
   ret= db_drop_events_from_table(thd, schema);
@@ -1003,20 +1221,48 @@ extern pthread_attr_t connection_attrib;
 */
 
 enum Event_scheduler::enum_error_code
-Event_scheduler::start(bool acquire_lock)
+Event_scheduler::start()
 {
   enum enum_error_code ret;
-  pthread_t th;
   DBUG_ENTER("Event_scheduler::start");
 
-  if (acquire_lock)
-    LOCK_SCHEDULER_DATA();
+  LOCK_SCHEDULER_DATA();
+  ret= start_no_lock();
+  UNLOCK_SCHEDULER_DATA();
+
+  DBUG_RETURN(ret);
+}
+
+
+/*
+  Starts the event scheduler
+
+  Synopsis
+    Event_scheduler::start()
+      acquire_lock  Whether to acquire a lock or it was externally acquired
+  
+  Note
+    The caller must have acquired LOCK_scheduler_data!
+
+  Returns
+    OP_OK               OK
+    OP_CANTFORK         Cannot create a new thread
+    OP_CANTSTART        ::run() had problem booting
+    OP_ALREADY_RUNNING  Scheduler already running
+*/
+
+enum Event_scheduler::enum_error_code
+Event_scheduler::start_no_lock()
+{
+  enum enum_error_code ret;
+  pthread_t th;
+  DBUG_ENTER("Event_scheduler::start_no_lock");
+
   /* If already working or starting don't make another attempt */
   if (state > INITIALIZED)
   {
     DBUG_PRINT("info", ("scheduler is already running or starting"));
-    ret= OP_ALREADY_RUNNING;
-    goto finish;
+    DBUG_RETURN(OP_ALREADY_RUNNING);
   }
 
   /*
@@ -1035,12 +1281,11 @@ Event_scheduler::start(bool acquire_lock
   {
     DBUG_PRINT("error", ("cannot create a new thread"));
     state= INITIALIZED;
-    ret= OP_CANTFORK;
-    goto finish;
+    DBUG_RETURN(OP_CANTFORK);
   }
 
   /*  Wait till the child thread has booted (w/ or wo success) */
-  while (state != RUNNING && state != CANTSTART)
+  while (!is_running() && state != CANTSTART)
     cond_wait(COND_started, &LOCK_scheduler_data);
 
   /*
@@ -1051,15 +1296,32 @@ Event_scheduler::start(bool acquire_lock
   {
     state= INITIALIZED;
     scheduler_working= FALSE;
-    ret= OP_CANTSTART;
-    goto finish;
+    DBUG_RETURN(OP_CANTSTART);
   }
-  ret= OP_OK;
+  DBUG_RETURN(OP_OK);
+}
 
-finish:
-  if (acquire_lock)
-    UNLOCK_SCHEDULER_DATA();
-  DBUG_RETURN(ret);
+
+/*
+  Starts the event scheduler in suspended mode.
+
+  Synopsis
+    Event_scheduler::start_suspended()
+      acquire_lock  Whether to acquire a lock or it was externally acquired
+
+  Returns
+    OP_OK               OK
+    OP_CANTFORK         Cannot create a new thread
+    OP_CANTSTART        ::run() had problem booting
+    OP_ALREADY_RUNNING  Scheduler already running
+*/
+
+enum Event_scheduler::enum_error_code
+Event_scheduler::start_suspended()
+{
+  DBUG_ENTER("Event_scheduler::start_suspended");
+  start_scheduler_suspended= true;
+  DBUG_RETURN(start());
 }
 
 
@@ -1089,7 +1351,8 @@ Event_scheduler::run(THD *thd)
   if (!ret)
   {
     thread_id= thd->thread_id;
-    state= RUNNING;
+    state= start_scheduler_suspended? SUSPENDED:RUNNING;
+    start_scheduler_suspended= false;
   }
   else 
     state= CANTSTART;
@@ -1103,17 +1366,16 @@ Event_scheduler::run(THD *thd)
   sql_print_information("SCHEDULER: Manager thread started with id %lu",
                         thd->thread_id);
   abstime.tv_nsec= 0;
-  while (state == RUNNING && (!thd->killed || !shutdown_in_progress))
+  while (is_running())
   {
     TIME time_now_utc;
     Event_timed *et;
     my_bool tmp;
     time_t now_utc;
     
-    DBUG_PRINT("info", ("thd->killed=%d shutdown_in_progress=%d",
-               thd->killed, shutdown_in_progress));
     LOCK_SCHEDULER_DATA();
-    check_n_wait_for_non_empty_queue(thd);
+    if (check_n_wait_for_non_empty_queue(thd))
+      continue;
 
     /* On TRUE data is unlocked, go back to the beginning */
     if (check_n_suspend_if_needed(thd))
@@ -1132,7 +1394,6 @@ Event_scheduler::run(THD *thd)
     /* Skip disabled events */
     if (et->status != MYSQL_EVENT_ENABLED)
     {
-      DBUG_ASSERT(0);
       sql_print_error("SCHEDULER: Found a disabled event %*s.%*s in the queue",
                       et->dbname.length, et->dbname.str, et->name.length,
                       et->name.str);
@@ -1144,7 +1405,7 @@ Event_scheduler::run(THD *thd)
     }
     thd->proc_info= (char *)"Computing";
     DBUG_PRINT("evex manager",("computing time to sleep till next exec"));
-    /* This timestamp is in UTC */
+    /* Timestamp is in UTC */
     abstime.tv_sec= sec_since_epoch_TIME(&et->execute_at);
 
     thd->end_time();
@@ -1285,14 +1546,19 @@ Event_scheduler::execute_top(THD *thd)
   DBUG_PRINT("evex manager", ("[%10s] next exec at [%llu]", et->name.str,
              TIME_to_ulonglong_datetime(&et->execute_at)));
 
-  et->update_fields(thd);
-
   /*
     1. For one-time event : year is > 0 and expression is 0
     2. For recurring, expression is != -=> check execute_at_null in this case
   */
   if ((et->execute_at.year && !et->expression) || et->execute_at_null)
-    et->flags |= EVENT_EXEC_NO_MORE;
+  {
+    sql_print_information("SCHEDULER: [%s.%s of %s] no more executions after "
+                          "this one", et->dbname.str, et->name.str,
+                          et->definer.str);
+    et->flags |= EVENT_EXEC_NO_MORE | EVENT_FREE_WHEN_FINISHED;
+  }
+
+  et->update_fields(thd);
 
   if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == MYSQL_EVENT_DISABLED)
     queue_remove(&queue, 0);// 0 is top, internally 1
@@ -1335,11 +1601,6 @@ Event_scheduler::execute_top(THD *thd)
       do {
         pthread_cond_wait(&param.COND_started, &param.LOCK_started);
       } while (!param.started);
-#ifndef DBUG_OFF
-      pthread_mutex_lock(&LOCK_executed);
-      pthread_cond_broadcast(&COND_executed);
-      pthread_mutex_unlock(&LOCK_executed);
-#endif
     }
     /*
       param was allocated on the stack so no explicit delete as well as
@@ -1470,12 +1731,12 @@ Event_scheduler::stop_all_running_events
   DBUG_VOID_RETURN;
 }
 
+
 /*
   Shutdowns the event scheduler
 
   Synopsis
     Event_scheduler::shutdown()
-      acquire_lock  Whether to acquire a lock or it was externally acquired
 
   Returns
     OP_OK           OK
@@ -1484,16 +1745,41 @@ Event_scheduler::stop_all_running_events
 */
 
 enum Event_scheduler::enum_error_code
-Event_scheduler::shutdown(bool acquire_lock)
+Event_scheduler::shutdown()
+{
+  enum enum_error_code ret;
+  DBUG_ENTER("Event_scheduler::shutdown");
+  LOCK_SCHEDULER_DATA();
+  ret= shutdown_no_lock();
+  UNLOCK_SCHEDULER_DATA();
+  DBUG_RETURN(OP_OK);
+}
+
+
+/*
+  Shutdowns the event scheduler
+
+  Synopsis
+    Event_scheduler::shutdown_no_lock()
+
+  Note
+    The caller must have acquited LOCK_scheduler_data.
+
+  Returns
+    OP_OK           OK
+    OP_CANT_KILL    Error during stopping of manager thread
+    OP_NOT_RUNNING  Manager not working
+*/
+
+enum Event_scheduler::enum_error_code
+Event_scheduler::shutdown_no_lock()
 {
   int ret;
   THD *thd= current_thd;
   DBUG_ENTER("Event_scheduler::shutdown");
   DBUG_PRINT("enter", ("thd=%p", current_thd));
 
-  if (acquire_lock)
-    LOCK_SCHEDULER_DATA();
-  if (state != RUNNING)
+  if (!is_running())
   {
     DBUG_PRINT("info", ("manager not running but %d. doing nothing", state));
     UNLOCK_SCHEDULER_DATA();
@@ -1514,6 +1800,8 @@ Event_scheduler::shutdown(bool acquire_l
     for this shutdown procedure but better not.
   */
   pthread_cond_signal(&cond_vars[COND_new_work]);
+  /* Or we are suspended - then we should wake up */
+  pthread_cond_signal(&cond_vars[COND_suspend_or_resume]);
 
   /* Guarantee we don't catch spurious signals */
   sql_print_information("SCHEDULER: Waiting the manager thread to reply");
@@ -1525,8 +1813,6 @@ Event_scheduler::shutdown(bool acquire_l
     cond_wait(COND_shutdown, &LOCK_scheduler_data);
   }
   DBUG_PRINT("info", ("Manager thread has cleaned up. Set state to INIT"));
-  if (acquire_lock)
-    UNLOCK_SCHEDULER_DATA();
 
   DBUG_RETURN(OP_OK);
 }
@@ -1554,7 +1840,8 @@ Event_scheduler::suspend_or_resume(
 
   LOCK_SCHEDULER_DATA();
 
-  if ((action==SUSPEND && suspended) || (action==RESUME && !suspended))
+  if ((action == SUSPEND && state == SUSPENDED) ||
+      (action == RESUME  && state == RUNNING))
   {
     DBUG_PRINT("info", ("Either trying to suspend suspended or resume "
                "running scheduler. Doing nothing."));
@@ -1563,13 +1850,19 @@ Event_scheduler::suspend_or_resume(
   {
     /* Wake the main thread up if he is asleep */
     DBUG_PRINT("info", ("Sending signal"));
-    if ((suspended= (action==SUSPEND)))
+    if (action==SUSPEND)
+    {
+      state= SUSPENDED;
       pthread_cond_signal(&cond_vars[COND_new_work]);
+    }
     else
+    {
+      state= RUNNING;
       pthread_cond_signal(&cond_vars[COND_suspend_or_resume]);
+    }
     DBUG_PRINT("info", ("Waiting on COND_suspend_or_resume"));
     cond_wait(COND_suspend_or_resume, &LOCK_scheduler_data);
-    scheduler_working= (action==RESUME);
+    scheduler_working= (state == RUNNING);
     DBUG_PRINT("info", ("Got response"));
   }
   UNLOCK_SCHEDULER_DATA();
@@ -1581,7 +1874,7 @@ Event_scheduler::suspend_or_resume(
   Starts or stops the scheduler thread.
 
   Synopsis
-    Event_scheduler::start_or_stop()
+    Event_scheduler::start_or_shutdown()
 
   Returns
     FALSE  OK
@@ -1589,11 +1882,15 @@ Event_scheduler::suspend_or_resume(
 */
 
 enum Event_scheduler::enum_error_code
-Event_scheduler::start_or_stop(my_bool new_value)
+Event_scheduler::start_or_shutdown(my_bool new_value)
 {
   enum enum_error_code res;
-  DBUG_ENTER("Event_scheduler::start_or_stop");
+  DBUG_ENTER("Event_scheduler::start_or_shutdown");
 
+  /*
+    We start under a lock so we serialize the setting of the
+    GLOBAL event_scheduler.
+  */
   LOCK_SCHEDULER_DATA();
   if (new_value == scheduler_working)
   {
@@ -1601,14 +1898,12 @@ Event_scheduler::start_or_stop(my_bool n
     UNLOCK_SCHEDULER_DATA();
     DBUG_RETURN(OP_OK);
   }
-  UNLOCK_SCHEDULER_DATA();
-
   /*
     ::start()/::shutdown() will serialize the access. Both methods don't
     allow parallel execution in them.
   */
   DBUG_PRINT("info", ("%s the manager", new_value? "starting":"stopping"));
-  res= new_value? start(false):shutdown(false);
+  res= new_value? start_no_lock():shutdown_no_lock();
   UNLOCK_SCHEDULER_DATA();
   DBUG_RETURN(res);
 }
@@ -1666,10 +1961,10 @@ Event_scheduler::check_n_suspend_if_need
   DBUG_ENTER("Event_scheduler::check_n_suspend_if_needed");
   if (thd->killed && !shutdown_in_progress)
   {
-    suspended= true;
+    state= SUSPENDED;
     thd->killed= THD::NOT_KILLED;
   }
-  if (suspended)
+  if (state == SUSPENDED)
   {
     thd->enter_cond(&cond_vars[COND_suspend_or_resume], &LOCK_scheduler_data,
                     "Suspended");
@@ -1679,11 +1974,11 @@ Event_scheduler::check_n_suspend_if_need
     was_suspended= true;
     scheduler_working= false;
   }
-  while (suspended && state == RUNNING && !thd->killed && !shutdown_in_progress)
+  while (state == SUSPENDED)
   {
     cond_wait(COND_suspend_or_resume, &LOCK_scheduler_data);
     DBUG_PRINT("info", ("Woke up after waiting on COND_suspend_or_resume"));
-    if (!suspended)
+    if (state != SUSPENDED)
     {
       pthread_cond_signal(&cond_vars[COND_suspend_or_resume]);
       sql_print_information("SCHEDULER: Resuming operations");
@@ -1691,6 +1986,28 @@ Event_scheduler::check_n_suspend_if_need
   }
   if (was_suspended)
   {
+    if (queue.elements)
+    {
+      QUEUE queue_tmp;
+      Event_timed *et_tmp;
+      DBUG_PRINT("info", ("We have to recompute the execution times"));
+    
+      init_queue_ex(&queue_tmp, 30, 0 , 0, event_timed_compare_q, NULL, 30);
+      while (queue.elements)
+      {
+        et_tmp= (Event_timed*)queue_element(&queue, 0);
+        queue_remove(&queue, 0);
+        et_tmp->compute_next_execution_time();
+        queue_insert_safe(&queue_tmp,  (byte *) et_tmp);
+      }
+      while (queue_tmp.elements)
+      {
+        et_tmp= (Event_timed*)queue_element(&queue_tmp, 0);
+        queue_remove(&queue_tmp, 0);
+        queue_insert_safe(&queue,  (byte *) et_tmp);      
+      }
+      delete_queue(&queue_tmp);
+    }
     scheduler_working= true;
     /* This will implicitly unlock LOCK_scheduler_data */
     thd->exit_cond("");
@@ -1706,24 +2023,28 @@ Event_scheduler::check_n_suspend_if_need
     Event_scheduler::check_n_wait_for_non_empty_queue()
       thd  Thread
 
+  RETURN
+    FALSE  Did not wait - LOCK_scheduler_data still locked.
+    TRUE   Waited - LOCK_scheduler_data unlocked.
+
   NOTE
     The caller should have locked LOCK_scheduler_data!
 */
 
-void
+bool
 Event_scheduler::check_n_wait_for_non_empty_queue(THD *thd)
 {
   bool slept= false;
   DBUG_ENTER("Event_scheduler::check_n_wait_for_non_empty_queue");
-  DBUG_PRINT("enter", ("q.elements=%lu state=%d suspended=%d thd->killed=%d",
-             queue.elements, state, suspended, scheduler_working));
+  DBUG_PRINT("enter", ("q.elements=%lu state=%s thd->killed=%d",
+             queue.elements, states_names[state], scheduler_working));
   
   if (!queue.elements)
     thd->enter_cond(&cond_vars[COND_new_work], &LOCK_scheduler_data,
                     "Empty queue, sleeping");  
 
   /* Wait in a loop protecting against catching spurious signals */
-  while (!queue.elements && !thd->killed && state == RUNNING && !suspended)
+  while (!queue.elements && state == RUNNING)
   {
     slept= true;
     DBUG_PRINT("info", ("Entering condition because of empty queue"));
@@ -1737,14 +2058,12 @@ Event_scheduler::check_n_wait_for_non_em
     */
   }
   if (slept)
-  {
     thd->exit_cond("");
-    LOCK_SCHEDULER_DATA();
-  }
-  DBUG_PRINT("exit", ("q.elements=%lu state=%d suspended=%d thd->killed=%d",
-             queue.elements, state, suspended, thd->killed));
+
+  DBUG_PRINT("exit", ("q.elements=%lu state=%s thd->killed=%d",
+             queue.elements, states_names[state], thd->killed));
   
-  DBUG_VOID_RETURN;
+  DBUG_RETURN(slept);
 }
 
 
@@ -1809,7 +2128,7 @@ Event_scheduler::cond_wait(enum Event_sc
 {
   int ret;
   DBUG_ENTER("Event_scheduler::cond_wait");
-  DBUG_PRINT("enter", ("cond_wait=%d mutex_wait=%p", cond, mutex));
+  DBUG_PRINT("enter", ("cond=%s mutex=%p", cond_vars_names[cond], mutex));
   ret= pthread_cond_wait(&cond_vars[cond_waiting_on=cond], mutex);
   cond_waiting_on= COND_NONE;
   DBUG_RETURN(ret);
@@ -1817,6 +2136,22 @@ Event_scheduler::cond_wait(enum Event_sc
 
 
 /*
+  Checks whether the scheduler is in a running state
+
+  Synopsis
+    Event_scheduler::is_running()
+      cond   Conditional to wait for
+      mutex  Mutex of the conditional
+*/
+
+inline bool
+Event_scheduler::is_running()
+{
+  return (state == RUNNING || state == SUSPENDED);
+}
+
+
+/*
   Returns the number of elements in the queue
 
   Synopsis
@@ -1833,7 +2168,7 @@ Event_scheduler::events_count()
   int n;
   DBUG_ENTER("Event_scheduler::events_count");
   LOCK_SCHEDULER_DATA();
-  if (state == RUNNING)
+  if (is_running())
     n= queue.elements;
   else
     n= -1;
@@ -2015,7 +2350,7 @@ end:
   if (clean_the_queue)
   {
     for (count= 0; count < queue.elements; ++count)
-      queue_remove(&queue, count);
+      queue_remove(&queue, 0);
     ret= -1;
   }
   else
@@ -2097,17 +2432,6 @@ Event_scheduler::check_system_tables()
 
 
 #ifndef DBUG_OFF
-static
-LEX_STRING states_names[] =
-{
-  {(char*) STRING_WITH_LEN("UNINITIALIZED")},
-  {(char*) STRING_WITH_LEN("INITIALIZED")},
-  {(char*) STRING_WITH_LEN("COMMENCING")},
-  {(char*) STRING_WITH_LEN("CANTSTART")},
-  {(char*) STRING_WITH_LEN("RUNNING")},
-  {(char*) STRING_WITH_LEN("IN_SHUTDOWN")}
-};
-
 
 /*
   Dumps some data about the internal status of the scheduler.
@@ -2144,12 +2468,9 @@ Event_scheduler::dump_internal_status(TH
 
   protocol->prepare_for_resend();
   protocol->store(STRING_WITH_LEN("state"), scs);
-  if (singleton.suspended)
-    protocol->store(STRING_WITH_LEN("SUSPENDED"), scs);  
-  else
-    protocol->store(states_names[singleton.state].str,
-                    states_names[singleton.state].length,
-                    scs);
+  protocol->store(states_names[singleton.state].str,
+                  states_names[singleton.state].length,
+                  scs);
 
   ret= protocol->write();
   /*
@@ -2222,199 +2543,3 @@ Event_scheduler::dump_internal_status(TH
 }
 #endif
 
-
-
-#ifndef DBUG_OFF
-
-#define ERR_W_MSG(cond, msg) \
-{ \
-  sql_print_information("Testing %s", msg); \
-  DBUG_PRINT("info",("Testing %s", msg)); \
-  protocol->prepare_for_resend(); \
-  protocol->store(msg, strlen(msg), system_charset_info); \
-  if (!(cond)){ \
-    protocol->store((char*)STRING_WITH_LEN("ERROR"), system_charset_info); \
-    ret= protocol->write(); \
-    goto err; \
-} else {\
-    protocol->store((char*)STRING_WITH_LEN("OK"), system_charset_info); \
-    ret= protocol->write(); \
-    if (ret) \
-      goto err; \
-  } \
-}
-
-bool
-Event_scheduler::tests_run(THD *thd)
-{
-  Protocol *protocol= thd->protocol;
-  Event_scheduler *scheduler;
-  List<Item> field_list;
-  int ret;
-  struct timespec abstime;
-  int exec_count=0;
-
-  DBUG_ENTER("Event_scheduler::tests_run");
-
-  field_list.push_back(new Item_empty_string("Test", 100));
-  field_list.push_back(new Item_empty_string("Result", 5));
-  if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
-                                           Protocol::SEND_EOF))
-    DBUG_RETURN(true);
-
-  Event_timed et;
-  et.dbname.str= (char *)"test";
-  et.dbname.length= strlen("test");
-  et.name.str= (char *)"event_name";
-  et.name.length= strlen("event_name");
-  et.definer.str= (char *)"root@localhost";
-  et.definer.length= strlen("root@localhost");
-  et.body.str= (char *) "select 1 from dual";
-  et.body.length= strlen("select 1 from dual");
-  et.expression= 3;
-  et.interval= INTERVAL_SECOND;
-  et.ends_null= true;
-
-  my_tz_UTC->gmt_sec_to_TIME(&et.starts, time(NULL) + 10);
-  Event_timed et2;
-  et2.dbname.str= (char *)"test";
-  et2.dbname.length= strlen("test");
-  et2.name.str= (char *)"event_name2";
-  et2.name.length= strlen("event_name2");
-  et2.definer.str= (char *)"root@localhost";
-  et2.definer.length= strlen("root@localhost");
-  et2.body.str= (char *) "select 2 from dual";
-  et2.body.length= strlen("select 2 from dual");
-  et2.expression= 5;
-  et2.interval= INTERVAL_SECOND;
-  et2.ends_null= true;
-  my_tz_UTC->gmt_sec_to_TIME(&et2.starts, time(NULL) + 15);
-
-  uint tmp;
-
-  
-  scheduler= Event_scheduler::get_instance();
-  ERR_W_MSG(scheduler, "Create new or get an old scheduler");
-
-  /* Force new scheduler instantiation */
-  ERR_W_MSG(!Event_scheduler::destroy_singleton(), "Destroy old scheduler");
-
-  scheduler= Event_scheduler::get_instance();
-  ERR_W_MSG(scheduler, "Create new scheduler");
-
-  ERR_W_MSG((scheduler->state == Event_scheduler::INITIALIZED),
-                  "Test scheduler state ");
-
-  ERR_W_MSG(scheduler->events_count() == -1, "Test count");
-
-  ERR_W_MSG(!db_create_event(thd, &et, false, &tmp),
-                  "Create event on disk");
-
-  ERR_W_MSG(!scheduler->start(true), "Start scheduler");
-
-  scheduler->add_event(thd, &et, false);
-  ERR_W_MSG(scheduler->events_count() == 2, "Test count wo check");
-  
-  scheduler->add_event(thd, &et, true);
-  ERR_W_MSG(scheduler->events_count() == 2, "Test count with check");
-
-  scheduler->add_event(thd, &et, false);
-  ERR_W_MSG(scheduler->events_count() == 3, "Test count wo check");
-
-
-  scheduler->add_event(thd, &et2, true);
-  ERR_W_MSG(scheduler->events_count() == 3, "Test count after add of et2");
-
-  ERR_W_MSG(!db_create_event(thd, &et2, false, &tmp),
-                  "Create event et2 on disk");
-
-  scheduler->add_event(thd, &et2, true);
-  ERR_W_MSG(scheduler->events_count() == 4,
-                  "Test count after add of et2 (again)");
-
-  scheduler->drop_event(thd, &et);
-  ERR_W_MSG(scheduler->events_count() == 3, "Test delete 1");
-
-  scheduler->drop_event(thd, &et);
-  ERR_W_MSG(scheduler->events_count() == 2, "Test delete 2");
-
-  scheduler->drop_event(thd, &et);
-  ERR_W_MSG(scheduler->events_count() == 1, "Test delete 3");
-
-  et2.body.str= (char *) "select 212 from dual";
-  et2.body.length= strlen("select 212 from dual");
-
-  scheduler->replace_event(thd, &et2, NULL, NULL);
-  ERR_W_MSG(scheduler->events_count() == 1, "Test delete 4");
-
-  /*
-    Here we will get an empty line because kill_one_thread() which is
-    used by ::shutdown() calls send_eof().
-  */
-  ERR_W_MSG(!scheduler->shutdown(true), "Shutdown the scheduler");
-
-  ERR_W_MSG(!et.drop(thd), "et.drop()");
-  ERR_W_MSG(!et2.drop(thd), "et2.drop()");
-
-
-  /* New test starting */
-
-
-  ERR_W_MSG(!scheduler->start(true), "Start scheduler again and sleep 2s");
-  ERR_W_MSG(scheduler->start(true)==
-              Event_scheduler::OP_ALREADY_RUNNING,
-            "Try to start again");
-
-  pthread_mutex_lock(&scheduler->LOCK_executed);
-
-  ERR_W_MSG(!db_create_event(thd, &et, false, &tmp), "Disk Create event et1");
-  ERR_W_MSG(!db_create_event(thd, &et2, false, &tmp),"Disk Create event et2");
-
-  ERR_W_MSG(!scheduler->add_event(thd, &et, true),  "Add et to the queue, step 1");
-  ERR_W_MSG(scheduler->events_count() == 1, "Add et to queue, step 2");
-
-  ERR_W_MSG(!scheduler->add_event(thd, &et2, true),  "Add et2 to the queue, step 1");
-  ERR_W_MSG(scheduler->events_count() == 2, "Add et2 to the queue, step 2");
-
-  /* now there should be 2 events */
-  
-  /* 16 sec test */
-  abstime.tv_nsec= 0;
-  abstime.tv_sec= time(NULL) + 13;
-  while (ETIMEDOUT != pthread_cond_timedwait(&scheduler->COND_executed,
-                                             &scheduler->LOCK_executed,
-                                             &abstime))
-  {
-    exec_count++;
-    sql_print_information("Exec_count=%d", exec_count);
-  }
-
-  pthread_mutex_unlock(&scheduler->LOCK_executed);
-  sql_print_information("Exec_count=%d", exec_count);
-  ERR_W_MSG(exec_count == 8, "Execution count");
-  /*
-    Here we will get an empty line because kill_one_thread() which is
-    used by ::shutdown() calls send_eof().
-  */
-  ERR_W_MSG(!scheduler->shutdown(true), "Shutdown scheduler");
-  ERR_W_MSG(scheduler->shutdown(true)==Event_scheduler::OP_NOT_RUNNING,
-            "Shutdown scheduler again for checking");
-
-  ERR_W_MSG(!Event_scheduler::destroy_singleton(), "Destroy new scheduler");
-
-  ERR_W_MSG(!et.drop(thd), "et.drop()");
-
-  ERR_W_MSG(!et2.drop(thd), "et2.drop()");
-
-  ERR_W_MSG(TRUE, "All Tests");
-
-  send_eof(thd);
-  DBUG_RETURN(false);
-err:
-  scheduler->shutdown(true);
-  Event_scheduler::destroy_singleton();
-  send_eof(thd);
-  DBUG_RETURN(true);
-}
-
-#endif

--- 1.1/sql/event_scheduler.h	2006-05-11 16:31:49 +02:00
+++ 1.2/sql/event_scheduler.h	2006-05-15 23:10:20 +02:00
@@ -20,13 +20,6 @@
 class THD;
 typedef bool * (*event_timed_identifier_comparator)(Event_timed*, Event_timed*);
 
-/*
-  Locking hierarchy:
-  ->LOCK_executed (used for counting of forks together with COND_executed)
-
-  ->LOCK_scheduler_data (protects `state`)
-  -->LOCK_last_worker_exit (for COND_last_worker_exit)
-*/
 
 class Event_scheduler
 {
@@ -88,19 +81,23 @@ public:
   /* Status changing methods follow */
 
   enum enum_error_code
-  start(bool acquire_lock);
+  start();
 
-  bool
-  run(THD *thd);
+  enum enum_error_code
+  shutdown();
 
   enum enum_error_code
-  shutdown(bool acquire_lock);
+  start_or_shutdown(my_bool new_value);
 
   enum enum_error_code
-  suspend_or_resume(enum enum_suspend_or_resume action);
+  start_suspended();
+
+  bool
+  run(THD *thd);
 
   enum enum_error_code
-  start_or_stop(my_bool new_value);
+  suspend_or_resume(enum enum_suspend_or_resume action);
+
 
   /* Call this only at the end of the server. */
   static bool
@@ -110,14 +107,8 @@ public:
   register_worker_thread(ulong thd_id);
 
 #ifndef DBUG_OFF
-  static bool
-  tests_run(THD *thd);
-
   static int
   dump_internal_status(THD *thd);
-
-  pthread_cond_t COND_executed;
-  pthread_mutex_t LOCK_executed;
 #endif
 
 protected:
@@ -130,6 +121,12 @@ protected:
   bool 
   init_object_impl();
 
+  enum enum_error_code
+  start_no_lock();
+
+  enum enum_error_code
+  shutdown_no_lock();
+
   /*
     LOCKING PROTOCOL: Does not do any locking. The caller is responsible to
                       lock and signal.
@@ -140,6 +137,9 @@ protected:
   uint
   workers_count();
 
+  bool
+  is_running();
+
   /* helper functions */
   bool
   execute_top(THD *thd);
@@ -169,42 +169,16 @@ protected:
   bool
   check_n_suspend_if_needed(THD *thd);
 
-  void
+  bool
   check_n_wait_for_non_empty_queue(THD *thd);
 
 
 protected:
-  /* Singleton is used */
+  /* Singleton DP is used */
   Event_scheduler();
 
-  /*
-    LOCK PROCOTOL: Expects that this method is synchronized
-  */
   ~Event_scheduler();
 
-  /*
-    UNINITIALIZED -> INITIALIZED -> COMMENCING -> RUNNING -> IN_SHUTDOWN
-                       ^ ^               |                    |
-                       | |- CANTSTART <--|                    |
-                       |______________________________________|
-
-    The life cycle of a scheduler
-    - UNINITIALIZED :The object is created and only the mutex is initialized
-    - INITIALIZED   :All member variables are initialized
-    - COMMENCING    :The scheduler is starting, no other attempt to start 
-                     should succeed before the state is back to INITIALIZED.
-    - CANTSTART     :Set by the ::run() method in case it can't start for some
-                     reason. In this case the connection thread that tries to
-                     start the scheduler sees that some error has occurred and
-                     returns an error to the user. Finally, the connection
-                     thread sets the state to INITIALIZED, so further attempts
-                     to start the scheduler could be made.
-    - RUNNING       :The scheduler is running. New events could be added,
-                     dropped, altered. The scheduler could be stopped.
-    - IN_SHUTDOWN   :The scheduler is shutting down, due to request by setting
-                     the global event_scheduler to 0/false, or because of a
-                     KILL command sent by a user to the master thread.
-  */
   enum enum_state
   {
     UNINITIALIZED= 0,
@@ -212,6 +186,7 @@ protected:
     COMMENCING,
     CANTSTART,
     RUNNING,
+    SUSPENDED,
     IN_SHUTDOWN
   };
   
@@ -241,25 +216,6 @@ protected:
     */
     COND_shutdown,
     /*
-      COND_last_worker_exit is a conditional signaled by a worker thread when
-      the workers_count reaches 0. This notifies the manager thread that it
-      should continue with the clean up.
-    */
-    COND_last_worker_exit,
-    /*
-      COND_bulk_drop_finished is a conditional signaled by when bulk drop of
-      events in drop_matching_events() is finished. The conditional is used by
-      wait_for_bulk_drop_finished() in every method that modifies the queue.
-      In this case drop_matching_events runs without being protected by
-      LOCK_scheduler_data. The sole reason is that bulk drop stops running events.
-      When deregistering, in deregister_worker_thread(), LOCK_scheduler_data is
-      acquired. If we hold LOCK_scheduler_data during bulk drop we have a deadlock.
-      See how ::replace_event() and ::drop_event() handle this situation -> they
-      don't hold LOCK_scheduler_data when they kill the running thread, if any,
-      by this purpose.
-    */
-    COND_bulk_drop_finished,
-    /*
       Conditional used for signalling from the scheduler thread back to the
       thread that calls ::suspend() or ::resume. Synchronizing the calls.
     */
@@ -274,8 +230,8 @@ protected:
   /* This is the current status of the life-cycle of the manager. */
   enum enum_state state;
 
-  /* Set to suspend the scheduler. */
-  bool suspended;
+  /* Set to start the scheduler in suspended state */
+  bool start_scheduler_suspended;
 
   /*
     LOCK_scheduler_data is the mutex which protects the access to the

--- 1.52/sql/event_timed.cc	2006-05-11 17:08:29 +02:00
+++ 1.53/sql/event_timed.cc	2006-05-15 23:10:20 +02:00
@@ -874,6 +874,7 @@ Event_timed::compute_next_execution_time
     }
     goto ret;
   }
+  current_thd->end_time();
   my_tz_UTC->gmt_sec_to_TIME(&time_now, current_thd->query_start());
 
   DBUG_PRINT("info",("NOW=[%llu]", TIME_to_ulonglong_datetime(&time_now)));
@@ -946,12 +947,15 @@ Event_timed::compute_next_execution_time
       /* There was previous execution */
       if (my_time_compare(&ends, &next_exec) == -1)
       {
-        DBUG_PRINT("info", ("Next execution after ENDS. Stop executing."));
+        DBUG_PRINT("info", ("Next execution of %s after ENDS. Stop executing.",
+                   name.str));
         /* Next execution after ends. No more executions */
         set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
         execute_at_null= TRUE;
         if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
           dropped= true;
+        status= MYSQL_EVENT_DISABLED;
+        status_changed= true;
       }
       else
       {
@@ -1042,6 +1046,8 @@ Event_timed::compute_next_execution_time
           DBUG_PRINT("info", ("Next execution after ENDS. Stop executing."));
           set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
           execute_at_null= TRUE;
+          status= MYSQL_EVENT_DISABLED;
+          status_changed= true;
           if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
             dropped= true;
         }
@@ -1083,9 +1089,6 @@ Event_timed::mark_last_executed(THD *thd
   my_tz_UTC->gmt_sec_to_TIME(&time_now, (my_time_t) thd->query_start());
 
   last_executed= time_now; /* was execute_at */
-#ifdef ANDREY_0
-  last_executed= execute_at;
-#endif
   last_executed_changed= true;
 }
 
@@ -1623,6 +1626,8 @@ Event_timed::spawn_thread_finish(THD *th
   in_spawned_thread= false;
   DBUG_PRINT("info", ("Sending COND_finished for thread %d", thread_id));
   thread_id= 0;
+  if (dropped)
+    drop(thd);
   pthread_cond_signal(&COND_finished);
   VOID(pthread_mutex_unlock(&LOCK_running));
   DBUG_VOID_RETURN;
Thread
bk commit into 5.1 tree (andrey:1.2398) BUG#17619ahristov15 May