2 * test-conc-hashtable.c: Unit test for the concurrent hashtable.
4 * Copyright (C) 2014 Xamarin Inc
6 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
11 #include "utils/mono-threads.h"
12 #include "utils/mono-conc-hashtable.h"
13 #include "utils/checked-build.h"
14 #include "metadata/w32handle.h"
24 single_writer_single_reader (void)
27 MonoConcurrentHashTable *h;
30 mono_os_mutex_init (&mutex);
31 h = mono_conc_hashtable_new (NULL, NULL);
33 mono_os_mutex_lock (&mutex);
34 mono_conc_hashtable_insert (h, GUINT_TO_POINTER (10), GUINT_TO_POINTER (20));
35 mono_os_mutex_unlock (&mutex);
37 mono_os_mutex_lock (&mutex);
38 mono_conc_hashtable_insert (h, GUINT_TO_POINTER (30), GUINT_TO_POINTER (40));
39 mono_os_mutex_unlock (&mutex);
41 mono_os_mutex_lock (&mutex);
42 mono_conc_hashtable_insert (h, GUINT_TO_POINTER (50), GUINT_TO_POINTER (60));
43 mono_os_mutex_unlock (&mutex);
45 mono_os_mutex_lock (&mutex);
46 mono_conc_hashtable_insert (h, GUINT_TO_POINTER (2), GUINT_TO_POINTER (3));
47 mono_os_mutex_unlock (&mutex);
49 if (mono_conc_hashtable_lookup (h, GUINT_TO_POINTER (30)) != GUINT_TO_POINTER (40))
51 if (mono_conc_hashtable_lookup (h, GUINT_TO_POINTER (10)) != GUINT_TO_POINTER (20))
53 if (mono_conc_hashtable_lookup (h, GUINT_TO_POINTER (2)) != GUINT_TO_POINTER (3))
55 if (mono_conc_hashtable_lookup (h, GUINT_TO_POINTER (50)) != GUINT_TO_POINTER (60))
58 mono_conc_hashtable_destroy (h);
59 mono_os_mutex_destroy (&mutex);
61 printf ("SERIAL TEST FAILED %d\n", res);
65 static MonoConcurrentHashTable *hash;
66 static mono_mutex_t global_mutex;
69 pw_sr_thread (void *arg)
71 int i, idx = 1000 * GPOINTER_TO_INT (arg);
72 mono_thread_info_attach ();
74 for (i = 0; i < 1000; ++i) {
75 mono_os_mutex_lock (&global_mutex);
76 mono_conc_hashtable_insert (hash, GINT_TO_POINTER (i + idx), GINT_TO_POINTER (i + 1));
77 mono_os_mutex_unlock (&global_mutex);
83 parallel_writer_single_reader (void)
88 mono_os_mutex_init (&global_mutex);
89 hash = mono_conc_hashtable_new (NULL, NULL);
91 pthread_create (&a, NULL, pw_sr_thread, GINT_TO_POINTER (1));
92 pthread_create (&b, NULL, pw_sr_thread, GINT_TO_POINTER (2));
93 pthread_create (&c, NULL, pw_sr_thread, GINT_TO_POINTER (3));
95 pthread_join (a, NULL);
96 pthread_join (b, NULL);
97 pthread_join (c, NULL);
99 for (i = 0; i < 1000; ++i) {
100 for (j = 1; j < 4; ++j) {
101 if (mono_conc_hashtable_lookup (hash, GINT_TO_POINTER (j * 1000 + i)) != GINT_TO_POINTER (i + 1)) {
109 mono_conc_hashtable_destroy (hash);
110 mono_os_mutex_destroy (&global_mutex);
112 printf ("PAR_WRITER_SINGLE_READER TEST FAILED %d\n", res);
118 pr_sw_thread (void *arg)
120 int i = 0, idx = 100 * GPOINTER_TO_INT (arg);
121 mono_thread_info_attach ();
124 gpointer res = mono_conc_hashtable_lookup (hash, GINT_TO_POINTER (i + idx + 1));
127 if (res != GINT_TO_POINTER ((i + idx) * 2 + 1))
128 return GINT_TO_POINTER (i);
135 single_writer_parallel_reader (void)
140 ra = rb = rc = GINT_TO_POINTER (1);
142 mono_os_mutex_init (&global_mutex);
143 hash = mono_conc_hashtable_new (NULL, NULL);
145 pthread_create (&a, NULL, pr_sw_thread, GINT_TO_POINTER (0));
146 pthread_create (&b, NULL, pr_sw_thread, GINT_TO_POINTER (1));
147 pthread_create (&c, NULL, pr_sw_thread, GINT_TO_POINTER (2));
149 for (i = 0; i < 100; ++i) {
150 mono_os_mutex_lock (&global_mutex);
151 mono_conc_hashtable_insert (hash, GINT_TO_POINTER (i + 0 + 1), GINT_TO_POINTER ((i + 0) * 2 + 1));
152 mono_os_mutex_unlock (&global_mutex);
154 mono_os_mutex_lock (&global_mutex);
155 mono_conc_hashtable_insert (hash, GINT_TO_POINTER (i + 100 + 1), GINT_TO_POINTER ((i + 100) * 2 + 1));
156 mono_os_mutex_unlock (&global_mutex);
158 mono_os_mutex_lock (&global_mutex);
159 mono_conc_hashtable_insert (hash, GINT_TO_POINTER (i + 200 + 1), GINT_TO_POINTER ((i + 200) * 2 + 1));
160 mono_os_mutex_unlock (&global_mutex);
163 pthread_join (a, &ra);
164 pthread_join (b, &rb);
165 pthread_join (c, &rc);
166 res = GPOINTER_TO_INT (ra) + GPOINTER_TO_INT (rb) + GPOINTER_TO_INT (rc);
168 mono_conc_hashtable_destroy (hash);
169 mono_os_mutex_destroy (&global_mutex);
171 printf ("SINGLE_WRITER_PAR_READER TEST FAILED %d\n", res);
178 pw_pr_r_thread (void *arg)
181 mono_thread_info_attach ();
183 /* i will not be incremented as long as running is set to 1, this guarantee that
184 we loop over all the keys at least once after the writer threads have finished */
185 for (i = 0; i < 2; i += 1 - running) {
186 for (key = 1; key < 3 * 1000 + 1; key++) {
187 val = GPOINTER_TO_INT (mono_conc_hashtable_lookup (hash, GINT_TO_POINTER (key)));
192 return GINT_TO_POINTER (key);
199 pw_pr_w_add_thread (void *arg)
201 int i, idx = 1000 * GPOINTER_TO_INT (arg);
203 mono_thread_info_attach ();
205 for (i = idx; i < idx + 1000; i++) {
206 mono_os_mutex_lock (&global_mutex);
207 mono_conc_hashtable_insert (hash, GINT_TO_POINTER (i + 1), GINT_TO_POINTER (i + 1));
208 mono_os_mutex_unlock (&global_mutex);
214 pw_pr_w_del_thread (void *arg)
216 int i, idx = 1000 * GPOINTER_TO_INT (arg);
218 mono_thread_info_attach ();
220 for (i = idx; i < idx + 1000; i++) {
221 mono_os_mutex_lock (&global_mutex);
222 mono_conc_hashtable_remove (hash, GINT_TO_POINTER (i + 1));
223 mono_os_mutex_unlock (&global_mutex);
229 parallel_writer_parallel_reader (void)
231 pthread_t wa, wb, wc, ra, rb, rc;
237 mono_os_mutex_init (&global_mutex);
238 hash = mono_conc_hashtable_new (NULL, NULL);
240 for (i = 0; i < 2; i++) {
243 pthread_create (&ra, NULL, pw_pr_r_thread, NULL);
244 pthread_create (&rb, NULL, pw_pr_r_thread, NULL);
245 pthread_create (&rc, NULL, pw_pr_r_thread, NULL);
249 pthread_create (&wa, NULL, pw_pr_w_add_thread, GINT_TO_POINTER (0));
250 pthread_create (&wb, NULL, pw_pr_w_add_thread, GINT_TO_POINTER (1));
251 pthread_create (&wc, NULL, pw_pr_w_add_thread, GINT_TO_POINTER (2));
254 pthread_create (&wa, NULL, pw_pr_w_del_thread, GINT_TO_POINTER (0));
255 pthread_create (&wb, NULL, pw_pr_w_del_thread, GINT_TO_POINTER (1));
256 pthread_create (&wc, NULL, pw_pr_w_del_thread, GINT_TO_POINTER (2));
260 pthread_join (wa, NULL);
261 pthread_join (wb, NULL);
262 pthread_join (wc, NULL);
266 pthread_join (ra, &a);
267 pthread_join (rb, &b);
268 pthread_join (rc, &c);
270 res += GPOINTER_TO_INT (a) + GPOINTER_TO_INT (b) + GPOINTER_TO_INT (c);
274 printf ("PAR_WRITER_PAR_READER TEST FAILED %d %d %d\n", GPOINTER_TO_INT (a), GPOINTER_TO_INT (b), GPOINTER_TO_INT (c));
276 mono_conc_hashtable_destroy (hash);
277 mono_os_mutex_destroy (&global_mutex);
282 static void G_GNUC_UNUSED
283 benchmark_conc (void)
285 MonoConcurrentHashTable *h;
288 h = mono_conc_hashtable_new (NULL, NULL);
290 for (i = 1; i < 10 * 1000; ++i) {
291 mono_conc_hashtable_insert (h, GUINT_TO_POINTER (i), GUINT_TO_POINTER (i));
295 for (j = 0; j < 100000; ++j)
296 for (i = 1; i < 10 * 105; ++i)
297 mono_conc_hashtable_lookup (h, GUINT_TO_POINTER (i));
299 mono_conc_hashtable_destroy (h);
302 static void G_GNUC_UNUSED
303 benchmark_glib (void)
308 h = g_hash_table_new (NULL, NULL);
310 for (i = 1; i < 10 * 1000; ++i)
311 g_hash_table_insert (h, GUINT_TO_POINTER (i), GUINT_TO_POINTER (i));
314 for (j = 0; j < 100000; ++j)
315 for (i = 1; i < 10 * 105; ++i)
316 g_hash_table_lookup (h, GUINT_TO_POINTER (i));
318 g_hash_table_destroy (h);
322 thread_state_init (MonoThreadUnwindState *ctx)
330 MonoThreadInfoRuntimeCallbacks ticallbacks;
333 CHECKED_MONO_INIT ();
334 mono_thread_info_init (sizeof (MonoThreadInfo));
335 memset (&ticallbacks, 0, sizeof (ticallbacks));
336 ticallbacks.thread_state_init = thread_state_init;
337 mono_thread_info_runtime_init (&ticallbacks);
339 mono_w32handle_init ();
342 mono_thread_info_attach ();
344 // benchmark_conc ();
345 // benchmark_glib ();
347 res += single_writer_single_reader ();
348 res += parallel_writer_single_reader ();
349 res += single_writer_parallel_reader ();
350 res += parallel_writer_parallel_reader ();