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"
23 single_writer_single_reader (void)
26 MonoConcurrentHashTable *h;
29 mono_os_mutex_init (&mutex);
30 h = mono_conc_hashtable_new (NULL, NULL);
32 mono_os_mutex_lock (&mutex);
33 mono_conc_hashtable_insert (h, GUINT_TO_POINTER (10), GUINT_TO_POINTER (20));
34 mono_os_mutex_unlock (&mutex);
36 mono_os_mutex_lock (&mutex);
37 mono_conc_hashtable_insert (h, GUINT_TO_POINTER (30), GUINT_TO_POINTER (40));
38 mono_os_mutex_unlock (&mutex);
40 mono_os_mutex_lock (&mutex);
41 mono_conc_hashtable_insert (h, GUINT_TO_POINTER (50), GUINT_TO_POINTER (60));
42 mono_os_mutex_unlock (&mutex);
44 mono_os_mutex_lock (&mutex);
45 mono_conc_hashtable_insert (h, GUINT_TO_POINTER (2), GUINT_TO_POINTER (3));
46 mono_os_mutex_unlock (&mutex);
48 if (mono_conc_hashtable_lookup (h, GUINT_TO_POINTER (30)) != GUINT_TO_POINTER (40))
50 if (mono_conc_hashtable_lookup (h, GUINT_TO_POINTER (10)) != GUINT_TO_POINTER (20))
52 if (mono_conc_hashtable_lookup (h, GUINT_TO_POINTER (2)) != GUINT_TO_POINTER (3))
54 if (mono_conc_hashtable_lookup (h, GUINT_TO_POINTER (50)) != GUINT_TO_POINTER (60))
57 mono_conc_hashtable_destroy (h);
58 mono_os_mutex_destroy (&mutex);
60 printf ("SERIAL TEST FAILED %d\n", res);
64 static MonoConcurrentHashTable *hash;
65 static mono_mutex_t global_mutex;
68 pw_sr_thread (void *arg)
70 int i, idx = 1000 * GPOINTER_TO_INT (arg);
71 mono_thread_info_attach ((gpointer)&arg);
73 for (i = 0; i < 1000; ++i) {
74 mono_os_mutex_lock (&global_mutex);
75 mono_conc_hashtable_insert (hash, GINT_TO_POINTER (i + idx), GINT_TO_POINTER (i + 1));
76 mono_os_mutex_unlock (&global_mutex);
82 parallel_writer_single_reader (void)
87 mono_os_mutex_init (&global_mutex);
88 hash = mono_conc_hashtable_new (NULL, NULL);
90 pthread_create (&a, NULL, pw_sr_thread, GINT_TO_POINTER (1));
91 pthread_create (&b, NULL, pw_sr_thread, GINT_TO_POINTER (2));
92 pthread_create (&c, NULL, pw_sr_thread, GINT_TO_POINTER (3));
94 pthread_join (a, NULL);
95 pthread_join (b, NULL);
96 pthread_join (c, NULL);
98 for (i = 0; i < 1000; ++i) {
99 for (j = 1; j < 4; ++j) {
100 if (mono_conc_hashtable_lookup (hash, GINT_TO_POINTER (j * 1000 + i)) != GINT_TO_POINTER (i + 1)) {
108 mono_conc_hashtable_destroy (hash);
109 mono_os_mutex_destroy (&global_mutex);
111 printf ("PAR_WRITER_SINGLE_READER TEST FAILED %d\n", res);
117 pr_sw_thread (void *arg)
119 int i = 0, idx = 100 * GPOINTER_TO_INT (arg);
120 mono_thread_info_attach ((gpointer)&arg);
123 gpointer res = mono_conc_hashtable_lookup (hash, GINT_TO_POINTER (i + idx + 1));
126 if (res != GINT_TO_POINTER ((i + idx) * 2 + 1))
127 return GINT_TO_POINTER (i);
134 single_writer_parallel_reader (void)
139 ra = rb = rc = GINT_TO_POINTER (1);
141 mono_os_mutex_init (&global_mutex);
142 hash = mono_conc_hashtable_new (NULL, NULL);
144 pthread_create (&a, NULL, pr_sw_thread, GINT_TO_POINTER (0));
145 pthread_create (&b, NULL, pr_sw_thread, GINT_TO_POINTER (1));
146 pthread_create (&c, NULL, pr_sw_thread, GINT_TO_POINTER (2));
148 for (i = 0; i < 100; ++i) {
149 mono_os_mutex_lock (&global_mutex);
150 mono_conc_hashtable_insert (hash, GINT_TO_POINTER (i + 0 + 1), GINT_TO_POINTER ((i + 0) * 2 + 1));
151 mono_os_mutex_unlock (&global_mutex);
153 mono_os_mutex_lock (&global_mutex);
154 mono_conc_hashtable_insert (hash, GINT_TO_POINTER (i + 100 + 1), GINT_TO_POINTER ((i + 100) * 2 + 1));
155 mono_os_mutex_unlock (&global_mutex);
157 mono_os_mutex_lock (&global_mutex);
158 mono_conc_hashtable_insert (hash, GINT_TO_POINTER (i + 200 + 1), GINT_TO_POINTER ((i + 200) * 2 + 1));
159 mono_os_mutex_unlock (&global_mutex);
162 pthread_join (a, &ra);
163 pthread_join (b, &rb);
164 pthread_join (c, &rc);
165 res = GPOINTER_TO_INT (ra) + GPOINTER_TO_INT (rb) + GPOINTER_TO_INT (rc);
167 mono_conc_hashtable_destroy (hash);
168 mono_os_mutex_destroy (&global_mutex);
170 printf ("SINGLE_WRITER_PAR_READER TEST FAILED %d\n", res);
177 pw_pr_r_thread (void *arg)
180 mono_thread_info_attach ((gpointer)&arg);
182 /* i will not be incremented as long as running is set to 1, this guarantee that
183 we loop over all the keys at least once after the writer threads have finished */
184 for (i = 0; i < 2; i += 1 - running) {
185 for (key = 1; key < 3 * 1000 + 1; key++) {
186 val = GPOINTER_TO_INT (mono_conc_hashtable_lookup (hash, GINT_TO_POINTER (key)));
191 return GINT_TO_POINTER (key);
198 pw_pr_w_add_thread (void *arg)
200 int i, idx = 1000 * GPOINTER_TO_INT (arg);
202 mono_thread_info_attach ((gpointer)&arg);
204 for (i = idx; i < idx + 1000; i++) {
205 mono_os_mutex_lock (&global_mutex);
206 mono_conc_hashtable_insert (hash, GINT_TO_POINTER (i + 1), GINT_TO_POINTER (i + 1));
207 mono_os_mutex_unlock (&global_mutex);
213 pw_pr_w_del_thread (void *arg)
215 int i, idx = 1000 * GPOINTER_TO_INT (arg);
217 mono_thread_info_attach ((gpointer)&arg);
219 for (i = idx; i < idx + 1000; i++) {
220 mono_os_mutex_lock (&global_mutex);
221 mono_conc_hashtable_remove (hash, GINT_TO_POINTER (i + 1));
222 mono_os_mutex_unlock (&global_mutex);
228 parallel_writer_parallel_reader (void)
230 pthread_t wa, wb, wc, ra, rb, rc;
236 mono_os_mutex_init (&global_mutex);
237 hash = mono_conc_hashtable_new (NULL, NULL);
239 for (i = 0; i < 2; i++) {
242 pthread_create (&ra, NULL, pw_pr_r_thread, NULL);
243 pthread_create (&rb, NULL, pw_pr_r_thread, NULL);
244 pthread_create (&rc, NULL, pw_pr_r_thread, NULL);
248 pthread_create (&wa, NULL, pw_pr_w_add_thread, GINT_TO_POINTER (0));
249 pthread_create (&wb, NULL, pw_pr_w_add_thread, GINT_TO_POINTER (1));
250 pthread_create (&wc, NULL, pw_pr_w_add_thread, GINT_TO_POINTER (2));
253 pthread_create (&wa, NULL, pw_pr_w_del_thread, GINT_TO_POINTER (0));
254 pthread_create (&wb, NULL, pw_pr_w_del_thread, GINT_TO_POINTER (1));
255 pthread_create (&wc, NULL, pw_pr_w_del_thread, GINT_TO_POINTER (2));
259 pthread_join (wa, NULL);
260 pthread_join (wb, NULL);
261 pthread_join (wc, NULL);
265 pthread_join (ra, &a);
266 pthread_join (rb, &b);
267 pthread_join (rc, &c);
269 res += GPOINTER_TO_INT (a) + GPOINTER_TO_INT (b) + GPOINTER_TO_INT (c);
273 printf ("PAR_WRITER_PAR_READER TEST FAILED %d %d %d\n", GPOINTER_TO_INT (a), GPOINTER_TO_INT (b), GPOINTER_TO_INT (c));
275 mono_conc_hashtable_destroy (hash);
276 mono_os_mutex_destroy (&global_mutex);
281 static void G_GNUC_UNUSED
282 benchmark_conc (void)
284 MonoConcurrentHashTable *h;
287 h = mono_conc_hashtable_new (NULL, NULL);
289 for (i = 1; i < 10 * 1000; ++i) {
290 mono_conc_hashtable_insert (h, GUINT_TO_POINTER (i), GUINT_TO_POINTER (i));
294 for (j = 0; j < 100000; ++j)
295 for (i = 1; i < 10 * 105; ++i)
296 mono_conc_hashtable_lookup (h, GUINT_TO_POINTER (i));
298 mono_conc_hashtable_destroy (h);
301 static void G_GNUC_UNUSED
302 benchmark_glib (void)
307 h = g_hash_table_new (NULL, NULL);
309 for (i = 1; i < 10 * 1000; ++i)
310 g_hash_table_insert (h, GUINT_TO_POINTER (i), GUINT_TO_POINTER (i));
313 for (j = 0; j < 100000; ++j)
314 for (i = 1; i < 10 * 105; ++i)
315 g_hash_table_lookup (h, GUINT_TO_POINTER (i));
317 g_hash_table_destroy (h);
321 thread_state_init (MonoThreadUnwindState *ctx)
329 MonoThreadInfoCallbacks cb = { NULL };
330 MonoThreadInfoRuntimeCallbacks ticallbacks;
333 CHECKED_MONO_INIT ();
334 mono_threads_init (&cb, sizeof (MonoThreadInfo));
335 memset (&ticallbacks, 0, sizeof (ticallbacks));
336 ticallbacks.thread_state_init = thread_state_init;
337 mono_threads_runtime_init (&ticallbacks);
339 mono_thread_info_attach ((gpointer)&cb);
341 // benchmark_conc ();
342 // benchmark_glib ();
344 res += single_writer_single_reader ();
345 res += parallel_writer_single_reader ();
346 res += single_writer_parallel_reader ();
347 res += parallel_writer_parallel_reader ();