Guest User

Untitled

a guest
Dec 6th, 2018
80
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 8.68 KB | None | 0 0
  1. #include <stdlib.h>
  2. #include <string.h>
  3. #include <stdbool.h>
  4. #include <mysql/mysql.h>
  5. #include <event.h>
  6.  
  7. static const char *CONFIG_HOST = "localhost";
  8. static const char *CONFIG_USER = "root";
  9. static const char *CONFIG_PASSWORD = "***";
  10. static const char *CONFIG_DATABASE = "***";
  11. static const int CONFIG_NUM_CONNECTIONS = 10;
  12.  
  13. enum mysql_connection_state {
  14.     CONNECT_START,
  15.     CONNECT_WAITING,
  16.     CONNECT_DONE,
  17.  
  18.     QUERY_START,
  19.     QUERY_WAITING,
  20.     QUERY_RESULT_READY,
  21.  
  22.     FETCH_ROW_START,
  23.     FETCH_ROW_WAITING,
  24.     FETCH_ROW_RESULT_READY,
  25.  
  26.     CLOSE_START,
  27.     CLOSE_WAITING,
  28.     CLOSE_DONE
  29. };
  30.  
  31. struct mysql_connection {
  32.     enum mysql_connection_state state;
  33.     MYSQL mysql;
  34.     MYSQL *ret;
  35.     MYSQL_RES *result;
  36.     MYSQL_ROW row;
  37.     struct event mysql_event;
  38.     struct query_entry *current_query_entry;
  39.     int err;
  40.     int index;
  41. };
  42.  
  43. struct query_entry {
  44.     struct query_entry *next;
  45.     char *query;
  46.     int index;
  47. };
  48.  
  49. static struct query_entry *g_query_list;
  50. static struct query_entry **g_tail_ptr = &g_query_list;
  51. static int g_active_connection_num = 0;
  52. static int g_query_counter = 0;
  53.  
  54. static void connection_state_loop(int fd, short event, void *data);
  55.  
  56. static void fatal(struct mysql_connection *conn, const char *msg) {
  57.     fprintf(stderr, "%s: %s\n", msg, (conn ? mysql_error(&conn->mysql) : ""));
  58.     exit(1);
  59. }
  60.  
  61. static inline int add_event(int status, struct mysql_connection *conn) {
  62.     short wait_event= 0;
  63.     if (status & MYSQL_WAIT_READ) {
  64.         wait_event|= EV_READ;
  65.     }
  66.     if (status & MYSQL_WAIT_WRITE) {
  67.         wait_event|= EV_WRITE;
  68.     }
  69.  
  70.     int fd = -1;
  71.     if (wait_event) {
  72.         fd = mysql_get_socket(&conn->mysql);
  73.     }
  74.  
  75.     struct timeval tv, *ptv = NULL;
  76.     if (status & MYSQL_WAIT_TIMEOUT) {
  77.         tv.tv_sec = mysql_get_timeout_value(&conn->mysql);
  78.         tv.tv_usec = 0;
  79.         ptv = &tv;
  80.     }
  81.  
  82.     event_set(&conn->mysql_event, fd, wait_event, connection_state_loop, conn);
  83.     event_add(&conn->mysql_event, ptv);
  84.  
  85.     return 0;
  86. }
  87.  
  88. static int mysql_status(short event) {
  89.     int status= 0;
  90.     if (event & EV_READ) {
  91.         status |= MYSQL_WAIT_READ;
  92.     }
  93.     if (event & EV_WRITE) {
  94.         status |= MYSQL_WAIT_WRITE;
  95.     }
  96.     if (event & EV_TIMEOUT) {
  97.         status |= MYSQL_WAIT_TIMEOUT;
  98.     }
  99.  
  100.     return status;
  101. }
  102.  
  103. static int connection_decide_next_state(int need_wait, struct mysql_connection *connection, int state_wait, int state_go_on) {
  104.     if (need_wait) {
  105.         connection->state = state_wait;
  106.         add_event(need_wait, connection);  
  107.         return 0;
  108.     } else {        
  109.         connection->state = state_go_on;
  110.         return 1;
  111.     }
  112. }
  113.  
  114. static bool connection_state_run(struct mysql_connection *connection, short event) {
  115.     bool running = true;
  116.     switch (connection->state) {
  117.         int status;
  118.         case CONNECT_START:
  119.             status = mysql_real_connect_start(&connection->ret, &connection->mysql, CONFIG_HOST, CONFIG_USER, CONFIG_PASSWORD, CONFIG_DATABASE, 0, NULL, 0);
  120.             running = connection_decide_next_state(status, connection, CONNECT_WAITING, CONNECT_DONE);  
  121.             break;
  122.  
  123.         case CONNECT_WAITING:
  124.             status = mysql_real_connect_cont(&connection->ret, &connection->mysql, mysql_status(event));
  125.             running = connection_decide_next_state(status, connection, CONNECT_WAITING, CONNECT_DONE);  
  126.             break;
  127.  
  128.         case CONNECT_DONE:
  129.             if (!connection->ret) {
  130.                 fatal(connection, "Failed to mysql_real_connect()");
  131.             }
  132.             connection->state = QUERY_START;
  133.             break;
  134.  
  135.         case QUERY_START:
  136.             connection->current_query_entry = g_query_list;
  137.             if (!connection->current_query_entry) {  
  138.                 connection->state = CLOSE_START;  
  139.                 break;
  140.             }
  141.             g_query_list = g_query_list->next;
  142.             status= mysql_real_query_start(&connection->err, &connection->mysql, connection->current_query_entry->query, strlen(connection->current_query_entry->query));
  143.             running = connection_decide_next_state(status, connection, QUERY_WAITING, QUERY_RESULT_READY);  
  144.             break;
  145.  
  146.         case QUERY_WAITING:
  147.             status = mysql_real_query_cont(&connection->err, &connection->mysql, mysql_status(event));
  148.             running = connection_decide_next_state(status, connection, QUERY_WAITING, QUERY_RESULT_READY);
  149.             break;
  150.  
  151.         case QUERY_RESULT_READY:
  152.             if (connection->err) {
  153.                 printf("%d | Error: %s\n", connection->index, mysql_error(&connection->mysql));
  154.                 connection->state = CONNECT_DONE;
  155.                 break;
  156.             }
  157.             free(connection->current_query_entry->query);
  158.             free(connection->current_query_entry);
  159.             connection->result = mysql_use_result(&connection->mysql);
  160.             connection->state = (connection->result) ? FETCH_ROW_START : QUERY_START;
  161.             break;
  162.  
  163.         case FETCH_ROW_START:
  164.             status = mysql_fetch_row_start(&connection->row, connection->result);
  165.             running = connection_decide_next_state(status, connection, FETCH_ROW_WAITING, FETCH_ROW_RESULT_READY);
  166.             break;
  167.  
  168.         case FETCH_ROW_WAITING:
  169.             status= mysql_fetch_row_cont(&connection->row, connection->result, mysql_status(event));
  170.             running = connection_decide_next_state(status, connection, FETCH_ROW_WAITING, FETCH_ROW_RESULT_READY);
  171.             break;
  172.  
  173.         case FETCH_ROW_RESULT_READY:
  174.             if (!connection->row) {
  175.                 if (mysql_errno(&connection->mysql)) {
  176.                     printf("%d | Error: %s\n", connection->index, mysql_error(&connection->mysql));
  177.                 } else {
  178.                     // EOF. No more rows
  179.                 }
  180.                 mysql_free_result(connection->result);
  181.                 connection->state = QUERY_START;
  182.                 break;
  183.             }
  184.  
  185.             /*
  186.             // print fields in the row
  187.             printf("%d %d", connection->index, connection->current_query_entry->index);
  188.             for (int i= 0; i < mysql_num_fields(connection->result); i++) {
  189.                 printf("%s%s", (i ? "\t" : ""), (connection->row[i] ? connection->row[i] : "(null)"));
  190.             }
  191.             printf("\n");
  192.             */
  193.  
  194.             connection->state = FETCH_ROW_START;
  195.             break;
  196.  
  197.         case CLOSE_START:
  198.             status = mysql_close_start(&connection->mysql);
  199.             running = connection_decide_next_state(status, connection, CLOSE_WAITING, CLOSE_DONE);  
  200.             break;
  201.  
  202.         case CLOSE_WAITING:
  203.             status= mysql_close_cont(&connection->mysql, mysql_status(event));
  204.             running = connection_decide_next_state(status, connection, CLOSE_WAITING, CLOSE_DONE);  
  205.             break;
  206.  
  207.         case CLOSE_DONE:
  208.             g_active_connection_num--;
  209.             if (g_active_connection_num == 0) {
  210.                 event_loopbreak();
  211.             }
  212.             running = false;
  213.             break;
  214.  
  215.         default:
  216.             abort();
  217.             break;
  218.     }
  219.  
  220.     return running;
  221. }
  222.  
  223. static void connection_state_loop(int fd , short event, void *data) {
  224.     while (connection_state_run((struct mysql_connection *)data, event));
  225. }
  226.  
  227. void add_query(const char *query) {
  228.     struct query_entry *entry = (struct query_entry *)malloc(sizeof(*entry));
  229.     if (!entry) {
  230.         fatal(NULL, "Out of memory");
  231.     }
  232.     entry->query = (char *)malloc(strlen(query) + 1);
  233.     if (!entry->query) {
  234.         fatal(NULL, "Out of memory");
  235.     }
  236.  
  237.     strcpy(entry->query, query);
  238.     entry->next = NULL;
  239.     entry->index = g_query_counter++;
  240.     *g_tail_ptr = entry;
  241.     g_tail_ptr = &entry->next;
  242. }
  243.  
  244.  
  245. int main(int argc, char *argv[]) {
  246.     struct mysql_connection *connections = (struct mysql_connection *)calloc(CONFIG_NUM_CONNECTIONS, sizeof(*connections));
  247.     if (!connections) {
  248.         fatal(NULL, "Out of memory");
  249.     }
  250.  
  251.     struct event_base *libevent_base = event_init();
  252.  
  253.     for (int i = 0; i < CONFIG_NUM_CONNECTIONS; i++) {
  254.         mysql_init(&connections[i].mysql);
  255.         mysql_options(&connections[i].mysql, MYSQL_OPT_NONBLOCK, NULL);
  256.         //mysql_options(&connections[i].mysql, MYSQL_READ_DEFAULT_GROUP, "async_queries");
  257.  
  258.         connections[i].state = CONNECT_START;
  259.         connections[i].index = i;
  260.         g_active_connection_num++;
  261.         connection_state_loop(-1, -1, &connections[i]);
  262.     }
  263.  
  264.     for (int i = 0; i < 10000; i++) {
  265.         add_query("SELECT * FROM bans;");
  266.     }
  267.  
  268.     event_dispatch();
  269.  
  270.     free(connections);
  271.     event_base_free(libevent_base);
  272.  
  273.     return 0;
  274. }
Add Comment
Please, Sign In to add comment