I am working with a C networking application using POSIX threading. I encountered a problem where a child thread serving as a monitor not detecting the change (increment) to a global counter made by the main thread. I managed to extract the relevant code into the following 3 files and reproduce the problem.
// common.h
#ifndef COMMON_H
#define COMMON_H
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <sys/resource.h>
static volatile int glob = 0;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void increment_clients_count();
#endif
// common.c
#include "common.h"
void increment_clients_count()
{
// Lock
pthread_mutex_lock(&mutex);
glob++;
// Unlock
pthread_mutex_unlock(&mutex);
}
// thread_incr_wrong.c
// main function
#include "common.h"
void /* Loop 'arg' times incrementing 'glob' */
monitor()
{
while(1)
{
time_t current_time;
time(¤t_time);
char *t = ctime(¤t_time);
if (t[strlen(t)-1] == '\n') t[strlen(t)-1] = '\0';
printf("%s, Count : %d\n", t, glob);
sleep(3);
}
}
int
main(int argc, char *argv[])
{
pthread_t t1;
int s;
s = pthread_create(&t1, NULL, (void*)monitor, NULL);
if (s != 0){
perror("pthread_create");
exit(1);
}
while(1){
increment_clients_count();
sleep(5);
}
exit(0);
}
When this cod runs, I hope to see
Tue Jan 11 18:56:24 2022, Count : 1
Tue Jan 11 18:56:27 2022, Count : 1
Tue Jan 11 18:56:30 2022, Count : 2
Tue Jan 11 18:56:33 2022, Count : 2
Tue Jan 11 18:56:36 2022, Count : 3
Tue Jan 11 18:56:39 2022, Count : 4
Tue Jan 11 18:56:42 2022, Count : 4
Tue Jan 11 18:56:45 2022, Count : 5
Tue Jan 11 18:56:48 2022, Count : 5
Tue Jan 11 18:56:51 2022, Count : 6
Tue Jan 11 18:56:54 2022, Count : 7
Instead, the output looks like this
Tue Jan 11 18:59:40 2022, Count : 0
Tue Jan 11 18:59:43 2022, Count : 0
Tue Jan 11 18:59:46 2022, Count : 0
Tue Jan 11 18:59:49 2022, Count : 0
Tue Jan 11 18:59:52 2022, Count : 0
The monitor thread did not see glob
variable being incremented every 5 seconds.
Please let me know where in the code I did something wrong.
If you'd like to try reproduce the problem, you can copy those 3 files and build the executable with
cc -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -g -pedantic -Wall -W -Wmissing-prototypes -Wno-sign-compare -Wimplicit-fallthrough -Wno-unused-parameter -pthread -o thread_incr_wrong thread_incr_wrong.c common.c
on any Linux machine. The executable's name is thread_incr_wrong.
Thanks
Both glob
and mutex
are declared as static
. This means that when common.h is included in both common.c and main.c, they each have their own copy of those variables.
Global variables should be declared in a header file where all source files can see them and defined in exactly one source file. So change what's in the header to external declarations and put the definitions in common.c:
common.h:
extern volatile int glob;
extern pthread_mutex_t mutex;
common.c:
volatile int glob = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;