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
main(int argc,char ** argv)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
test_multiple_threads(GThreadFunc func,GError ** error)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
test1_start_thread(ThData * data)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
test1(GError ** error)232 test1 (GError **error)
233 {
234 return test_multiple_threads ((GThreadFunc) test1_start_thread, error);
235 }
236