Multi thread access a log file

Recently I created a logging functionality for multiple threads. I also used mutex lock to control multiple access on logging file. I met a issue when testing the logging functionality.
If I create multiple threads in a loop with parameters as bellow, the mutex lock seems take no effective.
err = pthread_create(&ntid[iLoop], NULL, thr_fn, &param);
while when I create thread using NULL parameter, the logging functinality works well.
err = pthread_create(&ntid[iLoop], NULL, thr_fn, NULL);

The following is the 2 source files:

/********************************************************/
/* thread_main.c */

#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include "Util.h"

#define MAX_THREAD_NUM 10
#define MAX_COUNT_NUM 8000

pthread_t ntid[MAX_THREAD_NUM];

typedef struct tagThrParams_t
{
char szThrName[16];
int iLogNum;
} ThrParams_t;

void *thr_fn(void *arg)
{
int i;
#ifdef ENABLE_PARM
ThrParams_t *param = (ThrParams_t*)arg;
for(i = 0; i < param->iLogNum; i ++)
{
logging(LOG_INFOR, "[%s]pid=%u tid=%u loop=%d/%d\n", param->szThrName, getpid(), pthread_self(), i, param->iLogNum);
}
#else
for(i = 0; i < MAX_COUNT_NUM; i ++)
{
logging(LOG_INFOR, "pid=%u tid=%u loop=%d\n", getpid(), pthread_self(), i);
}
#endif
return ((void *)0);
}

int thread_gen(int iLoop)
{
int err;
#ifdef ENABLE_PARM
ThrParams_t param;

memset(&param, 0x00, sizeof(param));
sprintf(param.szThrName, "THR%03d", iLoop);
param.iLogNum = MAX_COUNT_NUM;

err = pthread_create(&ntid[iLoop], NULL, thr_fn, &param);
#else
err = pthread_create(&ntid[iLoop], NULL, thr_fn, NULL);
#endif
if(err != 0)
{
printf("can't create thread: %s\n", strerror(err));
return 1;
}

logging(LOG_INFOR, "thread_gen:thread(%u) is created.\n", ntid[iLoop]);

return 0;
}

int main()
{
int i;

log_init();

for(i = 0; i < MAX_THREAD_NUM; i++)
{
thread_gen(i);
}

for(i = 0; i < MAX_THREAD_NUM; i++)
{
logging(LOG_INFOR, "main: thread(%u) is terminated.\n", ntid[i]);
pthread_join(ntid[i], NULL);
}

log_close();
return 0;
}

/*********************************************************/
/* Util.c */

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include<time.h>
#include <pthread.h>
#include "Util.h"

#define MAX_LINE_NUM 1000000

FILE * g_pFile = NULL;
char * g_pDirName = "./";
char * g_pFileName = "TestLog";
static unsigned int g_file_num = 0;
static unsigned int g_line_count = 0;

//pthread_mutex_t *g_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t g_mutex;

static void get_time(char *buf)
{
time_t now;
struct tm *tm_now;

if (NULL == buf)
{
return;
}


time(&now);
tm_now = localtime(&now);
sprintf(buf, "%d%02d%02d%02d%02d%02d", tm_now->tm_year + 1900, tm_now->tm_mon, tm_now->tm_mday, tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec);

return;
}

static void get_timestamp(char *buf)
{
time_t now;
struct tm *tm_now;

if (NULL == buf)
{
return;
}


time(&now);
tm_now = localtime(&now);
sprintf(buf, "%d-%02d-%02d %02d:%02d:%02d", tm_now->tm_year + 1900, tm_now->tm_mon, tm_now->tm_mday, tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec);

return;
}

int log_init()
{
char fileFullName[50];
int ret = -1;

memset(fileFullName, 0x00, 50);
sprintf(fileFullName, "%s%s.txt", g_pDirName, g_pFileName);
g_pFile = fopen(fileFullName, "w+");
if (NULL == g_pFile)
{
printf("Error, failed to open file %s.\n", fileFullName);
return -1;
}

/* Mutex lcok initialize */
ret = pthread_mutex_init(&g_mutex, NULL);
if (0 != ret)
{
printf("Error, failed to call pthread_mutex_init()\n");
return -1;
}

return 0;
}

