summaryrefslogtreecommitdiff
path: root/sql/threadpool_common.cc
blob: 91ae41a058f54b06fb0876b8ab395b72db0c022e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
#include <my_global.h>
#include <violite.h>
#include <sql_priv.h>
#include <sql_class.h>
#include <my_pthread.h>
#include <scheduler.h>
#include <sql_connect.h>
#include <sql_audit.h>
#include <debug_sync.h>


extern bool login_connection(THD *thd);
extern bool do_command(THD *thd);
extern void prepare_new_connection_state(THD* thd);
extern void end_connection(THD *thd);
extern void thd_cleanup(THD *thd);
extern void delete_thd(THD *thd);

/* Threadpool parameters */

uint threadpool_min_threads;
uint threadpool_idle_timeout;
uint threadpool_size;
uint threadpool_stall_limit;
uint threadpool_max_threads;
uint threadpool_oversubscribe;


/*
  Attach/associate the connection with the OS thread, for command processing.
*/
static inline bool thread_attach(THD* thd, char *stack_start, PSI_thread **save_psi_thread)
{
  DBUG_ENTER("thread_attach");

  if (PSI_server)
  {
    *save_psi_thread= PSI_server->get_thread();
    PSI_server->set_thread(thd->event_scheduler.m_psi);
  }
  else
    *save_psi_thread= NULL;
   
  /*
    We need to know the start of the stack so that we could check for
    stack overruns.
  */
  thd->thread_stack= stack_start;


  /* Calls close_connection() on failure */
  if (setup_connection_thread_globals(thd))
  {
    DBUG_RETURN(TRUE);
  }

  /* clear errors from processing the previous THD */
  my_errno= 0;
  thd->mysys_var->abort= 0;

#ifndef DBUG_OFF
  if (thd->event_scheduler.set_explain)
    DBUG_SET(thd->event_scheduler.dbug_explain);
#endif

  DBUG_RETURN(FALSE);
}

/*
  Detach/disassociate the connection with the OS thread.
*/
static inline void thread_detach(THD* thd, PSI_thread *restore_psi_thread)
{
  DBUG_ENTER("thread_detach");
  mysql_mutex_lock(&thd->LOCK_thd_data);
  thd->mysys_var = NULL;
  mysql_mutex_unlock(&thd->LOCK_thd_data);
#ifndef DBUG_OFF
  /*
    If during the session @@session.dbug was assigned, the
    dbug options/state has been pushed. Check if this is the
    case, to be able to restore the state when we attach this
    logical connection to a physical thread.
  */
  if (_db_is_pushed_())
  {
    thd->event_scheduler.set_explain= TRUE;
    if (DBUG_EXPLAIN(thd->event_scheduler.dbug_explain, sizeof(thd->event_scheduler.dbug_explain)))
      sql_print_error("thd_scheduler: DBUG_EXPLAIN buffer is too small");
  }
  /* DBUG_POP() is a no-op in case there is no session state */
  DBUG_POP();
#endif
  if (PSI_server)
    PSI_server->set_thread(restore_psi_thread);
  pthread_setspecific(THR_THD, NULL);
  DBUG_VOID_RETURN;
}



int threadpool_add_connection(THD *thd)
{
  int retval=1;
  PSI_thread *psi_thread;
#ifndef DBUG_OFF
  thd->event_scheduler.set_explain = 0;
#endif
  thread_attach(thd, (char *)&thd, &psi_thread);
  ulonglong now= microsecond_interval_timer();
  thd->prior_thr_create_utime= now;
  thd->start_utime= now;
  thd->thr_create_utime= now;

  if (PSI_server)
  {
    thd->event_scheduler.m_psi = 
      PSI_server->new_thread(key_thread_one_connection, thd, thd->thread_id);
    PSI_server->set_thread(thd->event_scheduler.m_psi);
  }
  
  if (setup_connection_thread_globals(thd) == 0)
  {
    if (login_connection(thd) == 0)
    {
       prepare_new_connection_state(thd);
       retval = thd_is_connection_alive(thd)?0:-1;
       thd->net.reading_or_writing= 1;
    }
  }
  thd->skip_wait_timeout= true;
  thread_detach(thd, psi_thread);
  return retval;
}

void threadpool_remove_connection(THD *thd)
{
  PSI_thread *save_psi_thread;

  thread_attach(thd, (char *)&thd, &save_psi_thread);
  thd->killed= KILL_CONNECTION;

  thd->net.reading_or_writing= 0;

  end_connection(thd);
  close_connection(thd, 0);

  mysql_mutex_lock(&thd->LOCK_thd_data);
  thd->event_scheduler.data= NULL;
  mysql_mutex_unlock(&thd->LOCK_thd_data);

  unlink_thd(thd);
  mysql_mutex_unlock(&LOCK_thread_count);
  mysql_cond_broadcast(&COND_thread_count);
  DBUG_POP();
  if (PSI_server)
    PSI_server->delete_current_thread();
  pthread_setspecific(THR_THD, NULL);
}

int threadpool_process_request(THD *thd)
{
  int retval= 0;
  PSI_thread *psi_thread;
  thread_attach(thd, (char *)&thd, &psi_thread);

  if (thd->killed >= KILL_CONNECTION)
  {
    /* 
      kill flag can be set have been killed by 
      timeout handler or by a KILL command
    */
    thread_detach(thd, psi_thread);
    return 1;
  }

  for(;;)
  {
    Vio *vio;
    thd->net.reading_or_writing= 0;
    mysql_audit_release(thd);

    if ((retval= do_command(thd)) != 0)
      break ;

    if (!thd_is_connection_alive(thd))
    {
      retval= 1;
      break;
    }

    vio= thd->net.vio;
    if (!vio->has_data(vio))
    { 
      /*
        More info on this debug sync is in sql_parse.cc
      */
      DEBUG_SYNC(thd, "before_do_command_net_read");
      break;
    }
  }
  thread_detach(thd, psi_thread);
  if (!retval)
    thd->net.reading_or_writing= 1;
  return retval;
}


/*
  Scheduler struct, individual functions are implemented
  in threadpool_unix.cc or threadpool_win.cc
*/

extern bool tp_init();
extern void tp_add_connection(THD*);
extern void tp_wait_begin(THD *, int);
extern void tp_wait_end(THD*);
extern void tp_post_kill_notification(THD *thd);
extern void tp_end(void);

static scheduler_functions tp_scheduler_functions=
{
  0,                                  // max_threads
  NULL,
  NULL,
  tp_init,                            // init
  NULL,                               // init_new_connection_thread
  tp_add_connection,                  // add_connection
  tp_wait_begin,                      // thd_wait_begin
  tp_wait_end,                        // thd_wait_end
  tp_post_kill_notification,          // post_kill_notification
  NULL,                               // end_thread
  tp_end                              // end
};

extern void scheduler_init();

void pool_of_threads_scheduler(struct scheduler_functions *func,
    ulong *arg_max_connections,
    uint *arg_connection_count)
{
  *func = tp_scheduler_functions;
  func->max_threads= *arg_max_connections + 1;
  func->max_connections= arg_max_connections;
  func->connection_count= arg_connection_count;
  scheduler_init();
}