Description:
We think we've found another bug in Embedded mySql; we've used the version of libmysqld.dll patched for bug #1959.
We've used the same test program of submission #1959, slightly modified.
The program does theese tasks:
- it creates a database and a table;
- it spawns N (i.e. 50) "inserter threads", each of which continously performs INSERT queries on the table;
- it spawns N "remover threads", each of which continously performs DELETE queries on the table;
- the main thread continously performs UPDATE queries on the table.
The program is the same of the #1959 submission with this difference: each thread opens the connection before executing the query, and closes the connection after having executed it.
Two behaviours can be observer:
- if mysql_thread_end is called after mysql_close, while the program runs the amount of used memory quickly grows up and after a while an access violation is raised (the error message is: "Unhandled exception in MySqlTest.exe (NTDLL:DLL) 0xC00000005: Access Violation.").
- if mysql_thread_end is not called, the program runs without any access violation, but it seems that there is some lack of memory since the amount of used memory continues to grow up.
So my question is: if a thread opens and closes a connection each time it wants to perform a query, it has or has not to call mysql_thread_end each time it closes the connection?
Thank you,
Cristiano Muzi
How to repeat:
Here is the test code:
--------------------------------------------------------------------------------
// mySqlTest.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
// This function creates the database and the test table.
void createDatabaseAndTable(void);
// This function connects to the database.
MYSQL* dbConnect(void);
// This function terminates the program, priting an error message.
void die(const char* errorMsg, const char* mysqlErrorMsg = NULL);
// How many threads per type are created.
#define threadCount 50
// Handles of threads.
HANDLE inserterThreadList[threadCount], removerThreadList[threadCount];
// Main procedure of the inserter threads.
DWORD WINAPI inserterThreadProc(LPVOID lpParameter);
// Main procedure of the remover threads.
DWORD WINAPI removerThreadProc(LPVOID lpParameter);
const char *args[] = {"Ignored line", NULL};
int main(int argc, char* argv[])
{
printf("Starting test program\n");
// Initializes the server.
if (mysql_server_init(1, (char**)args, NULL))
die("mysql_server_init error");
printf("mysql_server_init OK\n");
// Creates the dabase and the table.
createDatabaseAndTable();
printf("Database and table created.\n");
// Creates the remover threads.
for (int i = 0; i < threadCount; i++)
removerThreadList[i] = CreateThread(NULL, 0, *removerThreadProc, NULL, 0, NULL);
printf("Remover threads created.\n");
// Creates the inserter threads.
for (i = 0; i < threadCount; i++)
inserterThreadList[i] = CreateThread(NULL, 0, *inserterThreadProc, NULL, 0, NULL);
printf("Inserter threads created.\n");
// Repeatedly changes the value of the "passed" flag into the table.
for (;;) {
MYSQL *mySql = dbConnect();
if (mysql_query(mySql, "UPDATE testTable SET passed = 1 WHERE passed = 0"))
die("UPDATE query error", mysql_error(mySql));
mysql_close(mySql);
mysql_thread_end();
}
// Closes the server.
mysql_server_end();
return 0;
}
DWORD WINAPI inserterThreadProc(LPVOID lpParameter)
{
// Repeatedly insert rows.
for (;;) {
MYSQL *mySql = dbConnect();
if (mysql_query(mySql,
"INSERT INTO testTable (name, surname, passed) VALUES (\"Mark\", \"Twain\", 0)"))
die("inserterThread error", mysql_error(mySql));
mysql_close(mySql);
mysql_thread_end();
}
}
DWORD WINAPI removerThreadProc(LPVOID lpParameter)
{
// Repeatedly insert rows.
for (;;) {
MYSQL *mySql = dbConnect();
if (mysql_query(mySql,
"DELETE FROM testTable WHERE passed = 1"))
die("inserterThread error", mysql_error(mySql));
mysql_close(mySql);
mysql_thread_end();
}
}
MYSQL* dbConnect(void)
{
MYSQL *mySql = mysql_init(NULL);
if (!mySql)
die("mysql_init error");
if (!mysql_real_connect(mySql, NULL, NULL, NULL, "testDB", 0, NULL, 0))
die("mysql_real_connect error", mysql_error(mySql));
return mySql;
}
void createDatabaseAndTable(void)
{
MYSQL *mySql = mysql_init(NULL);
if (!mySql)
die("mysql_init error");
if (!mysql_real_connect(mySql, NULL, NULL, NULL, NULL, 0, NULL, 0))
die("mysql_real_connect error");
if (mysql_query(mySql, "CREATE DATABASE IF NOT EXISTS testDB"))
die("mysql_query error creating the database.", mysql_error(mySql));
mysql_close(mySql);
mySql = dbConnect();
// Defininf the table without indices will arise other types of error.
if (mysql_query(mySql,
"CREATE TABLE IF NOT EXISTS testTable (\
id INTEGER NOT NULL AUTO_INCREMENT, \
name TEXT NOT NULL, \
surname TEXT NOT NULL, \
passed TINYINT, \
PRIMARY KEY(id))" ))
die("mysql_query error creating table.", mysql_error(mySql));
mysql_close(mySql);
}
void die(const char* errorMsg, const char* mysqlErrorMsg)
{
if (mysqlErrorMsg)
printf("### DIE\n%s\n%s\n", errorMsg, mysqlErrorMsg);
else
printf("### DIE\n%s\n", errorMsg);
exit(EXIT_FAILURE);
}
-------------------------------------------------------------------------------