void logging(int level, char * format, ...)
{
char msg_buf[MAX_BUF_LEN + 1];
char msg_date[50];
char msg[MAX_BUF_LEN + 21];
char msg_level[4][15] = { "[INFOR] ", "[WARNING] ", "[ERROR] ", "[INFOR] "};

char fileNewName[100];
char fileOldName[100];


va_list list;
va_start(list, format);

memset(msg_buf, 0x00, MAX_BUF_LEN + 1);

vsnprintf(msg_buf, MAX_BUF_LEN + 1, format, list);

memset(msg_date, 0x00, 50);
get_timestamp(msg_date);

memset(msg, 0x00, MAX_BUF_LEN + 21);
sprintf(msg, "%s %s %s", msg_date, msg_level[level], msg_buf);

pthread_mutex_lock(&g_mutex);

/* Output the error log directly to screen. */
if (level == LOG_ERROR || level == LOG_SCREEN)
{
printf(msg);
}

if (g_line_count >= MAX_LINE_NUM)
{
fclose(g_pFile);

memset(msg_date, 0x00, 50);
get_time(msg_date);

memset(fileOldName, 0x00, 100);
sprintf(fileOldName, "%s%s.txt", g_pDirName, g_pFileName);

memset(fileNewName, 0x00, 100);
sprintf(fileNewName, "%s%s_%s_%04d.txt", g_pDirName, g_pFileName, msg_date, g_file_num);

rename(fileOldName, fileNewName);

log_init();

g_line_count = 0;
g_file_num++;
}

fputs(msg, g_pFile);

g_line_count ++;

va_end(list);

pthread_mutex_unlock(&g_mutex);

return;
}

void log_close()
{
char msg_date[50];
char fileOldName[100];
char fileNewName[100];
int ret = -1;

if(NULL == g_pFile)
{
return;
}

fclose(g_pFile);

memset(msg_date, 0x00, 50);
get_time(msg_date);

memset(fileOldName, 0x00, 100);
sprintf(fileOldName, "%s%s.txt", g_pDirName, g_pFileName);

memset(fileNewName, 0x00, 100);
sprintf(fileNewName, "%s%s_%s_%04d.txt", g_pDirName, g_pFileName, msg_date, g_file_num);
rename(fileOldName, fileNewName);

g_pFile = NULL;

ret = pthread_mutex_destroy(&g_mutex);
if (0 != ret)
{
printf("Error, failed to call pthread_mutex_destroy().\n");
return;
}

return;
}
/*****************************************************/

Last edited on
You can copy and paste, and then surround with code tags. There is a button to the right ,when editing your post, with <> inside that does this for you.

I suggest you edit your post to include snippets of your code including the function and the sections you are protecting with mutexes.

It wouldn't seam to make any sense at all that the parameter has any effect on the mutex.
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/********************************************************/
/* thread_main.c */

#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include "Util.h"

#define MAX_THREAD_NUM 10
#define MAX_COUNT_NUM 8000

pthread_t ntid[MAX_THREAD_NUM];

typedef struct tagThrParams_t {
    char szThrName[16];
    int iLogNum;
} ThrParams_t;

void *thr_fn(void *arg)
{
    int i;
#ifdef ENABLE_PARM
    ThrParams_t *param = (ThrParams_t *)arg;
    for (i = 0; i < param->iLogNum; i ++) {
        logging(LOG_INFOR, "[%s]pid=%u tid=%u loop=%d/%d\n", param->szThrName, getpid(), pthread_self(), i, param->iLogNum);
    }
#else
    for (i = 0; i < MAX_COUNT_NUM; i ++) {
        logging(LOG_INFOR, "pid=%u tid=%u loop=%d\n", getpid(), pthread_self(), i);
    }
#endif
    return ((void *)0);
}

