You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
5.1 KiB

13 years ago
13 years ago
APC thread per connection impl To have a guaranteed wakeup with no lost signal, the folowing approaches are applied on different platforms: * Linux/FreeBSD: This applies to any platform with _GNU_SOURCE defined.   1. set a signal mask to ignore the signal (namely, SIGUSR1. It is better   not to interfer with the signals that can be used by OS)   2. call ppoll instead of poll, that applies the signal mask atomically   with polling.   3. hopefully, if the signal is received before the ppoll call   (i.e. masked out) will be still set to pending, so the next ppoll call   (as well as any unmasking) will force it to be handled. We will receive   EINTR and the read will be retried. * Solaris, other POSIX There is no ppoll function on Solaris, but it has differently handled opened files limitation, which allows to freely open new file descriptors as needed. Here we will use the self-pipe trick to wake up:   1. A thread-local pipe is created. Its read end will be additionally used   by poll.   2. A signal handler will write to this pipe to wake up from poll. Linux is not safe for using this method, because its file limits are rather low, especially the default ones. Solaris has the per-process restriction rather than per-user: https://support.oracle.com/knowledge/Sun%20Microsystems/1005979_1.html > A process can have up to rlim_fd_cur file descriptors and can increase > the number up to rlim_fd_max. The latter is 1024 on Solaris 7 by default. We can't say about other POSIX'es limitations, but it is assumed they have no ppoll function. pselect is not assumed because of the silly limitations. So the only way for them can be self-piping. * Windows Self-pipe trick is used as well, but instead of poll, select is used, and QueueUserAPC is used instead of signals. Caveats: * It is possible that the connection will hang on write for long.   The requestor should wait until the write ends, or fail with timeout. * The connection can hang in other commands for long, for example, on   sleep(). We generally can't assume it is safe to handle the apc during both of these cases, but maybe some safe zones can be marked. Anyway it is considered to be out of the scope of this task.
4 years ago
14 years ago
13 years ago
  1. #ifndef SQL_MY_APC_INCLUDED
  2. #define SQL_MY_APC_INCLUDED
  3. /*
  4. Copyright (c) 2011, 2013 Monty Program Ab.
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; version 2 of the License.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  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 Street, Fifth Floor, Boston, MA 02110-1335 USA */
  15. /*
  16. Interface
  17. ~~~~~~~~~
  18. (
  19. - This is an APC request queue
  20. - We assume there is a particular owner thread which periodically calls
  21. process_apc_requests() to serve the call requests.
  22. - Other threads can post call requests, and block until they are exectued.
  23. )
  24. Implementation
  25. ~~~~~~~~~~~~~~
  26. - The target has a mutex-guarded request queue.
  27. - After the request has been put into queue, the requestor waits for request
  28. to be satisfied. The worker satisifes the request and signals the
  29. requestor.
  30. */
  31. #include <atomic>
  32. class THD;
  33. /*
  34. Target for asynchronous procedure calls (APCs).
  35. - A target is running in some particular thread,
  36. - One can make calls to it from other threads.
  37. */
  38. class Apc_target
  39. {
  40. mysql_mutex_t *LOCK_thd_kill_ptr;
  41. public:
  42. Apc_target() : enabled(0), apc_calls(NULL) {}
  43. ~Apc_target() { DBUG_ASSERT(!enabled && !apc_calls);}
  44. void init(mysql_mutex_t *target_mutex);
  45. /* Destroy the target. The target must be disabled when this call is made. */
  46. void destroy() { DBUG_ASSERT(!enabled); }
  47. /* Enter ther state where the target is available for serving APC requests */
  48. void enable() { enabled++; }
  49. /*
  50. Make the target unavailable for serving APC requests.
  51. @note
  52. This call will serve all requests that were already enqueued
  53. */
  54. void disable()
  55. {
  56. DBUG_ASSERT(enabled);
  57. mysql_mutex_lock(LOCK_thd_kill_ptr);
  58. bool process= !--enabled && have_apc_requests();
  59. mysql_mutex_unlock(LOCK_thd_kill_ptr);
  60. if (unlikely(process))
  61. process_apc_requests();
  62. }
  63. void process_apc_requests();
  64. /*
  65. A lightweight function, intended to be used in frequent checks like this:
  66. if (apc_target.have_requests()) apc_target.process_apc_requests()
  67. */
  68. inline bool have_apc_requests()
  69. {
  70. return MY_TEST(apc_calls);
  71. }
  72. inline bool is_enabled() { return enabled; }
  73. /* Functor class for calls you can schedule */
  74. class Apc_call
  75. {
  76. public:
  77. /* This function will be called in the target thread */
  78. virtual void call_in_target_thread()= 0;
  79. virtual ~Apc_call() {}
  80. };
  81. class Call_request;
  82. /* Make a call in the target thread (see function definition for details) */
  83. bool make_apc_call(THD *caller_thd, Apc_call *call,
  84. int timeout_sec, bool *timed_out);
  85. void enqueue_request(Call_request *request_buff, Apc_call *call);
  86. void unenqueue_request();
  87. int wait_for_completion(THD *caller_thd, Call_request *request,
  88. int timeout_sec);
  89. #ifndef DBUG_OFF
  90. int n_calls_processed; /* Number of calls served by this target */
  91. #endif
  92. // Epoch counter that increases before the command
  93. std::atomic<longlong> epoch {0};
  94. std::atomic<longlong> process_epoch {0};
  95. private:
  96. /*
  97. Non-zero value means we're enabled. It's an int, not bool, because one can
  98. call enable() N times (and then needs to call disable() N times before the
  99. target is really disabled)
  100. */
  101. int enabled;
  102. /*
  103. Circular, double-linked list of all enqueued call requests.
  104. We use this structure, because we
  105. - process requests sequentially: requests are added at the end of the
  106. list and removed from the front. With circular list, we can keep one
  107. pointer, and access both front an back of the list with it.
  108. - a thread that has posted a request may time out (or be KILLed) and
  109. cancel the request, which means we need a fast request-removal
  110. operation.
  111. */
  112. Call_request *apc_calls;
  113. public:
  114. class Call_request
  115. {
  116. public:
  117. Apc_call *call; /* Functor to be called */
  118. /* The caller will actually wait for "processed==TRUE" */
  119. bool processed;
  120. /* Condition that will be signalled when the request has been served */
  121. mysql_cond_t COND_request;
  122. mysql_mutex_t LOCK_request;
  123. /* Double linked-list linkage */
  124. Call_request *next;
  125. Call_request *prev;
  126. const char *what; /* (debug) state of the request */
  127. Call_request();
  128. ~Call_request();
  129. };
  130. private:
  131. void enqueue_request(Call_request *qe);
  132. void dequeue_request(Call_request *qe);
  133. /* return the first call request in queue, or NULL if there are none enqueued */
  134. Call_request *get_first_in_queue()
  135. {
  136. return apc_calls;
  137. }
  138. };
  139. #ifdef HAVE_PSI_INTERFACE
  140. void init_show_explain_psi_keys(void);
  141. #else
  142. #define init_show_explain_psi_keys() /* no-op */
  143. #endif
  144. #endif //SQL_MY_APC_INCLUDED