WL#3337 (Event scheduler new architecture)
This is the first cut of separating Event_scheduler in two classes which are more specialized. Inheritance was used to separate methods and member variables. Still Event_scheduler is a child of Event_queue. This dependency will be removed soon. sql/event_data_objects.cc: add comments sql/event_db_repository.cc: coding style sql/event_db_repository.h: add a call, will be implemented later sql/event_queue.cc: Event_queue, still as super-class of Event_scheduler sql/event_queue.h: Event_queue as super-class of Event_scheduler. Trying to separate the two classes sql/event_scheduler.cc: Event_scheduler as child class of Event_queue. Trying to separate both classes. sql/event_scheduler.h: Event_scheduler as child class of Event_queue. Trying to separate both classes. sql/events.cc: Don't allocate on the stack the scheduler but on the heap. The exact way it is done will be changed, that's ok for now.
This commit is contained in:
parent
8d961c45e2
commit
377446fa34
@ -25,6 +25,19 @@
|
|||||||
#define EVEX_MAX_INTERVAL_VALUE 2147483647L
|
#define EVEX_MAX_INTERVAL_VALUE 2147483647L
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Returns a new instance
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_parse_data::new_instance()
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
Address or NULL in case of error
|
||||||
|
|
||||||
|
NOTE
|
||||||
|
Created on THD's mem_root
|
||||||
|
*/
|
||||||
|
|
||||||
Event_parse_data *
|
Event_parse_data *
|
||||||
Event_parse_data::new_instance(THD *thd)
|
Event_parse_data::new_instance(THD *thd)
|
||||||
{
|
{
|
||||||
@ -32,6 +45,13 @@ Event_parse_data::new_instance(THD *thd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Constructor
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_parse_data::Event_parse_data()
|
||||||
|
*/
|
||||||
|
|
||||||
Event_parse_data::Event_parse_data()
|
Event_parse_data::Event_parse_data()
|
||||||
{
|
{
|
||||||
item_execute_at= item_expression= item_starts= item_ends= NULL;
|
item_execute_at= item_expression= item_starts= item_ends= NULL;
|
||||||
|
@ -1297,3 +1297,4 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname, LEX_STRING na
|
|||||||
|
|
||||||
DBUG_RETURN(OP_OK);
|
DBUG_RETURN(OP_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +113,9 @@ private:
|
|||||||
int
|
int
|
||||||
table_scan_all_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table);
|
table_scan_all_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table);
|
||||||
|
|
||||||
|
static bool
|
||||||
|
check_system_tables(THD *thd);
|
||||||
|
|
||||||
MEM_ROOT repo_root;
|
MEM_ROOT repo_root;
|
||||||
|
|
||||||
/* Prevent use of these */
|
/* Prevent use of these */
|
||||||
|
@ -15,5 +15,846 @@
|
|||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
#include "mysql_priv.h"
|
#include "mysql_priv.h"
|
||||||
|
#include "events.h"
|
||||||
|
#include "event_scheduler.h"
|
||||||
#include "event_queue.h"
|
#include "event_queue.h"
|
||||||
#include "event_data_objects.h"
|
#include "event_data_objects.h"
|
||||||
|
#include "event_db_repository.h"
|
||||||
|
#include "sp_head.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#if __GNUC__ >= 2
|
||||||
|
#define SCHED_FUNC __FUNCTION__
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define SCHED_FUNC "<unknown>"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LOCK_QUEUE_DATA() lock_data(SCHED_FUNC, __LINE__)
|
||||||
|
#define UNLOCK_QUEUE_DATA() unlock_data(SCHED_FUNC, __LINE__)
|
||||||
|
|
||||||
|
|
||||||
|
Event_scheduler*
|
||||||
|
Event_queue::singleton= NULL;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Compares the execute_at members of 2 Event_timed instances.
|
||||||
|
Used as callback for the prioritized queue when shifting
|
||||||
|
elements inside.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
event_timed_compare_q()
|
||||||
|
|
||||||
|
vptr - not used (set it to NULL)
|
||||||
|
a - first Event_timed object
|
||||||
|
b - second Event_timed object
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
-1 - a->execute_at < b->execute_at
|
||||||
|
0 - a->execute_at == b->execute_at
|
||||||
|
1 - a->execute_at > b->execute_at
|
||||||
|
|
||||||
|
NOTES
|
||||||
|
execute_at.second_part is not considered during comparison
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
event_timed_compare_q(void *vptr, byte* a, byte *b)
|
||||||
|
{
|
||||||
|
return my_time_compare(&((Event_timed *)a)->execute_at,
|
||||||
|
&((Event_timed *)b)->execute_at);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Constructor of class Event_queue.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_queue::Event_queue()
|
||||||
|
*/
|
||||||
|
|
||||||
|
Event_queue::Event_queue()
|
||||||
|
{
|
||||||
|
mutex_last_unlocked_at_line= mutex_last_locked_at_line= 0;
|
||||||
|
mutex_last_unlocked_in_func= mutex_last_locked_in_func= "";
|
||||||
|
mutex_queue_data_locked= FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Creates an event in the scheduler queue
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_queue::create_event()
|
||||||
|
et The event to add
|
||||||
|
check_existence Whether to check if already loaded.
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
OP_OK OK or scheduler not working
|
||||||
|
OP_LOAD_ERROR Error during loading from disk
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
Event_queue::create_event(THD *thd, Event_parse_data *et, bool check_existence)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
Event_timed *et_new;
|
||||||
|
DBUG_ENTER("Event_queue::create_event");
|
||||||
|
DBUG_PRINT("enter", ("thd=%p et=%p lock=%p",thd,et, &LOCK_event_queue));
|
||||||
|
|
||||||
|
LOCK_QUEUE_DATA();
|
||||||
|
if (check_existence && find_event(et->dbname, et->name, FALSE))
|
||||||
|
{
|
||||||
|
res= OP_ALREADY_EXISTS;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We need to load the event on scheduler_root */
|
||||||
|
if (!(res= db_repository->
|
||||||
|
load_named_event(thd, et->dbname, et->name, &et_new)))
|
||||||
|
{
|
||||||
|
queue_insert_safe(&queue, (byte *) et_new);
|
||||||
|
on_queue_change();
|
||||||
|
}
|
||||||
|
else if (res == OP_DISABLED_EVENT)
|
||||||
|
res= OP_OK;
|
||||||
|
end:
|
||||||
|
UNLOCK_QUEUE_DATA();
|
||||||
|
DBUG_RETURN(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Updates an event from the scheduler queue
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_scheduler::update_event()
|
||||||
|
thd Thread
|
||||||
|
et The event to replace(add) into the queue
|
||||||
|
new_schema New schema
|
||||||
|
new_name New name
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
OP_OK OK or scheduler not working
|
||||||
|
OP_LOAD_ERROR Error during loading from disk
|
||||||
|
OP_ALREADY_EXISTS Event already in the queue
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
Event_queue::update_event(THD *thd, Event_parse_data *et,
|
||||||
|
LEX_STRING *new_schema,
|
||||||
|
LEX_STRING *new_name)
|
||||||
|
{
|
||||||
|
int res= OP_OK;
|
||||||
|
Event_timed *et_old, *et_new= NULL;
|
||||||
|
LEX_STRING old_schema, old_name;
|
||||||
|
|
||||||
|
LINT_INIT(old_schema.str);
|
||||||
|
LINT_INIT(old_schema.length);
|
||||||
|
LINT_INIT(old_name.str);
|
||||||
|
LINT_INIT(old_name.length);
|
||||||
|
|
||||||
|
DBUG_ENTER("Event_queue::update_event");
|
||||||
|
DBUG_PRINT("enter", ("thd=%p et=%p et=[%s.%s] lock=%p",
|
||||||
|
thd, et, et->dbname.str, et->name.str, &LOCK_event_queue));
|
||||||
|
|
||||||
|
LOCK_QUEUE_DATA();
|
||||||
|
if (!(et_old= find_event(et->dbname, et->name, TRUE)))
|
||||||
|
DBUG_PRINT("info", ("%s.%s not found cached, probably was DISABLED",
|
||||||
|
et->dbname.str, et->name.str));
|
||||||
|
|
||||||
|
if (new_schema && new_name)
|
||||||
|
{
|
||||||
|
old_schema= et->dbname;
|
||||||
|
old_name= et->name;
|
||||||
|
et->dbname= *new_schema;
|
||||||
|
et->name= *new_name;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
We need to load the event (it's strings but on the object itself)
|
||||||
|
on scheduler_root. et_new could be NULL :
|
||||||
|
1. Error occured
|
||||||
|
2. If the replace is DISABLED, we don't load it into the queue.
|
||||||
|
*/
|
||||||
|
if (!(res= db_repository->
|
||||||
|
load_named_event(thd, et->dbname, et->name, &et_new)))
|
||||||
|
{
|
||||||
|
queue_insert_safe(&queue, (byte *) et_new);
|
||||||
|
on_queue_change();
|
||||||
|
}
|
||||||
|
else if (res == OP_DISABLED_EVENT)
|
||||||
|
res= OP_OK;
|
||||||
|
|
||||||
|
if (new_schema && new_name)
|
||||||
|
{
|
||||||
|
et->dbname= old_schema;
|
||||||
|
et->name= old_name;
|
||||||
|
}
|
||||||
|
DBUG_PRINT("info", ("res=%d", res));
|
||||||
|
UNLOCK_QUEUE_DATA();
|
||||||
|
/*
|
||||||
|
Andrey: Is this comment still truthful ???
|
||||||
|
|
||||||
|
We don't move this code above because a potential kill_thread will call
|
||||||
|
THD::awake(). Which in turn will try to acqure mysys_var->current_mutex,
|
||||||
|
which is LOCK_event_queue on which the COND_new_work in ::run() locks.
|
||||||
|
Hence, we try to acquire a lock which we have already acquired and we run
|
||||||
|
into an assert. Holding LOCK_event_queue however is not needed because
|
||||||
|
we don't touch any invariant of the scheduler anymore. ::drop_event() does
|
||||||
|
the same.
|
||||||
|
*/
|
||||||
|
if (et_old)
|
||||||
|
{
|
||||||
|
switch (et_old->kill_thread(thd)) {
|
||||||
|
case EVEX_CANT_KILL:
|
||||||
|
/* Don't delete but continue */
|
||||||
|
et_old->flags |= EVENT_FREE_WHEN_FINISHED;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
/*
|
||||||
|
kill_thread() waits till the spawned thread finishes after it's
|
||||||
|
killed. Hence, we delete here memory which is no more referenced from
|
||||||
|
a running thread.
|
||||||
|
*/
|
||||||
|
delete et_old;
|
||||||
|
/*
|
||||||
|
We don't signal COND_new_work here because:
|
||||||
|
1. Even if the dropped event is on top of the queue this will not
|
||||||
|
move another one to be executed before the time the one on the
|
||||||
|
top (but could be at the same second as the dropped one)
|
||||||
|
2. If this was the last event on the queue, then pthread_cond_timedwait
|
||||||
|
in ::run() will finish and then see that the queue is empty and
|
||||||
|
call cond_wait(). Hence, no need to interrupt the blocked
|
||||||
|
::run() thread.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DBUG_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DBUG_RETURN(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Drops an event from the scheduler queue
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_queue::drop_event()
|
||||||
|
thd Thread
|
||||||
|
name The event to drop
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
FALSE OK (replaced or scheduler not working)
|
||||||
|
TRUE Failure
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool
|
||||||
|
Event_queue::drop_event(THD *thd, sp_name *name)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
Event_timed *et_old;
|
||||||
|
DBUG_ENTER("Event_queue::drop_event");
|
||||||
|
DBUG_PRINT("enter", ("thd=%p name=%p lock=%p", thd, name,
|
||||||
|
&LOCK_event_queue));
|
||||||
|
|
||||||
|
LOCK_QUEUE_DATA();
|
||||||
|
if (!(et_old= find_event(name->m_db, name->m_name, TRUE)))
|
||||||
|
DBUG_PRINT("info", ("No such event found, probably DISABLED"));
|
||||||
|
|
||||||
|
UNLOCK_QUEUE_DATA();
|
||||||
|
|
||||||
|
/* See comments in ::replace_event() why this is split in two parts. */
|
||||||
|
if (et_old)
|
||||||
|
{
|
||||||
|
switch ((res= et_old->kill_thread(thd))) {
|
||||||
|
case EVEX_CANT_KILL:
|
||||||
|
/* Don't delete but continue */
|
||||||
|
et_old->flags |= EVENT_FREE_WHEN_FINISHED;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
/*
|
||||||
|
kill_thread() waits till the spawned thread finishes after it's
|
||||||
|
killed. Hence, we delete here memory which is no more referenced from
|
||||||
|
a running thread.
|
||||||
|
*/
|
||||||
|
delete et_old;
|
||||||
|
/*
|
||||||
|
We don't signal COND_new_work here because:
|
||||||
|
1. Even if the dropped event is on top of the queue this will not
|
||||||
|
move another one to be executed before the time the one on the
|
||||||
|
top (but could be at the same second as the dropped one)
|
||||||
|
2. If this was the last event on the queue, then pthread_cond_timedwait
|
||||||
|
in ::run() will finish and then see that the queue is empty and
|
||||||
|
call cond_wait(). Hence, no need to interrupt the blocked
|
||||||
|
::run() thread.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sql_print_error("SCHEDULER: Got unexpected error %d", res);
|
||||||
|
DBUG_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DBUG_RETURN(FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Searches for an event in the scheduler queue
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_queue::find_event()
|
||||||
|
db The schema of the event to find
|
||||||
|
name The event to find
|
||||||
|
remove_from_q If found whether to remove from the Q
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
NULL Not found
|
||||||
|
otherwise Address
|
||||||
|
|
||||||
|
NOTE
|
||||||
|
The caller should do the locking also the caller is responsible for
|
||||||
|
actual signalling in case an event is removed from the queue
|
||||||
|
(signalling COND_new_work for instance).
|
||||||
|
*/
|
||||||
|
|
||||||
|
Event_timed *
|
||||||
|
Event_queue::find_event(LEX_STRING db, LEX_STRING name, bool remove_from_q)
|
||||||
|
{
|
||||||
|
uint i;
|
||||||
|
DBUG_ENTER("Event_queue::find_event");
|
||||||
|
|
||||||
|
for (i= 0; i < queue.elements; ++i)
|
||||||
|
{
|
||||||
|
Event_timed *et= (Event_timed *) queue_element(&queue, i);
|
||||||
|
DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?", db.str, name.str,
|
||||||
|
et->dbname.str, et->name.str));
|
||||||
|
if (event_timed_identifier_equal(db, name, et))
|
||||||
|
{
|
||||||
|
if (remove_from_q)
|
||||||
|
queue_remove(&queue, i);
|
||||||
|
DBUG_RETURN(et);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DBUG_RETURN(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Drops all events from the in-memory queue and disk that match
|
||||||
|
certain pattern evaluated by a comparator function
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_queue::drop_matching_events()
|
||||||
|
thd THD
|
||||||
|
pattern A pattern string
|
||||||
|
comparator The function to use for comparing
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
-1 Scheduler not working
|
||||||
|
>=0 Number of dropped events
|
||||||
|
|
||||||
|
NOTE
|
||||||
|
Expected is the caller to acquire lock on LOCK_event_queue
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
Event_queue::drop_matching_events(THD *thd, LEX_STRING pattern,
|
||||||
|
bool (*comparator)(Event_timed *,LEX_STRING *))
|
||||||
|
{
|
||||||
|
DBUG_ENTER("Event_queue::drop_matching_events");
|
||||||
|
DBUG_PRINT("enter", ("pattern=%*s state=%d", pattern.length, pattern.str));
|
||||||
|
|
||||||
|
uint i= 0, dropped= 0;
|
||||||
|
while (i < queue.elements)
|
||||||
|
{
|
||||||
|
Event_timed *et= (Event_timed *) queue_element(&queue, i);
|
||||||
|
DBUG_PRINT("info", ("[%s.%s]?", et->dbname.str, et->name.str));
|
||||||
|
if (comparator(et, &pattern))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
The queue is ordered. If we remove an element, then all elements after
|
||||||
|
it will shift one position to the left, if we imagine it as an array
|
||||||
|
from left to the right. In this case we should not increment the
|
||||||
|
counter and the (i < queue.elements) condition is ok.
|
||||||
|
*/
|
||||||
|
queue_remove(&queue, i);
|
||||||
|
|
||||||
|
/* See replace_event() */
|
||||||
|
switch (et->kill_thread(thd)) {
|
||||||
|
case EVEX_CANT_KILL:
|
||||||
|
/* Don't delete but continue */
|
||||||
|
et->flags |= EVENT_FREE_WHEN_FINISHED;
|
||||||
|
++dropped;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
delete et;
|
||||||
|
++dropped;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DBUG_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
DBUG_PRINT("info", ("Dropped %lu", dropped));
|
||||||
|
/*
|
||||||
|
Don't send COND_new_work because no need to wake up the scheduler thread.
|
||||||
|
When it wakes next time up it will recalculate how much more it should
|
||||||
|
sleep if the top of the queue has been changed by this method.
|
||||||
|
*/
|
||||||
|
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Drops all events from the in-memory queue and disk that are from
|
||||||
|
certain schema.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_queue::drop_schema_events()
|
||||||
|
thd THD
|
||||||
|
db The schema name
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
-1 Scheduler not working
|
||||||
|
>=0 Number of dropped events
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
Event_queue::drop_schema_events(THD *thd, LEX_STRING schema)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
DBUG_ENTER("Event_queue::drop_schema_events");
|
||||||
|
LOCK_QUEUE_DATA();
|
||||||
|
drop_matching_events(thd, schema, event_timed_db_equal);
|
||||||
|
UNLOCK_QUEUE_DATA();
|
||||||
|
|
||||||
|
DBUG_RETURN(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Wrapper for pthread_mutex_lock
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_queue::lock_data()
|
||||||
|
mutex Mutex to lock
|
||||||
|
line The line number on which the lock is done
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
Error code of pthread_mutex_lock()
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
Event_queue::lock_data(const char *func, uint line)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("Event_queue::lock_mutex");
|
||||||
|
DBUG_PRINT("enter", ("mutex_lock=%p func=%s line=%u",
|
||||||
|
&LOCK_event_queue, func, line));
|
||||||
|
pthread_mutex_lock(&LOCK_event_queue);
|
||||||
|
mutex_last_locked_in_func= func;
|
||||||
|
mutex_last_locked_at_line= line;
|
||||||
|
mutex_queue_data_locked= TRUE;
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Wrapper for pthread_mutex_unlock
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_queue::unlock_data()
|
||||||
|
mutex Mutex to unlock
|
||||||
|
line The line number on which the unlock is done
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
Event_queue::unlock_data(const char *func, uint line)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("Event_queue::UNLOCK_mutex");
|
||||||
|
DBUG_PRINT("enter", ("mutex_unlock=%p func=%s line=%u",
|
||||||
|
&LOCK_event_queue, func, line));
|
||||||
|
mutex_last_unlocked_at_line= line;
|
||||||
|
mutex_queue_data_locked= FALSE;
|
||||||
|
mutex_last_unlocked_in_func= func;
|
||||||
|
pthread_mutex_unlock(&LOCK_event_queue);
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Returns the number of elements in the queue
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_queue::events_count()
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
0 Number of Event_timed objects in the queue
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint
|
||||||
|
Event_queue::events_count()
|
||||||
|
{
|
||||||
|
uint n;
|
||||||
|
DBUG_ENTER("Event_scheduler::events_count");
|
||||||
|
LOCK_QUEUE_DATA();
|
||||||
|
n= queue.elements;
|
||||||
|
UNLOCK_QUEUE_DATA();
|
||||||
|
|
||||||
|
DBUG_RETURN(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Returns the number of elements in the queue
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_queue::events_count_no_lock()
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
0 Number of Event_timed objects in the queue
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint
|
||||||
|
Event_queue::events_count_no_lock()
|
||||||
|
{
|
||||||
|
uint n;
|
||||||
|
DBUG_ENTER("Event_scheduler::events_count_no_lock");
|
||||||
|
|
||||||
|
n= queue.elements;
|
||||||
|
|
||||||
|
DBUG_RETURN(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Loads all ENABLED events from mysql.event into the prioritized
|
||||||
|
queue. Called during scheduler main thread initialization. Compiles
|
||||||
|
the events. Creates Event_timed instances for every ENABLED event
|
||||||
|
from mysql.event.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_queue::load_events_from_db()
|
||||||
|
thd - Thread context. Used for memory allocation in some cases.
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
0 OK
|
||||||
|
!0 Error (EVEX_OPEN_TABLE_FAILED, EVEX_MICROSECOND_UNSUP,
|
||||||
|
EVEX_COMPILE_ERROR) - in all these cases mysql.event was
|
||||||
|
tampered.
|
||||||
|
|
||||||
|
NOTES
|
||||||
|
Reports the error to the console
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
Event_queue::load_events_from_db(THD *thd)
|
||||||
|
{
|
||||||
|
TABLE *table;
|
||||||
|
READ_RECORD read_record_info;
|
||||||
|
int ret= -1;
|
||||||
|
uint count= 0;
|
||||||
|
bool clean_the_queue= FALSE;
|
||||||
|
/* Compile the events on this root but only for syntax check, then discard */
|
||||||
|
MEM_ROOT boot_root;
|
||||||
|
|
||||||
|
DBUG_ENTER("Event_queue::load_events_from_db");
|
||||||
|
DBUG_PRINT("enter", ("thd=%p", thd));
|
||||||
|
|
||||||
|
if ((ret= db_repository->open_event_table(thd, TL_READ, &table)))
|
||||||
|
{
|
||||||
|
sql_print_error("SCHEDULER: Table mysql.event is damaged. Can not open.");
|
||||||
|
DBUG_RETURN(EVEX_OPEN_TABLE_FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
init_alloc_root(&boot_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
|
||||||
|
init_read_record(&read_record_info, thd, table ,NULL,1,0);
|
||||||
|
while (!(read_record_info.read_record(&read_record_info)))
|
||||||
|
{
|
||||||
|
Event_timed *et;
|
||||||
|
if (!(et= new Event_timed))
|
||||||
|
{
|
||||||
|
DBUG_PRINT("info", ("Out of memory"));
|
||||||
|
clean_the_queue= TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DBUG_PRINT("info", ("Loading event from row."));
|
||||||
|
|
||||||
|
if ((ret= et->load_from_row(&scheduler_root, table)))
|
||||||
|
{
|
||||||
|
clean_the_queue= TRUE;
|
||||||
|
sql_print_error("SCHEDULER: Error while loading from mysql.event. "
|
||||||
|
"Table probably corrupted");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (et->status != Event_timed::ENABLED)
|
||||||
|
{
|
||||||
|
DBUG_PRINT("info",("%s is disabled",et->name.str));
|
||||||
|
delete et;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBUG_PRINT("info", ("Event %s loaded from row. ", et->name.str));
|
||||||
|
|
||||||
|
/* We load only on scheduler root just to check whether the body compiles */
|
||||||
|
switch (ret= et->compile(thd, &boot_root)) {
|
||||||
|
case EVEX_MICROSECOND_UNSUP:
|
||||||
|
et->free_sp();
|
||||||
|
sql_print_error("SCHEDULER: mysql.event is tampered. MICROSECOND is not "
|
||||||
|
"supported but found in mysql.event");
|
||||||
|
goto end;
|
||||||
|
case EVEX_COMPILE_ERROR:
|
||||||
|
sql_print_error("SCHEDULER: Error while compiling %s.%s. Aborting load.",
|
||||||
|
et->dbname.str, et->name.str);
|
||||||
|
goto end;
|
||||||
|
default:
|
||||||
|
/* Free it, it will be compiled again on the worker thread */
|
||||||
|
et->free_sp();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* let's find when to be executed */
|
||||||
|
if (et->compute_next_execution_time())
|
||||||
|
{
|
||||||
|
sql_print_error("SCHEDULER: Error while computing execution time of %s.%s."
|
||||||
|
" Skipping", et->dbname.str, et->name.str);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBUG_PRINT("load_events_from_db", ("Adding %p to the exec list."));
|
||||||
|
queue_insert_safe(&queue, (byte *) et);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
end_read_record(&read_record_info);
|
||||||
|
free_root(&boot_root, MYF(0));
|
||||||
|
|
||||||
|
if (clean_the_queue)
|
||||||
|
{
|
||||||
|
for (count= 0; count < queue.elements; ++count)
|
||||||
|
queue_remove(&queue, 0);
|
||||||
|
ret= -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret= 0;
|
||||||
|
sql_print_information("SCHEDULER: Loaded %d event%s", count, (count == 1)?"":"s");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force close to free memory */
|
||||||
|
thd->version--;
|
||||||
|
|
||||||
|
close_thread_tables(thd);
|
||||||
|
|
||||||
|
DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count));
|
||||||
|
DBUG_RETURN(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Opens mysql.db and mysql.user and checks whether:
|
||||||
|
1. mysql.db has column Event_priv at column 20 (0 based);
|
||||||
|
2. mysql.user has column Event_priv at column 29 (0 based);
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_queue::check_system_tables()
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool
|
||||||
|
Event_queue::check_system_tables(THD *thd)
|
||||||
|
{
|
||||||
|
TABLE_LIST tables;
|
||||||
|
bool not_used;
|
||||||
|
Open_tables_state backup;
|
||||||
|
bool ret;
|
||||||
|
|
||||||
|
DBUG_ENTER("Event_queue::check_system_tables");
|
||||||
|
DBUG_PRINT("enter", ("thd=%p", thd));
|
||||||
|
|
||||||
|
thd->reset_n_backup_open_tables_state(&backup);
|
||||||
|
|
||||||
|
bzero((char*) &tables, sizeof(tables));
|
||||||
|
tables.db= (char*) "mysql";
|
||||||
|
tables.table_name= tables.alias= (char*) "db";
|
||||||
|
tables.lock_type= TL_READ;
|
||||||
|
|
||||||
|
if ((ret= simple_open_n_lock_tables(thd, &tables)))
|
||||||
|
sql_print_error("Cannot open mysql.db");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret= table_check_intact(tables.table, MYSQL_DB_FIELD_COUNT,
|
||||||
|
mysql_db_table_fields, &mysql_db_table_last_check,
|
||||||
|
ER_CANNOT_LOAD_FROM_TABLE);
|
||||||
|
close_thread_tables(thd);
|
||||||
|
}
|
||||||
|
if (ret)
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
|
bzero((char*) &tables, sizeof(tables));
|
||||||
|
tables.db= (char*) "mysql";
|
||||||
|
tables.table_name= tables.alias= (char*) "user";
|
||||||
|
tables.lock_type= TL_READ;
|
||||||
|
|
||||||
|
if ((ret= simple_open_n_lock_tables(thd, &tables)))
|
||||||
|
sql_print_error("Cannot open mysql.db");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (tables.table->s->fields < 29 ||
|
||||||
|
strncmp(tables.table->field[29]->field_name,
|
||||||
|
STRING_WITH_LEN("Event_priv")))
|
||||||
|
{
|
||||||
|
sql_print_error("mysql.user has no `Event_priv` column at position 29");
|
||||||
|
ret= TRUE;
|
||||||
|
}
|
||||||
|
close_thread_tables(thd);
|
||||||
|
}
|
||||||
|
|
||||||
|
thd->restore_backup_open_tables_state(&backup);
|
||||||
|
|
||||||
|
DBUG_RETURN(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Inits mutexes.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_queue::init_mutexes()
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
Event_queue::init_mutexes()
|
||||||
|
{
|
||||||
|
pthread_mutex_init(&singleton->LOCK_event_queue, MY_MUTEX_INIT_FAST);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Destroys mutexes.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_queue::destroy_mutexes()
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
Event_queue::destroy_mutexes()
|
||||||
|
{
|
||||||
|
pthread_mutex_destroy(&singleton->LOCK_event_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Signals the main scheduler thread that the queue has changed
|
||||||
|
its state.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_queue::on_queue_change()
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
Event_queue::on_queue_change()
|
||||||
|
{
|
||||||
|
DBUG_ENTER("Event_queue::on_queue_change");
|
||||||
|
DBUG_PRINT("info", ("Sending COND_new_work"));
|
||||||
|
singleton->queue_changed();
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
The implementation of full-fledged initialization.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
Event_scheduler::init()
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
FALSE OK
|
||||||
|
TRUE Error
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool
|
||||||
|
Event_queue::init(Event_db_repository *db_repo)
|
||||||
|
{
|
||||||
|
int i= 0;
|
||||||
|
bool ret= FALSE;
|
||||||
|
DBUG_ENTER("Event_scheduler::init");
|
||||||
|
DBUG_PRINT("enter", ("this=%p", this));
|
||||||
|
|
||||||
|
LOCK_QUEUE_DATA();
|
||||||
|
db_repository= db_repo;
|
||||||
|
/* init memory root */
|
||||||
|
init_alloc_root(&scheduler_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
|
||||||
|
|
||||||
|
if (init_queue_ex(&queue, 30 /*num_el*/, 0 /*offset*/, 0 /*smallest_on_top*/,
|
||||||
|
event_timed_compare_q, NULL, 30 /*auto_extent*/))
|
||||||
|
{
|
||||||
|
sql_print_error("SCHEDULER: Can't initialize the execution queue");
|
||||||
|
ret= TRUE;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sizeof(my_time_t) != sizeof(time_t))
|
||||||
|
{
|
||||||
|
sql_print_error("SCHEDULER: sizeof(my_time_t) != sizeof(time_t) ."
|
||||||
|
"The scheduler may not work correctly. Stopping.");
|
||||||
|
DBUG_ASSERT(0);
|
||||||
|
ret= TRUE;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
UNLOCK_QUEUE_DATA();
|
||||||
|
DBUG_RETURN(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Event_queue::deinit()
|
||||||
|
{
|
||||||
|
DBUG_ENTER("Event_queue::deinit");
|
||||||
|
|
||||||
|
LOCK_QUEUE_DATA();
|
||||||
|
delete_queue(&queue);
|
||||||
|
free_root(&scheduler_root, MYF(0));
|
||||||
|
UNLOCK_QUEUE_DATA();
|
||||||
|
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Event_queue::recalculate_queue(THD *thd)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i= 0; i < queue.elements; i++)
|
||||||
|
{
|
||||||
|
((Event_timed*)queue_element(&queue, i))->compute_next_execution_time();
|
||||||
|
((Event_timed*)queue_element(&queue, i))->update_fields(thd);
|
||||||
|
}
|
||||||
|
queue_fix(&queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Event_queue::empty_queue()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
/* empty the queue */
|
||||||
|
for (i= 0; i < events_count_no_lock(); ++i)
|
||||||
|
{
|
||||||
|
Event_timed *et= (Event_timed *) queue_element(&queue, i);
|
||||||
|
et->free_sp();
|
||||||
|
delete et;
|
||||||
|
}
|
||||||
|
resize_queue(&queue, 0);
|
||||||
|
}
|
||||||
|
@ -16,5 +16,107 @@
|
|||||||
along with this program; if not, write to the Free Software
|
along with this program; if not, write to the Free Software
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
|
class sp_name;
|
||||||
|
class Event_timed;
|
||||||
|
class Event_db_repository;
|
||||||
|
|
||||||
|
class THD;
|
||||||
|
typedef bool * (*event_timed_identifier_comparator)(Event_timed*, Event_timed*);
|
||||||
|
|
||||||
|
class Event_scheduler;
|
||||||
|
|
||||||
|
class Event_queue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Event_queue();
|
||||||
|
|
||||||
|
static void
|
||||||
|
init_mutexes();
|
||||||
|
|
||||||
|
static void
|
||||||
|
destroy_mutexes();
|
||||||
|
|
||||||
|
bool
|
||||||
|
init(Event_db_repository *db_repo);
|
||||||
|
|
||||||
|
void
|
||||||
|
deinit();
|
||||||
|
|
||||||
|
/* Methods for queue management follow */
|
||||||
|
|
||||||
|
int
|
||||||
|
create_event(THD *thd, Event_parse_data *et, bool check_existence);
|
||||||
|
|
||||||
|
int
|
||||||
|
update_event(THD *thd, Event_parse_data *et, LEX_STRING *new_schema,
|
||||||
|
LEX_STRING *new_name);
|
||||||
|
|
||||||
|
bool
|
||||||
|
drop_event(THD *thd, sp_name *name);
|
||||||
|
|
||||||
|
int
|
||||||
|
drop_schema_events(THD *thd, LEX_STRING schema);
|
||||||
|
|
||||||
|
int
|
||||||
|
drop_user_events(THD *thd, LEX_STRING *definer)
|
||||||
|
{ DBUG_ASSERT(0); return 0;}
|
||||||
|
|
||||||
|
uint
|
||||||
|
events_count();
|
||||||
|
|
||||||
|
uint
|
||||||
|
events_count_no_lock();
|
||||||
|
|
||||||
|
static bool
|
||||||
|
check_system_tables(THD *thd);
|
||||||
|
|
||||||
|
void
|
||||||
|
recalculate_queue(THD *thd);
|
||||||
|
|
||||||
|
void
|
||||||
|
empty_queue();
|
||||||
|
|
||||||
|
///////////////protected
|
||||||
|
Event_timed *
|
||||||
|
find_event(LEX_STRING db, LEX_STRING name, bool remove_from_q);
|
||||||
|
|
||||||
|
int
|
||||||
|
load_events_from_db(THD *thd);
|
||||||
|
|
||||||
|
void
|
||||||
|
drop_matching_events(THD *thd, LEX_STRING pattern,
|
||||||
|
bool (*)(Event_timed *,LEX_STRING *));
|
||||||
|
|
||||||
|
/* LOCK_event_queue is the mutex which protects the access to the queue. */
|
||||||
|
pthread_mutex_t LOCK_event_queue;
|
||||||
|
|
||||||
|
Event_db_repository *db_repository;
|
||||||
|
|
||||||
|
/* The MEM_ROOT of the object */
|
||||||
|
MEM_ROOT scheduler_root;
|
||||||
|
|
||||||
|
/* The sorted queue with the Event_timed objects */
|
||||||
|
QUEUE queue;
|
||||||
|
|
||||||
|
uint mutex_last_locked_at_line;
|
||||||
|
uint mutex_last_unlocked_at_line;
|
||||||
|
const char* mutex_last_locked_in_func;
|
||||||
|
const char* mutex_last_unlocked_in_func;
|
||||||
|
bool mutex_queue_data_locked;
|
||||||
|
|
||||||
|
/* helper functions for working with mutexes & conditionals */
|
||||||
|
void
|
||||||
|
lock_data(const char *func, uint line);
|
||||||
|
|
||||||
|
void
|
||||||
|
unlock_data(const char *func, uint line);
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_queue_change();
|
||||||
|
protected:
|
||||||
|
/* Singleton instance */
|
||||||
|
static Event_scheduler *singleton;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* _EVENT_QUEUE_H_ */
|
#endif /* _EVENT_QUEUE_H_ */
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,6 @@ class Event_timed;
|
|||||||
class Event_db_repository;
|
class Event_db_repository;
|
||||||
|
|
||||||
class THD;
|
class THD;
|
||||||
typedef bool * (*event_timed_identifier_comparator)(Event_timed*, Event_timed*);
|
|
||||||
|
|
||||||
int
|
int
|
||||||
events_init();
|
events_init();
|
||||||
@ -29,10 +28,12 @@ events_init();
|
|||||||
void
|
void
|
||||||
events_shutdown();
|
events_shutdown();
|
||||||
|
|
||||||
class Event_scheduler
|
#include "event_queue.h"
|
||||||
|
#include "event_scheduler.h"
|
||||||
|
|
||||||
|
class Event_scheduler : public Event_queue
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum enum_state
|
enum enum_state
|
||||||
{
|
{
|
||||||
UNINITIALIZED= 0,
|
UNINITIALIZED= 0,
|
||||||
@ -50,32 +51,22 @@ public:
|
|||||||
RESUME= 2
|
RESUME= 2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* This is the current status of the life-cycle of the scheduler. */
|
||||||
|
enum enum_state state;
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
create_instance();
|
||||||
|
|
||||||
/* Singleton access */
|
/* Singleton access */
|
||||||
static Event_scheduler*
|
static Event_scheduler*
|
||||||
get_instance();
|
get_instance();
|
||||||
|
|
||||||
/* Methods for queue management follow */
|
|
||||||
|
|
||||||
int
|
|
||||||
create_event(THD *thd, Event_parse_data *et, bool check_existence);
|
|
||||||
|
|
||||||
int
|
|
||||||
update_event(THD *thd, Event_parse_data *et, LEX_STRING *new_schema,
|
|
||||||
LEX_STRING *new_name);
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
drop_event(THD *thd, sp_name *name);
|
init(Event_db_repository *db_repo);
|
||||||
|
|
||||||
|
void
|
||||||
int
|
destroy();
|
||||||
drop_schema_events(THD *thd, LEX_STRING schema);
|
|
||||||
|
|
||||||
int
|
|
||||||
drop_user_events(THD *thd, LEX_STRING *definer)
|
|
||||||
{ DBUG_ASSERT(0); return 0;}
|
|
||||||
|
|
||||||
uint
|
|
||||||
events_count();
|
|
||||||
|
|
||||||
/* State changing methods follow */
|
/* State changing methods follow */
|
||||||
|
|
||||||
@ -97,19 +88,13 @@ public:
|
|||||||
|
|
||||||
int
|
int
|
||||||
suspend_or_resume(enum enum_suspend_or_resume action);
|
suspend_or_resume(enum enum_suspend_or_resume action);
|
||||||
|
/*
|
||||||
bool
|
|
||||||
init(Event_db_repository *db_repo);
|
|
||||||
|
|
||||||
void
|
|
||||||
destroy();
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
init_mutexes();
|
init_mutexes();
|
||||||
|
|
||||||
static void
|
static void
|
||||||
destroy_mutexes();
|
destroy_mutexes();
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
report_error_during_start();
|
report_error_during_start();
|
||||||
|
|
||||||
@ -124,35 +109,34 @@ public:
|
|||||||
static int
|
static int
|
||||||
dump_internal_status(THD *thd);
|
dump_internal_status(THD *thd);
|
||||||
|
|
||||||
static bool
|
/* helper functions for working with mutexes & conditionals */
|
||||||
check_system_tables(THD *thd);
|
void
|
||||||
|
lock_data(const char *func, uint line);
|
||||||
|
|
||||||
private:
|
void
|
||||||
Event_timed *
|
unlock_data(const char *func, uint line);
|
||||||
find_event(LEX_STRING db, LEX_STRING name, bool remove_from_q);
|
|
||||||
|
int
|
||||||
|
cond_wait(int cond, pthread_mutex_t *mutex);
|
||||||
|
|
||||||
|
void
|
||||||
|
queue_changed();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
uint
|
uint
|
||||||
workers_count();
|
workers_count();
|
||||||
|
|
||||||
bool
|
|
||||||
is_running_or_suspended();
|
|
||||||
|
|
||||||
/* helper functions */
|
/* helper functions */
|
||||||
bool
|
bool
|
||||||
execute_top(THD *thd);
|
execute_top(THD *thd, Event_timed *et);
|
||||||
|
|
||||||
void
|
void
|
||||||
clean_queue(THD *thd);
|
clean_memory(THD *thd);
|
||||||
|
|
||||||
void
|
void
|
||||||
stop_all_running_events(THD *thd);
|
stop_all_running_events(THD *thd);
|
||||||
|
|
||||||
int
|
|
||||||
load_events_from_db(THD *thd);
|
|
||||||
|
|
||||||
void
|
|
||||||
drop_matching_events(THD *thd, LEX_STRING pattern,
|
|
||||||
bool (*)(Event_timed *,LEX_STRING *));
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
check_n_suspend_if_needed(THD *thd);
|
check_n_suspend_if_needed(THD *thd);
|
||||||
@ -163,47 +147,13 @@ private:
|
|||||||
/* Singleton DP is used */
|
/* Singleton DP is used */
|
||||||
Event_scheduler();
|
Event_scheduler();
|
||||||
|
|
||||||
enum enum_cond_vars
|
|
||||||
{
|
|
||||||
COND_NONE= -1,
|
|
||||||
/*
|
|
||||||
COND_new_work is a conditional used to signal that there is a change
|
|
||||||
of the queue that should inform the executor thread that new event should
|
|
||||||
be executed sooner than previously expected, because of add/replace event.
|
|
||||||
*/
|
|
||||||
COND_new_work= 0,
|
|
||||||
/*
|
|
||||||
COND_started is a conditional used to synchronize the thread in which
|
|
||||||
::start() was called and the spawned thread. ::start() spawns a new thread
|
|
||||||
and then waits on COND_started but also checks when awaken that `state` is
|
|
||||||
either RUNNING or CANTSTART. Then it returns back.
|
|
||||||
*/
|
|
||||||
COND_started_or_stopped,
|
|
||||||
/*
|
|
||||||
Conditional used for signalling from the scheduler thread back to the
|
|
||||||
thread that calls ::suspend() or ::resume. Synchronizing the calls.
|
|
||||||
*/
|
|
||||||
COND_suspend_or_resume,
|
|
||||||
/* Must be always last */
|
|
||||||
COND_LAST
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Singleton instance */
|
pthread_mutex_t *LOCK_scheduler_data;
|
||||||
static Event_scheduler singleton;
|
|
||||||
|
|
||||||
/* This is the current status of the life-cycle of the manager. */
|
|
||||||
enum enum_state state;
|
|
||||||
|
|
||||||
/* Set to start the scheduler in suspended state */
|
/* Set to start the scheduler in suspended state */
|
||||||
bool start_scheduler_suspended;
|
bool start_scheduler_suspended;
|
||||||
|
|
||||||
/*
|
|
||||||
LOCK_scheduler_data is the mutex which protects the access to the
|
|
||||||
manager's queue as well as used when signalling COND_new_work,
|
|
||||||
COND_started and COND_shutdown.
|
|
||||||
*/
|
|
||||||
pthread_mutex_t LOCK_scheduler_data;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Holds the thread id of the executor thread or 0 if the executor is not
|
Holds the thread id of the executor thread or 0 if the executor is not
|
||||||
running. It is used by ::shutdown() to know which thread to kill with
|
running. It is used by ::shutdown() to know which thread to kill with
|
||||||
@ -212,33 +162,27 @@ private:
|
|||||||
*/
|
*/
|
||||||
ulong thread_id;
|
ulong thread_id;
|
||||||
|
|
||||||
pthread_cond_t cond_vars[COND_LAST];
|
enum enum_cond_vars
|
||||||
static const char * const cond_vars_names[COND_LAST];
|
{
|
||||||
|
COND_NONE= -1,
|
||||||
|
COND_new_work= 0,
|
||||||
|
COND_started_or_stopped,
|
||||||
|
COND_suspend_or_resume,
|
||||||
|
/* Must be always last */
|
||||||
|
COND_LAST
|
||||||
|
};
|
||||||
|
|
||||||
/* The MEM_ROOT of the object */
|
uint mutex_last_locked_at_line_nr;
|
||||||
MEM_ROOT scheduler_root;
|
uint mutex_last_unlocked_at_line_nr;
|
||||||
|
const char* mutex_last_locked_in_func_name;
|
||||||
Event_db_repository *db_repository;
|
const char* mutex_last_unlocked_in_func_name;
|
||||||
|
int cond_waiting_on;
|
||||||
/* The sorted queue with the Event_timed objects */
|
|
||||||
QUEUE queue;
|
|
||||||
|
|
||||||
uint mutex_last_locked_at_line;
|
|
||||||
uint mutex_last_unlocked_at_line;
|
|
||||||
const char* mutex_last_locked_in_func;
|
|
||||||
const char* mutex_last_unlocked_in_func;
|
|
||||||
enum enum_cond_vars cond_waiting_on;
|
|
||||||
bool mutex_scheduler_data_locked;
|
bool mutex_scheduler_data_locked;
|
||||||
|
|
||||||
/* helper functions for working with mutexes & conditionals */
|
|
||||||
void
|
|
||||||
lock_data(const char *func, uint line);
|
|
||||||
|
|
||||||
void
|
static const char * const cond_vars_names[COND_LAST];
|
||||||
unlock_data(const char *func, uint line);
|
|
||||||
|
|
||||||
int
|
pthread_cond_t cond_vars[COND_LAST];
|
||||||
cond_wait(enum enum_cond_vars, pthread_mutex_t *mutex);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/* Prevent use of these */
|
/* Prevent use of these */
|
||||||
|
@ -559,6 +559,7 @@ void
|
|||||||
Events::init_mutexes()
|
Events::init_mutexes()
|
||||||
{
|
{
|
||||||
db_repository= new Event_db_repository;
|
db_repository= new Event_db_repository;
|
||||||
|
Event_scheduler::create_instance();
|
||||||
Event_scheduler::init_mutexes();
|
Event_scheduler::init_mutexes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user