LCOV - code coverage report
Current view: top level - clients/examples/C - testcondvar.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 86 98 87.8 %
Date: 2024-12-19 23:10:26 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: MPL-2.0
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       7             :  *
       8             :  * Copyright 2024 MonetDB Foundation;
       9             :  * Copyright August 2008 - 2023 MonetDB B.V.;
      10             :  * Copyright 1997 - July 2008 CWI.
      11             :  */
      12             : 
      13             : #include "monetdb_config.h"
      14             : #include "gdk.h"
      15             : 
      16             : #define NN (3)
      17             : 
      18             : volatile int timeout = 100; // set this to 0 during interactive debugging
      19             : 
      20             : /* global state protected by a lock.
      21             :  *
      22             :  * Every worker thread has a number of permits and a number of ticks.
      23             :  * It sleeps on the condition variable, whenever it wakes it will
      24             :  * try to convert one permit into a tick, that is, permit-- and tick++.
      25             :  */
      26             : 
      27             : MT_Lock lock = MT_LOCK_INITIALIZER(lock);
      28             : MT_Cond condvar = MT_COND_INITIALIZER(the_condvar);
      29             : struct state {
      30             :         MT_Id id;
      31             :         int ticks;
      32             :         int permits;
      33             :         bool terminate;
      34             :         bool terminated;
      35             : } states[NN] = { {0} };
      36             : 
      37             : /*
      38             :  * The main thread holds the lock so it can manipulate and verify the permits
      39             :  * and ticks.  It uses this function to temporarily release the lock so the
      40             :  * workers get a chance to do their work. We give them a 100ms, which should be
      41             :  * plenty.
      42             :  *
      43             :  * If we cannot retake the lock after that interval we assume a worker thread
      44             :  * has gone astray while holding the lock.
      45             :  */
      46             : static void
      47           7 : let_run(void)
      48             : {
      49           7 :         MT_lock_unset(&lock);
      50             : 
      51           7 :         MT_sleep_ms(100);
      52             : 
      53             :         // try to retake the lock. Make a few attempts before giving up.
      54           7 :         int attempts = 0;
      55           7 :         while (!MT_lock_try(&lock)) {
      56           0 :                 if (timeout > 0 && ++attempts > timeout) {
      57           0 :                         fprintf(stderr, "Can't get hold of the lock after %d attempts\n", attempts);
      58           0 :                         fprintf(stderr, "If this is because you're running this program in a debugger,\n");
      59           0 :                         fprintf(stderr, "try setting the timeout variable to 0.\n");
      60           0 :                         abort();
      61             :                 }
      62           0 :                 MT_sleep_ms(10);
      63             :         }
      64           7 : }
      65             : 
      66             : 
      67             : static void
      68           3 : worker(void *arg)
      69             : {
      70           3 :         struct state *st = arg;
      71           3 :         int id = (int)(st - &states[0]);
      72           3 :         fprintf(stderr, "worker %d started, waiting to acquire lock\n", id);
      73             : 
      74           3 :         MT_lock_set(&lock);
      75           3 :         fprintf(stderr, "worker %d acquired lock\n", id);
      76          21 :         while (1) {
      77          12 :                 if (st->terminate) {
      78           3 :                         fprintf(stderr, "worker %d terminating\n", id);
      79           3 :                         break;
      80             :                 }
      81           9 :                 if (st->permits > 0) {
      82           6 :                         fprintf(stderr, "worker %d ticking\n", id);
      83           6 :                         st->ticks++;
      84           6 :                         st->permits--;
      85             :                 }
      86           9 :                 fprintf(stderr, "worker %d waiting on condvar\n", id);
      87           9 :                 MT_cond_wait(&condvar, &lock);
      88           9 :                 fprintf(stderr, "worker %d woke up\n", id);
      89             :         }
      90           3 :         st->terminated = true;
      91           3 :         MT_lock_unset(&lock);
      92           3 : }
      93             : 
      94             : 
      95             : static void
      96           8 : check_impl(int line, int expected_sum_ticks, int expected_max_ticks, int expected_sum_permits)
      97             : {
      98           8 :         int sum_ticks = 0;
      99           8 :         int max_ticks = -1;
     100           8 :         int sum_permits = 0;
     101             : 
     102          32 :         for (int i = 0; i < NN; i++) {
     103          24 :                 sum_permits += states[i].permits;
     104          24 :                 int ticks = states[i].ticks;
     105          24 :                 sum_ticks += ticks;
     106          24 :                 if (ticks > max_ticks)
     107             :                         max_ticks = ticks;
     108             :         }
     109             : 
     110           8 :         fprintf(stderr, "On line %d: (sum_ticks, max_ticks, sum_permits) = (%d, %d, %d)\n",
     111             :                         line,
     112             :                         sum_ticks, max_ticks, sum_permits);
     113             : 
     114           8 :         bool good = true;
     115           8 :         good &= (sum_ticks == expected_sum_ticks);
     116           8 :         if (expected_max_ticks >= 0)
     117           5 :                 good &= (max_ticks == expected_max_ticks);
     118           8 :         good &= (sum_permits == expected_sum_permits);
     119           8 :         if (good)
     120           8 :                 return;
     121             : 
     122           0 :         if (expected_max_ticks >= 0) {
     123           0 :                 fprintf(stderr, "MISMATCH: expected (%d, %d, %d)\n",
     124             :                         expected_sum_ticks, expected_max_ticks, expected_sum_permits);
     125             :         } else {
     126           0 :                 fprintf(stderr, "MISMATCH: expected (%d, ?, %d)\n",
     127             :                         expected_sum_ticks, expected_sum_permits);
     128             :         }
     129             : 
     130           0 :         for (int i = 0; i < NN; i++) {
     131           0 :                 fprintf(stderr, "worker %d: ticks=%d permits=%d\n", i, states[i].ticks, states[i].permits);
     132             :         }
     133           0 :         abort();
     134             : }
     135             : 
     136             : #define check(expected_sum, expected_max, expected_permits) check_impl(__LINE__, expected_sum, expected_max, expected_permits)
     137             : 
     138             : int
     139           1 : main(void)
     140             : {
     141           1 :         MT_thread_init();
     142             : 
     143             :         // All code in this function runs while we hold the lock.
     144             :         // From time to time we call let_run() to allow the worker threads to obtain it.
     145           1 :         MT_lock_set(&lock);
     146             : 
     147           1 :         fprintf(stderr, "-- Initially, everything is zero\n");
     148           1 :         check(0, 0, 0);
     149             : 
     150           1 :         fprintf(stderr, "\n-- Starting the worker threads\n");
     151           4 :         for (int i = 0; i < NN; i++) {
     152           3 :                 struct state *st = &states[i];
     153           3 :                 MT_create_thread(&st->id, worker, st, MT_THR_JOINABLE, "workerXXXX");
     154             :         }
     155           1 :         MT_sleep_ms(100);
     156             : 
     157           1 :         fprintf(stderr, "\n-- Now allow the workers to take the lock, they should enter their main loop\n");
     158           1 :         let_run();
     159           1 :         check(0, 0, 0);
     160             : 
     161           1 :         fprintf(stderr, "\n-- All threads get a permit but nothing happens because we haven't touched the condvar\n");
     162           4 :         for (int i = 0; i < NN; i++)
     163           3 :                 states[i].permits = 1;
     164           1 :         let_run();
     165           1 :         check(0, 0, NN);
     166             : 
     167           1 :         fprintf(stderr, "\n-- Now we broadcast on the condvar. All should wake\n");
     168           1 :         MT_cond_broadcast(&condvar);
     169           1 :         let_run();
     170           1 :         check(NN, 1, 0);
     171             : 
     172           1 :         fprintf(stderr, "\n-- Now we give each of them %d permits\n", NN);
     173           4 :         for (int i = 0; i < NN; i++) {
     174           3 :                 states[i].ticks = 0;
     175           3 :                 states[i].permits = NN;
     176             :         }
     177           1 :         check(0, 0, NN * NN);
     178             : 
     179             :         // Note: counting from 1 instead of 0
     180           4 :         for (int i = 1; i <= NN; i++) {
     181           3 :                 fprintf(stderr, "\n-- [%d] Signal one, don't know which one it will be\n", i);
     182           3 :                 MT_cond_signal(&condvar);
     183           3 :                 let_run();
     184           3 :                 check(i, -1, NN * NN - i);
     185             :         }
     186             : 
     187             : 
     188           1 :         fprintf(stderr, "\n-- Telling them all to quit\n");
     189           4 :         for (int i = 0; i < NN; i++) {
     190           3 :                 states[i].terminate = true;
     191             :         }
     192           1 :         MT_cond_broadcast(&condvar);
     193           1 :         let_run();
     194             : 
     195           4 :         for (int i = 0; i < NN; i++) {
     196           3 :                 fprintf(stderr, "-- Joining worker %d\n", i);
     197           3 :                 MT_join_thread(states[i].id);
     198           3 :                 fprintf(stderr, "-- Joined worker %d\n", i);
     199             :         }
     200           1 :         fprintf(stderr, "\n-- Joined all, exiting\n");
     201             : 
     202           1 :         return 0;
     203             : }

Generated by: LCOV version 1.14