1 | /* Copyright (c) 2006, 2013, Oracle and/or its affiliates. |
2 | |
3 | This program is free software; you can redistribute it and/or modify |
4 | it under the terms of the GNU General Public License as published by |
5 | the Free Software Foundation; version 2 of the License. |
6 | |
7 | This program is distributed in the hope that it will be useful, |
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | GNU General Public License for more details. |
11 | |
12 | You should have received a copy of the GNU General Public License |
13 | along with this program; if not, write to the Free Software |
14 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
15 | |
16 | #include "mariadb.h" |
17 | #include "sql_priv.h" |
18 | #include "unireg.h" |
19 | #include "event_scheduler.h" |
20 | #include "events.h" |
21 | #include "event_data_objects.h" |
22 | #include "event_queue.h" |
23 | #include "event_db_repository.h" |
24 | #include "sql_connect.h" // init_new_connection_handler_thread |
25 | #include "sql_acl.h" // SUPER_ACL |
26 | |
27 | /** |
28 | @addtogroup Event_Scheduler |
29 | @{ |
30 | */ |
31 | |
32 | #ifdef __GNUC__ |
33 | #if __GNUC__ >= 2 |
34 | #define SCHED_FUNC __FUNCTION__ |
35 | #endif |
36 | #else |
37 | #define SCHED_FUNC "<unknown>" |
38 | #endif |
39 | |
40 | #define LOCK_DATA() lock_data(SCHED_FUNC, __LINE__) |
41 | #define UNLOCK_DATA() unlock_data(SCHED_FUNC, __LINE__) |
42 | #define COND_STATE_WAIT(mythd, abstime, stage) \ |
43 | cond_wait(mythd, abstime, stage, SCHED_FUNC, __FILE__, __LINE__) |
44 | |
45 | extern pthread_attr_t connection_attrib; |
46 | extern ulong event_executed; |
47 | |
48 | Event_db_repository *Event_worker_thread::db_repository; |
49 | |
50 | |
51 | static |
52 | const LEX_CSTRING scheduler_states_names[] = |
53 | { |
54 | { STRING_WITH_LEN("INITIALIZED" ) }, |
55 | { STRING_WITH_LEN("RUNNING" ) }, |
56 | { STRING_WITH_LEN("STOPPING" ) } |
57 | }; |
58 | |
59 | struct scheduler_param { |
60 | THD *thd; |
61 | Event_scheduler *scheduler; |
62 | }; |
63 | |
64 | |
65 | /* |
66 | Prints the stack of infos, warnings, errors from thd to |
67 | the console so it can be fetched by the logs-into-tables and |
68 | checked later. |
69 | |
70 | SYNOPSIS |
71 | evex_print_warnings |
72 | thd Thread used during the execution of the event |
73 | et The event itself |
74 | */ |
75 | |
76 | void |
77 | Event_worker_thread::print_warnings(THD *thd, Event_job_data *et) |
78 | { |
79 | const Sql_condition *err; |
80 | DBUG_ENTER("evex_print_warnings" ); |
81 | if (thd->get_stmt_da()->is_warning_info_empty()) |
82 | DBUG_VOID_RETURN; |
83 | |
84 | char msg_buf[10 * STRING_BUFFER_USUAL_SIZE]; |
85 | char prefix_buf[5 * STRING_BUFFER_USUAL_SIZE]; |
86 | String prefix(prefix_buf, sizeof(prefix_buf), system_charset_info); |
87 | prefix.length(0); |
88 | prefix.append(STRING_WITH_LEN("Event Scheduler: [" )); |
89 | |
90 | prefix.append(et->definer.str, et->definer.length, system_charset_info); |
91 | prefix.append("][" , 2); |
92 | prefix.append(et->dbname.str, et->dbname.length, system_charset_info); |
93 | prefix.append('.'); |
94 | prefix.append(et->name.str, et->name.length, system_charset_info); |
95 | prefix.append("] " , 2); |
96 | |
97 | Diagnostics_area::Sql_condition_iterator it= |
98 | thd->get_stmt_da()->sql_conditions(); |
99 | while ((err= it++)) |
100 | { |
101 | String err_msg(msg_buf, sizeof(msg_buf), system_charset_info); |
102 | /* set it to 0 or we start adding at the end. That's the trick ;) */ |
103 | err_msg.length(0); |
104 | err_msg.append(prefix); |
105 | err_msg.append(err->get_message_text(), |
106 | err->get_message_octet_length(), system_charset_info); |
107 | DBUG_ASSERT(err->get_level() < 3); |
108 | (sql_print_message_handlers[err->get_level()])("%*s" , err_msg.length(), |
109 | err_msg.c_ptr_safe()); |
110 | } |
111 | DBUG_VOID_RETURN; |
112 | } |
113 | |
114 | |
115 | /* |
116 | Performs post initialization of structures in a new thread. |
117 | |
118 | SYNOPSIS |
119 | post_init_event_thread() |
120 | thd Thread |
121 | |
122 | NOTES |
123 | Before this is called, one should not do any DBUG_XXX() calls. |
124 | |
125 | */ |
126 | |
127 | bool |
128 | post_init_event_thread(THD *thd) |
129 | { |
130 | (void) init_new_connection_handler_thread(); |
131 | if (init_thr_lock() || thd->store_globals()) |
132 | { |
133 | thd->cleanup(); |
134 | return TRUE; |
135 | } |
136 | return FALSE; |
137 | } |
138 | |
139 | |
140 | /* |
141 | Cleans up the THD and the threaded environment of the thread. |
142 | |
143 | SYNOPSIS |
144 | deinit_event_thread() |
145 | thd Thread |
146 | */ |
147 | |
148 | void |
149 | deinit_event_thread(THD *thd) |
150 | { |
151 | thd->proc_info= "Clearing" ; |
152 | DBUG_PRINT("exit" , ("Event thread finishing" )); |
153 | unlink_not_visible_thd(thd); |
154 | delete thd; |
155 | } |
156 | |
157 | |
158 | /* |
159 | Performs pre- mysql_thread_create() initialisation of THD. Do this |
160 | in the thread that will pass THD to the child thread. In the |
161 | child thread call post_init_event_thread(). |
162 | |
163 | SYNOPSIS |
164 | pre_init_event_thread() |
165 | thd The THD of the thread. Has to be allocated by the caller. |
166 | |
167 | NOTES |
168 | 1. The host of the thead is my_localhost |
169 | 2. thd->net is initted with NULL - no communication. |
170 | */ |
171 | |
172 | void |
173 | pre_init_event_thread(THD* thd) |
174 | { |
175 | THD *orig_thd= current_thd; |
176 | DBUG_ENTER("pre_init_event_thread" ); |
177 | |
178 | set_current_thd(thd); |
179 | thd->client_capabilities= 0; |
180 | thd->security_ctx->master_access= 0; |
181 | thd->security_ctx->db_access= 0; |
182 | thd->security_ctx->host_or_ip= (char*)my_localhost; |
183 | my_net_init(&thd->net, NULL, thd, MYF(MY_THREAD_SPECIFIC)); |
184 | thd->security_ctx->set_user((char*)"event_scheduler" ); |
185 | thd->net.read_timeout= slave_net_timeout; |
186 | thd->variables.option_bits|= OPTION_AUTO_IS_NULL; |
187 | thd->client_capabilities|= CLIENT_MULTI_RESULTS; |
188 | add_to_active_threads(thd); |
189 | |
190 | /* |
191 | Guarantees that we will see the thread in SHOW PROCESSLIST though its |
192 | vio is NULL. |
193 | */ |
194 | |
195 | thd->proc_info= "Initialized" ; |
196 | thd->set_time(); |
197 | |
198 | /* Do not use user-supplied timeout value for system threads. */ |
199 | thd->variables.lock_wait_timeout= LONG_TIMEOUT; |
200 | |
201 | set_current_thd(orig_thd); |
202 | DBUG_VOID_RETURN; |
203 | } |
204 | |
205 | |
206 | /* |
207 | Function that executes the scheduler, |
208 | |
209 | SYNOPSIS |
210 | event_scheduler_thread() |
211 | arg Pointer to `struct scheduler_param` |
212 | |
213 | RETURN VALUE |
214 | 0 OK |
215 | */ |
216 | |
217 | pthread_handler_t |
218 | event_scheduler_thread(void *arg) |
219 | { |
220 | /* needs to be first for thread_stack */ |
221 | THD *thd= (THD *) ((struct scheduler_param *) arg)->thd; |
222 | Event_scheduler *scheduler= ((struct scheduler_param *) arg)->scheduler; |
223 | bool res; |
224 | |
225 | thd->thread_stack= (char *)&thd; // remember where our stack is |
226 | |
227 | mysql_thread_set_psi_id(thd->thread_id); |
228 | |
229 | res= post_init_event_thread(thd); |
230 | |
231 | DBUG_ENTER("event_scheduler_thread" ); |
232 | my_free(arg); |
233 | if (!res) |
234 | scheduler->run(thd); |
235 | |
236 | deinit_event_thread(thd); |
237 | DBUG_LEAVE; // Against gcc warnings |
238 | my_thread_end(); |
239 | return 0; |
240 | } |
241 | |
242 | |
243 | /** |
244 | Function that executes an event in a child thread. Setups the |
245 | environment for the event execution and cleans after that. |
246 | |
247 | SYNOPSIS |
248 | event_worker_thread() |
249 | arg The Event_job_data object to be processed |
250 | |
251 | RETURN VALUE |
252 | 0 OK |
253 | */ |
254 | |
255 | pthread_handler_t |
256 | event_worker_thread(void *arg) |
257 | { |
258 | THD *thd; |
259 | Event_queue_element_for_exec *event= (Event_queue_element_for_exec *)arg; |
260 | |
261 | thd= event->thd; |
262 | |
263 | mysql_thread_set_psi_id(thd->thread_id); |
264 | |
265 | Event_worker_thread worker_thread; |
266 | worker_thread.run(thd, event); |
267 | |
268 | my_thread_end(); |
269 | return 0; // Can't return anything here |
270 | } |
271 | |
272 | |
273 | /** |
274 | Function that executes an event in a child thread. Setups the |
275 | environment for the event execution and cleans after that. |
276 | |
277 | SYNOPSIS |
278 | Event_worker_thread::run() |
279 | thd Thread context |
280 | event The Event_queue_element_for_exec object to be processed |
281 | */ |
282 | |
283 | void |
284 | Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event) |
285 | { |
286 | /* needs to be first for thread_stack */ |
287 | char my_stack; |
288 | Event_job_data job_data; |
289 | bool res; |
290 | |
291 | DBUG_ASSERT(thd->m_digest == NULL); |
292 | DBUG_ASSERT(thd->m_statement_psi == NULL); |
293 | |
294 | thd->thread_stack= &my_stack; // remember where our stack is |
295 | res= post_init_event_thread(thd); |
296 | |
297 | DBUG_ENTER("Event_worker_thread::run" ); |
298 | DBUG_PRINT("info" , ("Time is %u, THD: %p" , (uint)my_time(0), thd)); |
299 | |
300 | if (res) |
301 | goto end; |
302 | |
303 | if ((res= db_repository->load_named_event(thd, &event->dbname, &event->name, |
304 | &job_data))) |
305 | { |
306 | DBUG_PRINT("error" , ("Got error from load_named_event" )); |
307 | goto end; |
308 | } |
309 | |
310 | thd->enable_slow_log= TRUE; |
311 | |
312 | res= job_data.execute(thd, event->dropped); |
313 | |
314 | print_warnings(thd, &job_data); |
315 | |
316 | if (res) |
317 | sql_print_information("Event Scheduler: " |
318 | "[%s].[%s.%s] event execution failed." , |
319 | job_data.definer.str, |
320 | job_data.dbname.str, job_data.name.str); |
321 | end: |
322 | DBUG_ASSERT(thd->m_statement_psi == NULL); |
323 | DBUG_ASSERT(thd->m_digest == NULL); |
324 | DBUG_PRINT("info" , ("Done with Event %s.%s" , event->dbname.str, |
325 | event->name.str)); |
326 | |
327 | delete event; |
328 | deinit_event_thread(thd); |
329 | |
330 | DBUG_VOID_RETURN; |
331 | } |
332 | |
333 | |
334 | Event_scheduler::Event_scheduler(Event_queue *queue_arg) |
335 | :state(INITIALIZED), |
336 | scheduler_thd(NULL), |
337 | queue(queue_arg), |
338 | mutex_last_locked_at_line(0), |
339 | mutex_last_unlocked_at_line(0), |
340 | mutex_last_locked_in_func("n/a" ), |
341 | mutex_last_unlocked_in_func("n/a" ), |
342 | mutex_scheduler_data_locked(FALSE), |
343 | waiting_on_cond(FALSE), |
344 | started_events(0) |
345 | { |
346 | mysql_mutex_init(key_event_scheduler_LOCK_scheduler_state, |
347 | &LOCK_scheduler_state, MY_MUTEX_INIT_FAST); |
348 | mysql_cond_init(key_event_scheduler_COND_state, &COND_state, NULL); |
349 | mysql_mutex_record_order(&LOCK_scheduler_state, &LOCK_global_system_variables); |
350 | } |
351 | |
352 | |
353 | Event_scheduler::~Event_scheduler() |
354 | { |
355 | stop(); /* does nothing if not running */ |
356 | mysql_mutex_destroy(&LOCK_scheduler_state); |
357 | mysql_cond_destroy(&COND_state); |
358 | } |
359 | |
360 | |
361 | /** |
362 | Starts the scheduler (again). Creates a new THD and passes it to |
363 | a forked thread. Does not wait for acknowledgement from the new |
364 | thread that it has started. Asynchronous starting. Most of the |
365 | needed initializations are done in the current thread to minimize |
366 | the chance of failure in the spawned thread. |
367 | |
368 | @param[out] err_no - errno indicating type of error which caused |
369 | failure to start scheduler thread. |
370 | |
371 | @return |
372 | @retval false Success. |
373 | @retval true Error. |
374 | */ |
375 | |
376 | bool |
377 | Event_scheduler::start(int *err_no) |
378 | { |
379 | THD *new_thd= NULL; |
380 | bool ret= false; |
381 | pthread_t th; |
382 | struct scheduler_param *scheduler_param_value; |
383 | DBUG_ENTER("Event_scheduler::start" ); |
384 | |
385 | LOCK_DATA(); |
386 | DBUG_PRINT("info" , ("state before action %s" , scheduler_states_names[state].str)); |
387 | if (state > INITIALIZED) |
388 | goto end; |
389 | |
390 | if (!(new_thd= new THD(next_thread_id()))) |
391 | { |
392 | sql_print_error("Event Scheduler: Cannot initialize the scheduler thread" ); |
393 | ret= true; |
394 | goto end; |
395 | } |
396 | |
397 | pre_init_event_thread(new_thd); |
398 | new_thd->system_thread= SYSTEM_THREAD_EVENT_SCHEDULER; |
399 | new_thd->set_command(COM_DAEMON); |
400 | |
401 | /* |
402 | We should run the event scheduler thread under the super-user privileges. |
403 | In particular, this is needed to be able to lock the mysql.event table |
404 | for writing when the server is running in the read-only mode. |
405 | |
406 | Same goes for transaction access mode. Set it to read-write for this thd. |
407 | */ |
408 | new_thd->security_ctx->master_access |= SUPER_ACL; |
409 | new_thd->variables.tx_read_only= false; |
410 | new_thd->tx_read_only= false; |
411 | |
412 | /* This should not be marked with MY_THREAD_SPECIFIC */ |
413 | scheduler_param_value= |
414 | (struct scheduler_param *)my_malloc(sizeof(struct scheduler_param), MYF(0)); |
415 | scheduler_param_value->thd= new_thd; |
416 | scheduler_param_value->scheduler= this; |
417 | |
418 | scheduler_thd= new_thd; |
419 | DBUG_PRINT("info" , ("Setting state go RUNNING" )); |
420 | state= RUNNING; |
421 | DBUG_PRINT("info" , ("Forking new thread for scheduler. THD: %p" , new_thd)); |
422 | if ((*err_no= mysql_thread_create(key_thread_event_scheduler, |
423 | &th, &connection_attrib, |
424 | event_scheduler_thread, |
425 | (void*)scheduler_param_value))) |
426 | { |
427 | DBUG_PRINT("error" , ("cannot create a new thread" )); |
428 | sql_print_error("Event scheduler: Failed to start scheduler," |
429 | " Can not create thread for event scheduler (errno=%d)" , |
430 | *err_no); |
431 | |
432 | state= INITIALIZED; |
433 | scheduler_thd= NULL; |
434 | deinit_event_thread(new_thd); |
435 | |
436 | delete scheduler_param_value; |
437 | ret= true; |
438 | } |
439 | |
440 | end: |
441 | UNLOCK_DATA(); |
442 | DBUG_RETURN(ret); |
443 | } |
444 | |
445 | |
446 | /* |
447 | The main loop of the scheduler. |
448 | |
449 | SYNOPSIS |
450 | Event_scheduler::run() |
451 | thd Thread |
452 | |
453 | RETURN VALUE |
454 | FALSE OK |
455 | TRUE Error (Serious error) |
456 | */ |
457 | |
458 | bool |
459 | Event_scheduler::run(THD *thd) |
460 | { |
461 | int res= FALSE; |
462 | DBUG_ENTER("Event_scheduler::run" ); |
463 | |
464 | sql_print_information("Event Scheduler: scheduler thread started with id %lu" , |
465 | (ulong) thd->thread_id); |
466 | /* |
467 | Recalculate the values in the queue because there could have been stops |
468 | in executions of the scheduler and some times could have passed by. |
469 | */ |
470 | queue->recalculate_activation_times(thd); |
471 | |
472 | while (is_running()) |
473 | { |
474 | Event_queue_element_for_exec *event_name; |
475 | |
476 | /* Gets a minimized version */ |
477 | if (queue->get_top_for_execution_if_time(thd, &event_name)) |
478 | { |
479 | sql_print_information("Event Scheduler: " |
480 | "Serious error during getting next " |
481 | "event to execute. Stopping" ); |
482 | break; |
483 | } |
484 | |
485 | DBUG_PRINT("info" , ("get_top_for_execution_if_time returned " |
486 | "event_name=%p" , event_name)); |
487 | if (event_name) |
488 | { |
489 | if ((res= execute_top(event_name))) |
490 | break; |
491 | } |
492 | else |
493 | { |
494 | DBUG_ASSERT(thd->killed); |
495 | DBUG_PRINT("info" , ("job_data is NULL, the thread was killed" )); |
496 | } |
497 | DBUG_PRINT("info" , ("state=%s" , scheduler_states_names[state].str)); |
498 | } |
499 | |
500 | LOCK_DATA(); |
501 | scheduler_thd= NULL; |
502 | state= INITIALIZED; |
503 | DBUG_PRINT("info" , ("Broadcasting COND_state back to the stoppers" )); |
504 | mysql_cond_broadcast(&COND_state); |
505 | UNLOCK_DATA(); |
506 | |
507 | DBUG_RETURN(res); |
508 | } |
509 | |
510 | |
511 | /* |
512 | Creates a new THD instance and then forks a new thread, while passing |
513 | the THD pointer and job_data to it. |
514 | |
515 | SYNOPSIS |
516 | Event_scheduler::execute_top() |
517 | |
518 | RETURN VALUE |
519 | FALSE OK |
520 | TRUE Error (Serious error) |
521 | */ |
522 | |
523 | bool |
524 | Event_scheduler::execute_top(Event_queue_element_for_exec *event_name) |
525 | { |
526 | THD *new_thd; |
527 | pthread_t th; |
528 | int res= 0; |
529 | DBUG_ENTER("Event_scheduler::execute_top" ); |
530 | |
531 | if (!(new_thd= new THD(next_thread_id()))) |
532 | goto error; |
533 | |
534 | pre_init_event_thread(new_thd); |
535 | new_thd->system_thread= SYSTEM_THREAD_EVENT_WORKER; |
536 | event_name->thd= new_thd; |
537 | DBUG_PRINT("info" , ("Event %s@%s ready for start" , |
538 | event_name->dbname.str, event_name->name.str)); |
539 | |
540 | /* |
541 | TODO: should use thread pool here, preferably with an upper limit |
542 | on number of threads: if too many events are scheduled for the |
543 | same time, starting all of them at once won't help them run truly |
544 | in parallel (because of the great amount of synchronization), so |
545 | we may as well execute them in sequence, keeping concurrency at a |
546 | reasonable level. |
547 | */ |
548 | /* Major failure */ |
549 | if ((res= mysql_thread_create(key_thread_event_worker, |
550 | &th, &connection_attrib, event_worker_thread, |
551 | event_name))) |
552 | { |
553 | mysql_mutex_lock(&LOCK_global_system_variables); |
554 | Events::opt_event_scheduler= Events::EVENTS_OFF; |
555 | mysql_mutex_unlock(&LOCK_global_system_variables); |
556 | |
557 | sql_print_error("Event_scheduler::execute_top: Can not create event worker" |
558 | " thread (errno=%d). Stopping event scheduler" , res); |
559 | |
560 | deinit_event_thread(new_thd); |
561 | goto error; |
562 | } |
563 | |
564 | started_events++; |
565 | executed_events++; // For SHOW STATUS |
566 | |
567 | DBUG_PRINT("info" , ("Event is in THD: %p" , new_thd)); |
568 | DBUG_RETURN(FALSE); |
569 | |
570 | error: |
571 | DBUG_PRINT("error" , ("Event_scheduler::execute_top() res: %d" , res)); |
572 | delete event_name; |
573 | DBUG_RETURN(TRUE); |
574 | } |
575 | |
576 | |
577 | /* |
578 | Checks whether the state of the scheduler is RUNNING |
579 | |
580 | SYNOPSIS |
581 | Event_scheduler::is_running() |
582 | |
583 | RETURN VALUE |
584 | TRUE RUNNING |
585 | FALSE Not RUNNING |
586 | */ |
587 | |
588 | bool |
589 | Event_scheduler::is_running() |
590 | { |
591 | LOCK_DATA(); |
592 | bool ret= (state == RUNNING); |
593 | UNLOCK_DATA(); |
594 | return ret; |
595 | } |
596 | |
597 | |
598 | /** |
599 | Stops the scheduler (again). Waits for acknowledgement from the |
600 | scheduler that it has stopped - synchronous stopping. |
601 | |
602 | Already running events will not be stopped. If the user needs |
603 | them stopped manual intervention is needed. |
604 | |
605 | SYNOPSIS |
606 | Event_scheduler::stop() |
607 | |
608 | RETURN VALUE |
609 | FALSE OK |
610 | TRUE Error (not reported) |
611 | */ |
612 | |
613 | bool |
614 | Event_scheduler::stop() |
615 | { |
616 | THD *thd= current_thd; |
617 | DBUG_ENTER("Event_scheduler::stop" ); |
618 | DBUG_PRINT("enter" , ("thd: %p" , thd)); |
619 | |
620 | LOCK_DATA(); |
621 | DBUG_PRINT("info" , ("state before action %s" , scheduler_states_names[state].str)); |
622 | if (state != RUNNING) |
623 | { |
624 | /* Synchronously wait until the scheduler stops. */ |
625 | while (state != INITIALIZED) |
626 | COND_STATE_WAIT(thd, NULL, &stage_waiting_for_scheduler_to_stop); |
627 | goto end; |
628 | } |
629 | |
630 | /* Guarantee we don't catch spurious signals */ |
631 | do { |
632 | DBUG_PRINT("info" , ("Waiting for COND_started_or_stopped from " |
633 | "the scheduler thread. Current value of state is %s . " |
634 | "workers count=%d" , scheduler_states_names[state].str, |
635 | workers_count())); |
636 | /* |
637 | NOTE: We don't use kill_one_thread() because it can't kill COM_DEAMON |
638 | threads. In addition, kill_one_thread() requires THD but during shutdown |
639 | current_thd is NULL. Hence, if kill_one_thread should be used it has to |
640 | be modified to kill also daemons, by adding a flag, and also we have to |
641 | create artificial THD here. To save all this work, we just do what |
642 | kill_one_thread() does to kill a thread. See also sql_repl.cc for similar |
643 | usage. |
644 | */ |
645 | |
646 | state= STOPPING; |
647 | DBUG_PRINT("info" , ("Scheduler thread has id %lu" , |
648 | (ulong) scheduler_thd->thread_id)); |
649 | /* This will wake up the thread if it waits on Queue's conditional */ |
650 | sql_print_information("Event Scheduler: Killing the scheduler thread, " |
651 | "thread id %lu" , |
652 | (ulong) scheduler_thd->thread_id); |
653 | scheduler_thd->awake(KILL_CONNECTION); |
654 | |
655 | /* thd could be 0x0, when shutting down */ |
656 | sql_print_information("Event Scheduler: " |
657 | "Waiting for the scheduler thread to reply" ); |
658 | |
659 | /* |
660 | Wait only 2 seconds, as there is a small chance the thread missed the |
661 | above awake() call and we may have to do it again |
662 | */ |
663 | struct timespec top_time; |
664 | set_timespec(top_time, 2); |
665 | COND_STATE_WAIT(thd, &top_time, &stage_waiting_for_scheduler_to_stop); |
666 | } while (state == STOPPING); |
667 | DBUG_PRINT("info" , ("Scheduler thread has cleaned up. Set state to INIT" )); |
668 | sql_print_information("Event Scheduler: Stopped" ); |
669 | end: |
670 | UNLOCK_DATA(); |
671 | DBUG_RETURN(FALSE); |
672 | } |
673 | |
674 | |
675 | /* |
676 | Returns the number of living event worker threads. |
677 | |
678 | SYNOPSIS |
679 | Event_scheduler::workers_count() |
680 | */ |
681 | |
682 | uint |
683 | Event_scheduler::workers_count() |
684 | { |
685 | THD *tmp; |
686 | uint count= 0; |
687 | |
688 | DBUG_ENTER("Event_scheduler::workers_count" ); |
689 | mysql_mutex_lock(&LOCK_thread_count); // For unlink from list |
690 | I_List_iterator<THD> it(threads); |
691 | while ((tmp=it++)) |
692 | if (tmp->system_thread == SYSTEM_THREAD_EVENT_WORKER) |
693 | ++count; |
694 | mysql_mutex_unlock(&LOCK_thread_count); |
695 | DBUG_PRINT("exit" , ("%d" , count)); |
696 | DBUG_RETURN(count); |
697 | } |
698 | |
699 | |
700 | /* |
701 | Auxiliary function for locking LOCK_scheduler_state. Used |
702 | by the LOCK_DATA macro. |
703 | |
704 | SYNOPSIS |
705 | Event_scheduler::lock_data() |
706 | func Which function is requesting mutex lock |
707 | line On which line mutex lock is requested |
708 | */ |
709 | |
710 | void |
711 | Event_scheduler::lock_data(const char *func, uint line) |
712 | { |
713 | DBUG_ENTER("Event_scheduler::lock_data" ); |
714 | DBUG_PRINT("enter" , ("func=%s line=%u" , func, line)); |
715 | mysql_mutex_lock(&LOCK_scheduler_state); |
716 | mutex_last_locked_in_func= func; |
717 | mutex_last_locked_at_line= line; |
718 | mutex_scheduler_data_locked= TRUE; |
719 | DBUG_VOID_RETURN; |
720 | } |
721 | |
722 | |
723 | /* |
724 | Auxiliary function for unlocking LOCK_scheduler_state. Used |
725 | by the UNLOCK_DATA macro. |
726 | |
727 | SYNOPSIS |
728 | Event_scheduler::unlock_data() |
729 | func Which function is requesting mutex unlock |
730 | line On which line mutex unlock is requested |
731 | */ |
732 | |
733 | void |
734 | Event_scheduler::unlock_data(const char *func, uint line) |
735 | { |
736 | DBUG_ENTER("Event_scheduler::unlock_data" ); |
737 | DBUG_PRINT("enter" , ("func=%s line=%u" , func, line)); |
738 | mutex_last_unlocked_at_line= line; |
739 | mutex_scheduler_data_locked= FALSE; |
740 | mutex_last_unlocked_in_func= func; |
741 | mysql_mutex_unlock(&LOCK_scheduler_state); |
742 | DBUG_VOID_RETURN; |
743 | } |
744 | |
745 | |
746 | /* |
747 | Wrapper for mysql_cond_wait/timedwait |
748 | |
749 | SYNOPSIS |
750 | Event_scheduler::cond_wait() |
751 | thd Thread (Could be NULL during shutdown procedure) |
752 | abstime If not null then call mysql_cond_timedwait() |
753 | msg Message for thd->proc_info |
754 | func Which function is requesting cond_wait |
755 | line On which line cond_wait is requested |
756 | */ |
757 | |
758 | void |
759 | Event_scheduler::cond_wait(THD *thd, struct timespec *abstime, const PSI_stage_info *stage, |
760 | const char *src_func, const char *src_file, uint src_line) |
761 | { |
762 | DBUG_ENTER("Event_scheduler::cond_wait" ); |
763 | waiting_on_cond= TRUE; |
764 | mutex_last_unlocked_at_line= src_line; |
765 | mutex_scheduler_data_locked= FALSE; |
766 | mutex_last_unlocked_in_func= src_func; |
767 | if (thd) |
768 | thd->enter_cond(&COND_state, &LOCK_scheduler_state, stage, |
769 | NULL, src_func, src_file, src_line); |
770 | |
771 | DBUG_PRINT("info" , ("mysql_cond_%swait" , abstime? "timed" :"" )); |
772 | if (!abstime) |
773 | mysql_cond_wait(&COND_state, &LOCK_scheduler_state); |
774 | else |
775 | mysql_cond_timedwait(&COND_state, &LOCK_scheduler_state, abstime); |
776 | if (thd) |
777 | { |
778 | /* |
779 | This will free the lock so we need to relock. Not the best thing to |
780 | do but we need to obey cond_wait() |
781 | */ |
782 | thd->exit_cond(NULL, src_func, src_file, src_line); |
783 | LOCK_DATA(); |
784 | } |
785 | mutex_last_locked_in_func= src_func; |
786 | mutex_last_locked_at_line= src_line; |
787 | mutex_scheduler_data_locked= TRUE; |
788 | waiting_on_cond= FALSE; |
789 | DBUG_VOID_RETURN; |
790 | } |
791 | |
792 | |
793 | /* |
794 | Dumps the internal status of the scheduler |
795 | |
796 | SYNOPSIS |
797 | Event_scheduler::dump_internal_status() |
798 | */ |
799 | |
800 | void |
801 | Event_scheduler::dump_internal_status() |
802 | { |
803 | DBUG_ENTER("Event_scheduler::dump_internal_status" ); |
804 | |
805 | puts("" ); |
806 | puts("Event scheduler status:" ); |
807 | printf("State : %s\n" , scheduler_states_names[state].str); |
808 | printf("Thread id : %lu\n" , scheduler_thd ? |
809 | (ulong) scheduler_thd->thread_id : (ulong) 0); |
810 | printf("LLA : %s:%u\n" , mutex_last_locked_in_func, |
811 | mutex_last_locked_at_line); |
812 | printf("LUA : %s:%u\n" , mutex_last_unlocked_in_func, |
813 | mutex_last_unlocked_at_line); |
814 | printf("WOC : %s\n" , waiting_on_cond? "YES" :"NO" ); |
815 | printf("Workers : %u\n" , workers_count()); |
816 | printf("Executed : %lu\n" , (ulong) started_events); |
817 | printf("Data locked: %s\n" , mutex_scheduler_data_locked ? "YES" :"NO" ); |
818 | |
819 | DBUG_VOID_RETURN; |
820 | } |
821 | |
822 | /** |
823 | @} (End of group Event_Scheduler) |
824 | */ |
825 | |