int thread_gen(int iLoop)
{
    int err;
#ifdef ENABLE_PARM
    ThrParams_t param;

    memset(&param, 0x00, sizeof(param));
    sprintf(param.szThrName, "THR%03d", iLoop);
    param.iLogNum = MAX_COUNT_NUM;

    err = pthread_create(&ntid[iLoop], NULL, thr_fn, &param);
#else
    err = pthread_create(&ntid[iLoop], NULL, thr_fn, NULL);
#endif
    if (err != 0) {
        printf("can't create thread: %s\n", strerror(err));
        return 1;
    }

    logging(LOG_INFOR, "thread_gen:thread(%u) is created.\n", ntid[iLoop]);

    return 0;
}

int main()
{
    int i;

    log_init();

    for (i = 0; i < MAX_THREAD_NUM; i++) {
        thread_gen(i);
    }

    for (i = 0; i < MAX_THREAD_NUM; i++) {
        logging(LOG_INFOR, "main: thread(%u) is terminated.\n", ntid[i]);
        pthread_join(ntid[i], NULL);
    }

    log_close();
    return 0;
}

/*********************************************************/
/* Util.c */

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include<time.h>
#include <pthread.h>
#include "Util.h"

#define MAX_LINE_NUM 1000000

FILE *g_pFile = NULL;
char *g_pDirName = "./";
char *g_pFileName = "TestLog";
static unsigned int g_file_num = 0;
static unsigned int g_line_count = 0;

//pthread_mutex_t *g_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t g_mutex;

static void get_time(char *buf)
{
    time_t now;
    struct tm *tm_now;

    if (NULL == buf) {
        return;
    }


    time(&now);
    tm_now = localtime(&now);
    sprintf(buf, "%d%02d%02d%02d%02d%02d", tm_now->tm_year + 1900, tm_now->tm_mon, tm_now->tm_mday, tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec);

    return;
}

static void get_timestamp(char *buf)
{
    time_t now;
    struct tm *tm_now;

    if (NULL == buf) {
        return;
    }


    time(&now);
    tm_now = localtime(&now);
    sprintf(buf, "%d-%02d-%02d %02d:%02d:%02d", tm_now->tm_year + 1900, tm_now->tm_mon, tm_now->tm_mday, tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec);

    return;
}

int log_init()
{
    char fileFullName[50];
    int ret = -1;

    memset(fileFullName, 0x00, 50);
    sprintf(fileFullName, "%s%s.txt", g_pDirName, g_pFileName);
    g_pFile = fopen(fileFullName, "w+");
    if (NULL == g_pFile) {
        printf("Error, failed to open file %s.\n", fileFullName);
        return -1;
    }

    /* Mutex lcok initialize */
    ret = pthread_mutex_init(&g_mutex, NULL);
    if (0 != ret) {
        printf("Error, failed to call pthread_mutex_init()\n");
        return -1;
    }

    return 0;
}

void logging(int level, char *format, ...)
{
    char msg_buf[MAX_BUF_LEN + 1];
    char msg_date[50];
    char msg[MAX_BUF_LEN + 21];
    char msg_level[4][15] = { "[INFOR] ", "[WARNING] ", "[ERROR] ", "[INFOR] "};

    char fileNewName[100];
    char fileOldName[100];


    va_list list;
    va_start(list, format);

    memset(msg_buf, 0x00, MAX_BUF_LEN + 1);

    vsnprintf(msg_buf, MAX_BUF_LEN + 1, format, list);

    memset(msg_date, 0x00, 50);
    get_timestamp(msg_date);

    memset(msg, 0x00, MAX_BUF_LEN + 21);
    sprintf(msg, "%s %s %s", msg_date, msg_level[level], msg_buf);

    pthread_mutex_lock(&g_mutex);

    /* Output the error log directly to screen. */
    if (level == LOG_ERROR || level == LOG_SCREEN) {
        printf(msg);
    }

    if (g_line_count >= MAX_LINE_NUM) {
        fclose(g_pFile);

        memset(msg_date, 0x00, 50);
        get_time(msg_date);

        memset(fileOldName, 0x00, 100);
        sprintf(fileOldName, "%s%s.txt", g_pDirName, g_pFileName);

        memset(fileNewName, 0x00, 100);
        sprintf(fileNewName, "%s%s_%s_%04d.txt", g_pDirName, g_pFileName, msg_date, g_file_num);

        rename(fileOldName, fileNewName);

        log_init();

        g_line_count = 0;
        g_file_num++;
    }

    fputs(msg, g_pFile);

    g_line_count ++;

    va_end(list);

    pthread_mutex_unlock(&g_mutex);

    return;
}

