Branch data Line data Source code
1 : : #include <glib.h> 2 : : #include <glib/gwakeup.h> 3 : : 4 : : static gboolean 5 : 10 : check_signaled (GWakeup *wakeup) 6 : : { 7 : : GPollFD fd; 8 : : 9 : 10 : g_wakeup_get_pollfd (wakeup, &fd); 10 : 10 : return g_poll (&fd, 1, 0); 11 : : } 12 : : 13 : : static void 14 : 932659 : wait_for_signaled (GWakeup *wakeup) 15 : : { 16 : : GPollFD fd; 17 : : 18 : 932659 : g_wakeup_get_pollfd (wakeup, &fd); 19 : 932659 : g_poll (&fd, 1, -1); 20 : 932659 : } 21 : : 22 : : static void 23 : 2 : test_semantics (void) 24 : : { 25 : : GWakeup *wakeup; 26 : : gint i; 27 : : 28 : 2 : wakeup = g_wakeup_new (); 29 : 2 : g_assert (!check_signaled (wakeup)); 30 : : 31 : 2 : g_wakeup_signal (wakeup); 32 : 2 : g_assert (check_signaled (wakeup)); 33 : : 34 : 2 : g_wakeup_acknowledge (wakeup); 35 : 2 : g_assert (!check_signaled (wakeup)); 36 : : 37 : 2 : g_wakeup_free (wakeup); 38 : : 39 : : /* free unused */ 40 : 2 : wakeup = g_wakeup_new (); 41 : 2 : g_wakeup_free (wakeup); 42 : : 43 : : /* free while signaled */ 44 : 2 : wakeup = g_wakeup_new (); 45 : 2 : g_wakeup_signal (wakeup); 46 : 2 : g_wakeup_free (wakeup); 47 : : 48 : : /* ensure excessive signalling doesn't deadlock */ 49 : 2 : wakeup = g_wakeup_new (); 50 [ + + ]: 2000002 : for (i = 0; i < 1000000; i++) 51 : 2000000 : g_wakeup_signal (wakeup); 52 : 2 : g_assert (check_signaled (wakeup)); 53 : : 54 : : /* ensure a single acknowledgement is sufficient */ 55 : 2 : g_wakeup_acknowledge (wakeup); 56 : 2 : g_assert (!check_signaled (wakeup)); 57 : : 58 : 2 : g_wakeup_free (wakeup); 59 : 2 : } 60 : : 61 : : struct token 62 : : { 63 : : gpointer owner; 64 : : gint ttl; 65 : : }; 66 : : 67 : : struct context 68 : : { 69 : : GSList *pending_tokens; 70 : : GMutex lock; 71 : : GWakeup *wakeup; 72 : : gboolean quit; 73 : : }; 74 : : 75 : : #define NUM_THREADS 50 76 : : #define NUM_TOKENS 5 77 : : #define TOKEN_TTL 100000 78 : : 79 : : static struct context contexts[NUM_THREADS]; 80 : : static GThread *threads[NUM_THREADS]; 81 : : static GWakeup *last_token_wakeup; 82 : : static gint tokens_alive; /* (atomic) */ 83 : : 84 : : static void 85 : 100 : context_init (struct context *ctx) 86 : : { 87 : 100 : ctx->pending_tokens = NULL; 88 : 100 : g_mutex_init (&ctx->lock); 89 : 100 : ctx->wakeup = g_wakeup_new (); 90 : 100 : ctx->quit = FALSE; 91 : 100 : } 92 : : 93 : : static void 94 : 100 : context_clear (struct context *ctx) 95 : : { 96 : 100 : g_assert (ctx->pending_tokens == NULL); 97 : 100 : g_assert (ctx->quit); 98 : : 99 : 100 : g_mutex_clear (&ctx->lock); 100 : 100 : g_wakeup_free (ctx->wakeup); 101 : 100 : } 102 : : 103 : : static void 104 : 100 : context_quit (struct context *ctx) 105 : : { 106 : 100 : g_atomic_int_set (&ctx->quit, TRUE); 107 : 100 : g_wakeup_signal (ctx->wakeup); 108 : 100 : } 109 : : 110 : : static struct token * 111 : 1932657 : context_try_pop_token (struct context *ctx) 112 : : { 113 : 1932657 : struct token *token = NULL; 114 : : 115 : 1932657 : g_mutex_lock (&ctx->lock); 116 [ + + ]: 1932657 : if (ctx->pending_tokens != NULL) 117 : : { 118 : 1000000 : token = ctx->pending_tokens->data; 119 : 1000000 : ctx->pending_tokens = g_slist_delete_link (ctx->pending_tokens, 120 : : ctx->pending_tokens); 121 : : } 122 : 1932657 : g_mutex_unlock (&ctx->lock); 123 : : 124 : 1932657 : return token; 125 : : } 126 : : 127 : : static void 128 : 1000000 : context_push_token (struct context *ctx, 129 : : struct token *token) 130 : : { 131 : 1000000 : g_assert (token->owner == ctx); 132 : : 133 : 1000000 : g_mutex_lock (&ctx->lock); 134 : 1000000 : ctx->pending_tokens = g_slist_prepend (ctx->pending_tokens, token); 135 : 1000000 : g_mutex_unlock (&ctx->lock); 136 : : 137 : 1000000 : g_wakeup_signal (ctx->wakeup); 138 : 1000000 : } 139 : : 140 : : static void 141 : 1000010 : dispatch_token (struct token *token) 142 : : { 143 [ + + ]: 1000010 : if (token->ttl > 0) 144 : : { 145 : : struct context *ctx; 146 : : gint next_ctx; 147 : : 148 : 1000000 : next_ctx = g_test_rand_int_range (0, NUM_THREADS); 149 : 1000000 : ctx = &contexts[next_ctx]; 150 : 1000000 : token->owner = ctx; 151 : 1000000 : token->ttl--; 152 : : 153 : 1000000 : context_push_token (ctx, token); 154 : : } 155 : : else 156 : : { 157 : 10 : g_slice_free (struct token, token); 158 : : 159 [ + + ]: 10 : if (g_atomic_int_dec_and_test (&tokens_alive)) 160 : 2 : g_wakeup_signal (last_token_wakeup); 161 : : } 162 : 1000010 : } 163 : : 164 : : static struct token * 165 : 10 : token_new (int ttl) 166 : : { 167 : : struct token *token; 168 : : 169 : 10 : token = g_slice_new (struct token); 170 : 10 : token->ttl = ttl; 171 : : 172 : 10 : g_atomic_int_inc (&tokens_alive); 173 : : 174 : 10 : return token; 175 : : } 176 : : 177 : : static gpointer 178 : 100 : thread_func (gpointer data) 179 : : { 180 : 100 : struct context *ctx = data; 181 : : struct token *token; 182 : : 183 [ + + ]: 932757 : while (!g_atomic_int_get (&ctx->quit)) 184 : : { 185 : 932657 : wait_for_signaled (ctx->wakeup); 186 : 932657 : g_wakeup_acknowledge (ctx->wakeup); 187 : : 188 [ + + ]: 1932657 : while ((token = context_try_pop_token (ctx)) != NULL) 189 : : { 190 : 1000000 : g_assert (token->owner == ctx); 191 : 1000000 : dispatch_token (token); 192 : : } 193 : : } 194 : : 195 : 100 : return NULL; 196 : : } 197 : : 198 : : static void 199 : 2 : test_threaded (void) 200 : : { 201 : : gint i; 202 : : 203 : : /* simple mainloop test based on GWakeup. 204 : : * 205 : : * create a bunch of contexts and a thread to 'run' each one. create 206 : : * some tokens and randomly pass them between the threads, until the 207 : : * TTL on each token is zero. 208 : : * 209 : : * when no tokens are left, signal that we are done. the mainthread 210 : : * will then signal each worker thread to exit and join them to make 211 : : * sure that works. 212 : : */ 213 : : 214 : 2 : last_token_wakeup = g_wakeup_new (); 215 : : 216 : : /* create contexts, assign to threads */ 217 [ + + ]: 102 : for (i = 0; i < NUM_THREADS; i++) 218 : : { 219 : 100 : context_init (&contexts[i]); 220 : 100 : threads[i] = g_thread_new ("test", thread_func, &contexts[i]); 221 : : } 222 : : 223 : : /* dispatch tokens */ 224 [ + + ]: 12 : for (i = 0; i < NUM_TOKENS; i++) 225 : 10 : dispatch_token (token_new (TOKEN_TTL)); 226 : : 227 : : /* wait until all tokens are gone */ 228 : 2 : wait_for_signaled (last_token_wakeup); 229 : : 230 : : /* ask threads to quit, join them, cleanup */ 231 [ + + ]: 102 : for (i = 0; i < NUM_THREADS; i++) 232 : : { 233 : 100 : context_quit (&contexts[i]); 234 : 100 : g_thread_join (threads[i]); 235 : 100 : context_clear (&contexts[i]); 236 : : } 237 : : 238 : 2 : g_wakeup_free (last_token_wakeup); 239 : 2 : } 240 : : 241 : : int 242 : 2 : main (int argc, char **argv) 243 : : { 244 : 2 : g_test_init (&argc, &argv, NULL); 245 : : 246 : : #ifdef TEST_EVENTFD_FALLBACK 247 : : #define TESTNAME_SUFFIX "-fallback" 248 : : #else 249 : : #define TESTNAME_SUFFIX 250 : : #endif 251 : : 252 : : 253 : 2 : g_test_add_func ("/gwakeup/semantics" TESTNAME_SUFFIX, test_semantics); 254 : 2 : g_test_add_func ("/gwakeup/threaded" TESTNAME_SUFFIX, test_threaded); 255 : : 256 : 2 : return g_test_run (); 257 : : }