1 /*
2  * Copyright (C) 2008 - 2011 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19 #include <libgda/libgda.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include "common.h"
23 
24 #define NTHREADS 10
25 #define DBNAME "testdb"
26 #define DBDIR "."
27 
28 #define DEBUG_PRINT
29 #undef DEBUG_PRINT
30 
31 /*
32  * tests
33  */
34 typedef gboolean (*TestFunc) (GError **);
35 static gboolean test1 (GError **error);
36 
37 TestFunc tests[] = {
38 	test1,
39 };
40 
41 int
42 main (int argc, char** argv)
43 {
44 	gchar *fname;
45 	GError *error = NULL;
46 
47 #if GLIB_CHECK_VERSION(2,36,0)
48 #else
49 	g_type_init ();
50 #endif
51 	gda_init ();
52 
53 	/* set up the test database */
54 	fname = g_build_filename (ROOT_DIR, "tests", "multi-threading", "testdb.sql", NULL);
55 	if (!create_sqlite_db (DBDIR, DBNAME, fname, &error)) {
56 		g_print ("Cannot create test database: %s\n", error && error->message ?
57 			 error->message : "no detail");
58 		return 1;
59 	}
60 	g_free (fname);
61 
62 	guint failures = 0;
63 	guint j, ntests = 0;;
64 	for (j = 0; j < 500; j++) {
65 		guint i;
66 
67 #ifdef DEBUG_PRINT
68 		g_print ("================================================== test %d\n", j);
69 #else
70 		g_print (".");
71 		fflush (stdout);
72 #endif
73 		for (i = 0; i < sizeof (tests) / sizeof (TestFunc); i++) {
74 			GError *error = NULL;
75 			if (! tests[i] (&error)) {
76 				g_print ("Test %d failed: %s\n", i+1,
77 					 error && error->message ? error->message : "No detail");
78 				if (error)
79 					g_error_free (error);
80 				failures ++;
81 			}
82 			ntests ++;
83 		}
84 	}
85 
86 	g_print ("\nTESTS COUNT: %d\n", ntests);
87 	g_print ("FAILURES: %d\n", failures);
88 
89 	return failures != 0 ? 1 : 0;
90 }
91 
92 typedef struct _ThData {
93 	GMutex        *start_lock;
94 	GThread       *thread;
95 	GdaConnection *cnc;
96 	gint           th_id;
97 	gboolean       error;
98 } ThData;
99 
100 static gboolean
101 test_multiple_threads (GThreadFunc func, GError **error)
102 {
103 	ThData data[NTHREADS];
104 	gint i;
105 	GdaConnection *cnc = NULL;
106 
107 	/* open cnc */
108 	gchar *cnc_string;
109 	gchar *edir, *edbname;
110 
111 	edir = gda_rfc1738_encode (DBDIR);
112 	edbname = gda_rfc1738_encode (DBNAME);
113 	cnc_string = g_strdup_printf ("DB_DIR=%s;DB_NAME=%s", edir, edbname);
114 	g_free (edir);
115 	g_free (edbname);
116 	cnc = gda_connection_open_from_string ("SQLite", cnc_string, NULL,
117 					       GDA_CONNECTION_OPTIONS_NONE, NULL);
118 	g_free (cnc_string);
119 
120 	if (!cnc)
121 		return FALSE;
122 	g_object_set (G_OBJECT (cnc), "thread-owner", g_thread_self (), NULL);
123 
124 	/* prepare threads data */
125 	for (i = 0; i < NTHREADS; i++) {
126 		ThData *d = &(data[i]);
127 		d->start_lock = g_mutex_new ();
128 		g_mutex_lock (d->start_lock);
129 		d->thread = NULL;
130 		d->cnc = cnc;
131 		d->th_id = i;
132 		d->error = FALSE;
133 	}
134 
135 	/* start all the threads, they will lock on d->start_lock */
136 	for (i = 0; i < NTHREADS; i++) {
137 		ThData *d = &(data[i]);
138 #ifdef DEBUG_PRINT
139 		g_print ("Running thread %d\n", d->th_id);
140 #endif
141 		d->thread = g_thread_create (func, d, TRUE, NULL);
142 #ifdef DEBUG_PRINT
143 		g_print ("Running thread %d has pointer %p\n", d->th_id, d->thread);
144 #endif
145 	}
146 
147 	/* unlock all the threads */
148 	for (i = 0; i < NTHREADS; i++) {
149 		ThData *d = &(data[i]);
150 		g_mutex_unlock (d->start_lock);
151 	}
152 
153 	gboolean retval = TRUE;
154 	for (i = 0; i < NTHREADS; i++) {
155 		ThData *d = &(data[i]);
156 		g_object_set (G_OBJECT (cnc), "thread-owner", d->thread, NULL);
157 		g_thread_join (d->thread);
158 		if (d->error)
159 			retval = FALSE;
160 	}
161 
162 	for (i = 0; i < NTHREADS; i++) {
163 		ThData *d = &(data[i]);
164 		g_mutex_free (d->start_lock);
165 	}
166 
167 	g_object_unref (cnc);
168 
169 	return retval;
170 }
171 
172 /*
173  * Run SELECT on the same connection
174  */
175 gpointer
176 test1_start_thread (ThData *data)
177 {
178 	/* initially start locked */
179 	g_mutex_lock (data->start_lock);
180 	g_mutex_unlock (data->start_lock);
181 
182 	/* threads use @cnc */
183 	if (gda_lockable_trylock ((GdaLockable*) data->cnc)) {
184 #ifdef DEBUG_PRINT
185 		g_print ("Th %d has tried to lock cnc and succeeded\n", data->th_id);
186 #endif
187 	}
188 	else {
189 #ifdef DEBUG_PRINT
190 		g_print ("Th %d has tried to lock cnc and failed, locking it (may block)...\n", data->th_id);
191 #endif
192 		gda_lockable_lock ((GdaLockable*) data->cnc);
193 	}
194 	gda_lockable_unlock ((GdaLockable*) data->cnc);
195 #ifdef DEBUG_PRINT
196 	g_print ("Th %d has unlocked cnc\n", data->th_id);
197 #endif
198 
199 	gda_lockable_lock ((GdaLockable*) data->cnc);
200 #ifdef DEBUG_PRINT
201 	g_print ("Th %d has locked cnc\n", data->th_id);
202 #endif
203 
204 	g_thread_yield ();
205 
206 	gda_lockable_lock ((GdaLockable*) data->cnc);
207 #ifdef DEBUG_PRINT
208 	g_print ("Th %d has re-locked cnc\n", data->th_id);
209 #endif
210 	g_thread_yield ();
211 
212 	gda_lockable_unlock ((GdaLockable*) data->cnc);
213 #ifdef DEBUG_PRINT
214 	g_print ("Th %d has unlocked cnc\n", data->th_id);
215 #endif
216 	g_thread_yield ();
217 
218 	gda_lockable_unlock ((GdaLockable*) data->cnc);
219 #ifdef DEBUG_PRINT
220 	g_print ("Th %d has re-unlocked cnc\n", data->th_id);
221 #endif
222 	g_thread_yield ();
223 
224 
225 #ifdef DEBUG_PRINT
226 	g_print ("Th %d finished\n", data->th_id);
227 #endif
228 	return NULL;
229 }
230 
231 static gboolean
232 test1 (GError **error)
233 {
234 	return test_multiple_threads ((GThreadFunc) test1_start_thread, error);
235 }
236