void log_close()
{
    char msg_date[50];
    char fileOldName[100];
    char fileNewName[100];
    int ret = -1;

    if (NULL == g_pFile) {
        return;
    }

    fclose(g_pFile);

    memset(msg_date, 0x00, 50);
    get_time(msg_date);

    memset(fileOldName, 0x00, 100);
    sprintf(fileOldName, "%s%s.txt", g_pDirName, g_pFileName);

    memset(fileNewName, 0x00, 100);
    sprintf(fileNewName, "%s%s_%s_%04d.txt", g_pDirName, g_pFileName, msg_date, g_file_num);
    rename(fileOldName, fileNewName);

    g_pFile = NULL;

    ret = pthread_mutex_destroy(&g_mutex);
    if (0 != ret) {
        printf("Error, failed to call pthread_mutex_destroy().\n");
        return;
    }

    return;
}
/*****************************************************/
The problem is that the param you're passing to thr_fn is a local variable in thread_gen(). As soon as thread_gen exits, the memory is deallocated and gets used by Something Else. At that point, the value of param is undefined.

You need to pass a param that won't go away.
Thanks for your reply. So If I store the contents of param which is local variable in thread_gen() to thr_fn() as bellow, the issue will be finally fixed, right?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void *thr_fn(void *arg)
{
    int i;
#ifdef ENABLE_PARM  
    ThrParams_t stParam;
    memset(&stParam, 0x00, sizeof(ThrParams_t));
    memcpy(&stParam, (ThrParams_t*)arg, sizeof(ThrParams_t)); // Store the contents of parameter which is local variable in thread_gen().

    for(i = 0; i < stParam.iLogNum; i ++) 
    {   
        logging(LOG_INFOR, "[%s]pid=%u tid=%u loop=%d/%d\n", stParam.szThrName, getpid(), pthread_self(), i, stParam.iLogNum);
    }   
#else
    for(i = 0; i < MAX_COUNT_NUM; i ++) 
    {   
        logging(LOG_INFOR, "pid=%u tid=%u loop=%d\n", getpid(), pthread_self(), i); 
    }   
#endif
    return ((void *)0);
}
Last edited on
No, because you're assuming that the data in arg will be valid long enough for you to copy it. In fact, you have a race condition. Once you call pthread_create(), the two threads may run in whatever order the OS wants, for however long it wants. So the contents in param may be blown away before thr_fn() executes its first statement.

Note that you have already found that the code works. Don't be fooled, you're just getting lucky. The bug is really there.

I would make a copy of param on the heap before passing it to thr_fn. Then have thr_fn delete it when it exits:

In thread_gen():
err = pthread_create(&ntid[iLoop], NULL, thr_fn, new ThrParams_t(param));

and in thr_fn():
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void *thr_fn(void *arg)
{
    int i;
#ifdef ENABLE_PARM
    ThrParams_t *param = (ThrParams_t *)arg;
    for (i = 0; i < param->iLogNum; i ++) {
        logging(LOG_INFOR, "[%s]pid=%u tid=%u loop=%d/%d\n", param->szThrName,
                    getpid(), pthread_self(), i, param->iLogNum);
    }
    delete param;
#else
    for (i = 0; i < MAX_COUNT_NUM; i ++) {
        logging(LOG_INFOR, "pid=%u tid=%u loop=%d\n", getpid(), pthread_self(), i);
    }
#endif
    return ((void *)0);
}
Your solution is really nice, thank you!
Topic archived. No new replies allowed.