Related
Currently I am attempting to parse words from all text files in a directory (in this case it is safe to assume there will only be text files within the directory). It seems as though I am able to open the file within the threads function, however I am unable to grab the text within. No error messages are being presented but the printf within splitInput is not printing to the terminal.
Forgive my semantic work within the code, I am a fresh novice with C! Along with this there may be unused code within main as this will be part of a larger project. I appreciate the assistance in advance!
#include <stdlib.h>
#include <dirent.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include "queue.h"
void* splitInput(void *filename) {
printf("Thread %s Created\n", (char*)filename);
FILE *file;
int i = 0;
char *cp;
char *bp;
char line[255];
char *array[5000];
file = fopen((char*)filename, "r");
if(file == NULL) {
perror("Error opening file");
}
printf("Opened File %s\n", (char*)filename);
while(fgets(line, sizeof(line), file) != NULL) {
bp = line;
while(1) {
cp = strtok(bp, ",.!? \n");
bp = NULL;
if(cp == NULL) {
break;
}
array[i++] = cp;
printf("Check print - word %i:%s:\n", i-1, cp);
}
}
fclose(file);
return 0;
}
int main(int argc, char *argv[]) {
DIR* d;
struct dirent* e;
// grab our queueSize and threadCount
int queueSize = atoi(argv[2]);
int threadCount = atoi(argv[3]);
// var for creating a thread each file
int i = 0;
// open the dir
d = opendir(argv[1]);
printf("Queue Size: %d\n", queueSize);
printf("Thread Count: %d\n", threadCount);
// set our thread count now that we know how many files are in dir
pthread_t threads[threadCount];
// read through our directory
while((e = readdir(d)) != NULL) {
// make sure we aren't reading . and ..
if(strcmp(e->d_name, ".") == 0) {
continue;
}
if(strcmp(e->d_name, "..") == 0) {
continue;
}
printf("entered file %s\n", e->d_name);
char *filename = strdup(e->d_name);
if(i < threadCount) {
// create our threads
pthread_create(&threads[i], NULL, splitInput, filename);
}
// increment i
i++;
}
// join our existing threads
for(int j = 0; j < i; j++) {
pthread_join(threads[j], NULL);
}
return 0;
}
Current Output
device#user:~/os/testdir$ ./output ~/os/testdir/test 10 10 output
Queue Size: 10
Thread Count: 10
entered file test
Thread test Created
Opened File test
Found the answer, I was attempting to open a file outside my working directory which cannot be done without the full path. Changing the working directory to the parameter given solved the issue. This can be done in this case with chdir(argv[1])
Revised code below.
#include <stdlib.h>
#include <dirent.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include "queue.h"
void* splitInput(void *filename) {
printf("Thread %s Created\n", (char*)filename);
FILE *file;
int i = 0;
char *cp;
char *bp;
char line[255];
char *array[5000];
file = fopen((char*)filename, "r");
if(file == NULL) {
perror("Error opening file");
}
printf("Opened File %s\n", (char*)filename);
while(fgets(line, sizeof(line), file) != NULL) {
bp = line;
while(1) {
cp = strtok(bp, ",.!? \n");
bp = NULL;
if(cp == NULL) {
break;
}
array[i++] = cp;
printf("Check print - word %i:%s:\n", i-1, cp);
}
}
fclose(file);
return 0;
}
int main(int argc, char *argv[]) {
DIR* d;
struct dirent* e;
// grab our queueSize and threadCount
int queueSize = atoi(argv[2]);
int threadCount = atoi(argv[3]);
// var for creating a thread each file
int i = 0;
// open the dir
chdir(argv[1]);
d = opendir(argv[1]);
printf("Queue Size: %d\n", queueSize);
printf("Thread Count: %d\n", threadCount);
// set our thread count now that we know how many files are in dir
pthread_t threads[threadCount];
// read through our directory
while((e = readdir(d)) != NULL) {
// make sure we aren't reading . and ..
if(strcmp(e->d_name, ".") == 0) {
continue;
}
if(strcmp(e->d_name, "..") == 0) {
continue;
}
printf("entered file %s\n", e->d_name);
char *filename = strdup(e->d_name);
if(i < threadCount) {
// create our threads
pthread_create(&threads[i], NULL, splitInput, filename);
}
// increment i
i++;
}
// join our existing threads
for(int j = 0; j < i; j++) {
pthread_join(threads[j], NULL);
}
return 0;
}
Hi everyone
I need your help
I'm Zed, and i have a primitiv question, look at this function :
void cat_stdin(void)
{
char c[1] //c;
while (read(0, c//&c, 1) > 0)
write(1, c//&c, 1);
}
how it works ? what are doing write and read syscall ?
this code is like : cat -
Sorry i'm a french noob
Share your knowledge to me, i'm stuck :/
Your posted answer has a few issues.
Notably, cat_stdout and cat_stdin could be one function (e.g. cat_stream) that takes the input file descriptor as an argument.
It's only transferring one byte at a time, so it's very slow.
Here's a version that I coded from scratch [with annotations]. It handles most cases and has error checking. It may help you improve your own version:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
int
cat_stream(int fd)
{
ssize_t rlen;
ssize_t wlen;
int err = 0;
char *cur;
char buf[64 * 1024];
// NOTE: the buffer size can be _any_ length, but 64KB is optimal for many
// filesystem files or devices
while (1) {
// read a chunk of input
rlen = read(fd,buf,sizeof(buf));
// end of file
if (rlen == 0)
break;
// error
if (rlen < 0) {
err = errno;
fprintf(stderr,"cat_stream: read error -- %s\n",strerror(err));
break;
}
// write out all data read in current chunk -- loop until all data
// read in this chunk has been output, even if it could _not_ be
// output in a single write call, advancing the pointer into the
// buffer [and shortening the remaining length]
cur = buf;
for (; rlen > 0; rlen -= wlen, cur += wlen) {
wlen = write(1,cur,rlen);
// handle error
if (wlen < 0) {
err = errno;
fprintf(stderr,"cat_stream: write error -- %s\n",strerror(err));
break;
}
}
}
// restore errno after fprintf so caller can use it [if desired]
if (err)
errno = err;
return err;
}
int
main(int argc,char **argv)
{
const char *file;
int fd;
int err = 0;
// skip over program name
--argc;
++argv;
// no arguments means stdin -- fake a "-" entry
if (argc <= 0) {
++argc;
--argv;
argv[0] = "-";
}
// loop on all input files
for (; argc > 0; --argc, ++argv) {
file = *argv;
// copy stdin
if (strcmp(file,"-") == 0) {
cat_stream(0);
continue;
}
// open the explicit input file
fd = open(file,O_RDONLY);
if (fd < 0) {
err = errno;
fprintf(stderr,"cat: unable to open '%s' -- %s\n",
file,strerror(err));
break;
}
// cat the open file stream
err = cat_stream(fd);
// close the input file
close(fd);
// stop on error
if (err)
break;
}
// regardless of the error, just return 1
if (err)
err = 1;
return err;
}
I wrote a little daemon.
This is the flow of the daemon, in general:
Get variables
Get all rows from the database (may be either MySQL or Oracle) that meets the query parameters (in this case get all rows that has this current time).
If any rows were found then run a Perl script for each row (using execv).
This daemon works well, but the problem is that when I have two rows or more coming back from the query, they start, but the Perl script outputs are mixed. They are supposed to run independently without interfering with each other.
Am I doing something wrong?
Is this a memory leak?
Here is my code :
/*
============================================================================
Name : main.c
Description : Daemon for scheduler
============================================================================
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <strings.h>
#include <regex.h>
#include <time.h>
#ifdef MYSQL_CODE
#include <mysql.h>
#endif
#ifdef ORACLE_CODE
#include <ocilib.h>
#endif
#define DAEMON_NAME "schedulerd"
void start_job(char *automation_user,char *automation_path,char *automation_log_path, char *id, char *site, char *db_type,char *db_host,char *db_user,char *db_password,char *db_schemata){
pid_t pid;
pid = fork();
if (pid < 0) { exit(EXIT_FAILURE); }
//We got a good pid, Continue to the next result
if (pid > 0) {
return;
}
char *file_format = "scheduler";
char *seperator = "_";
char *postfix = "_XXXXXX";
char *extension = ".log";
//get Time
time_t now;
struct tm *now_tm;
char hour[2],min[2],sec[2];
now = time(NULL);
now_tm = localtime(&now);
sprintf(hour, "%d", now_tm->tm_hour);
sprintf(min, "%d", now_tm->tm_min);
sprintf(sec, "%d", now_tm->tm_sec);
char *str = (char *)automation_log_path;
strcat(str,(char *)file_format);
strcat(str,seperator);
strcat(str,hour);
strcat(str,seperator);
strcat(str,min);
strcat(str,seperator);
strcat(str,sec);
strcat(str,postfix);
// buffer to hold the temporary file name
char nameBuff[128];
int filedes = -1;
// memset the buffers to 0
memset(nameBuff,0,sizeof(nameBuff));
// Copy the relevant information in the buffers
strncpy(nameBuff,str,128);
// Create the temporary file, this function will replace the 'X's
filedes = mkstemp(nameBuff);
if(filedes<1)
{
syslog (LOG_NOTICE, "Creation of temp file [%s] failed with error [%s]\n",nameBuff,strerror(errno));
}else{
mode_t perm = 0644;
fchmod(filedes, perm); //Change permission to the file so everyone can read
close(filedes); // Close created file
//Rename file
int ret;
char newname[128];
sprintf(newname, "%s%s", nameBuff,extension);
ret = rename(nameBuff, newname);
if(ret != 0) {
syslog (LOG_NOTICE, "Renaming of temp file %s to %s failed \n",nameBuff,newname);
exit(EXIT_FAILURE);
}
char statement[256];
sprintf(statement, "UPDATE scheduler_list SET log_file='%s' WHERE id='%s'", newname,id);
syslog (LOG_NOTICE,"Adding to DB : %s\n", statement);
char command[2048];
sprintf(command, "cd %s ; ./runner.pl -site %s -log %s -scheduler_id %s -command \\\"./run_me.pl\\\"", automation_path,site,newname,id);
//sprintf(command, "cd /net/10.7.5.50/opt/trunk/ ; ./runner.pl -site %s -log %s -scheduler_id %s -command \\\"./run_me.pl\\\"",site,newname,id);
if (strcasestr(db_type,"mysql")){/* mysql */
#ifdef MYSQL_CODE
MYSQL *conn;
//mysql_free_result(res); // Free mysql
conn = mysql_init(NULL);
/* Connect to database */
if (!mysql_real_connect(conn, db_host,db_user, db_password, db_schemata, 0, NULL, 0)) {
syslog (LOG_NOTICE,"%s\n", mysql_error(conn));
exit(EXIT_FAILURE);
}
if (mysql_query(conn,statement)) {
syslog (LOG_NOTICE,"%s\n", mysql_error(conn));
exit(EXIT_FAILURE);
}
#endif
}else{
#ifdef ORACLE_CODE
OCI_Connection* cn;
OCI_Statement* st;
OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT);
char query_command[128];
sprintf(query_command, "%s:1521/%s", db_host,db_schemata);
cn = OCI_ConnectionCreate(query_command, db_user, db_password, OCI_SESSION_DEFAULT);
st = OCI_StatementCreate(cn);
OCI_Prepare(st, statement);
OCI_Execute(st);
OCI_Commit(cn);
OCI_Cleanup();
#endif
}
char *args[] = {"sudo", "-u", automation_user, "bash","-c",command,NULL};
FILE *log_file_h = fopen(newname, "w");
if (log_file_h == NULL)
{
syslog (LOG_NOTICE,"Error opening file %s !\n",newname);
exit(EXIT_FAILURE);
}
syslog (LOG_NOTICE,"Starting scheduler job %s , command : %s",id, command);
fclose(log_file_h);
execv("/usr/bin/sudo",args);
syslog (LOG_NOTICE,"Failed to start job %s ",id);
perror("error");
}
exit(EXIT_FAILURE);
}
void process(char *automation_user,char *automation_path, char *automation_log_path ,char *db_type,char *db_host,char *db_user,char *db_password,char *db_schemata){
if (strcasestr(db_type,"mysql")){/* mysql */
#ifdef MYSQL_CODE
MYSQL *conn;
MYSQL_RES *res;
MYSQL_ROW row;
conn = mysql_init(NULL);
/* Connect to database */
if (!mysql_real_connect(conn, db_host,db_user, db_password, db_schemata, 0, NULL, 0)) {
syslog (LOG_NOTICE,"%s\n", mysql_error(conn));
return;
}
/* send SQL query */
if (mysql_query(conn, "SELECT id,site from scheduler_list where start_date = DATE_FORMAT(now(),'%Y-%m-%d %k:%i:00') AND id != father_id AND run='yes'")) {
syslog (LOG_NOTICE,"%s\n", mysql_error(conn));
return;
}
res = mysql_use_result(conn);
/* output table name */
while ((row = mysql_fetch_row(res)) != NULL){
char *id = malloc(strlen(row[0]) +1);
strcpy(id,row[0]);
char *site = malloc(strlen(row[1]) +1);
strcpy(site,row[1]);
start_job(automation_user,automation_path,automation_log_path,id,site,db_type, db_host,db_user,db_password,db_schemata);
}
/* close connection */
mysql_free_result(res);
mysql_close(conn);
#endif
}else{/* oracle */
#ifdef ORACLE_CODE
OCI_Connection* cn;
OCI_Statement* st;
OCI_Resultset* rs;
OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT);
char query_command[128];
sprintf(query_command, "%s:1521/%s", db_host,db_schemata);
cn = OCI_ConnectionCreate(query_command, db_user, db_password, OCI_SESSION_DEFAULT);
st = OCI_StatementCreate(cn);
OCI_ExecuteStmt(st, "SELECT id,site from scheduler_list where to_char(start_date, 'yyyy-mm-dd hh24:mi') = to_char(SYSDATE, 'yyyy-mm-dd hh24:mi') AND id != father_id AND run='yes'");
rs = OCI_GetResultset(st);
while (OCI_FetchNext(rs)){
char *id = malloc(strlen(OCI_GetString(rs, 1)) +1);
strcpy(id,OCI_GetString(rs,1));
char *site = malloc(strlen(OCI_GetString(rs,2)) +1);
strcpy(site,OCI_GetString(rs,2));
start_job(automation_user,automation_path,automation_log_path,id,site,db_type, db_host,db_user,db_password,db_schemata);
}
OCI_Cleanup();
#endif
}
}
char * set_conf_param (char *line, int addSlash){
char *param = malloc(strlen(line) + 2 + addSlash);
strcpy(param,line);
param = strchr(line,'=');
param = param+1; //Remove '='
strtok(param, "\n"); //remove /n
if (addSlash == 1){
int len = strlen(param);
param[len] = '/';
param[len+1] = '\0';
}
return strdup(param);
}
int main(int argc, char *argv[]) {
FILE * fp;
char * line = NULL;
size_t len = 0;
int found_db = 0;
ssize_t read;
pid_t pid, sid;
char *automation_user=NULL,*automation_log_path=NULL ,*db_type=NULL, *db_host=NULL , *db_user=NULL, *db_password=NULL, *db_schemata=NULL;
char *automation_path = getenv("AUTOMATION_PATH");
//char *automation_path = "/net/10.7.5.50/opt/trunk/";
char *automation_user_search = "automation_user=";
char *automation_log_path_search = "automation_log=";
char *db_type_search = "type=";
char *db_host_search = "host=";
char *db_user_search = "user=";
char *db_password_search = "password=";
char *db_schemata_search = "schemata=";
const char comment = '#';
/* Change the working directory to the root directory */
/* or another appropriated directory */
chdir(automation_path);
//Set our Logging Mask and open the Log
setlogmask(LOG_UPTO(LOG_NOTICE));
openlog(DAEMON_NAME, LOG_CONS | LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_USER);
syslog(LOG_NOTICE, "Entering Daemon");
//Read framework.conf
fp = fopen("framework.conf", "r");
if (fp == NULL){
syslog (LOG_NOTICE,"Failed to open framework.conf");
exit(1);
}
//Read framework.conf
fp = fopen("framework.conf", "r");
if (fp == NULL){
syslog (LOG_NOTICE,"Failed to open framework.conf");
exit(1);
}
while ((read = getline(&line, &len, fp)) != -1) {
//If line commented
if (strchr(line,comment) != NULL){
continue;
}
if (strstr(line,automation_user_search) != NULL){
automation_user = set_conf_param(line,0);
}
else if (strstr(line,automation_log_path_search) != NULL){
automation_log_path = set_conf_param(line,1);
}
else if (db_type!=NULL && strcasestr(line,db_type) != NULL){
found_db = 1;
}
else if (strstr(line,db_type_search) != NULL){
db_type = set_conf_param(line,0);
}
else if (found_db && db_host==NULL && strstr(line,db_host_search) != NULL){
db_host = set_conf_param(line,0);
}
else if (found_db && db_user==NULL && strstr(line,db_user_search) != NULL){
db_user = set_conf_param(line,0);
}
else if (found_db && db_password==NULL && strstr(line,db_password_search) != NULL){
db_password = set_conf_param(line,0);
}
else if (found_db && db_schemata==NULL && strstr(line,db_schemata_search) != NULL){
db_schemata = set_conf_param(line,0);
}
}
fclose(fp);
if (line)
free(line);
if (automation_user==NULL){
automation_user = "root";
}
//Fork the Parent Process
pid = fork();
if (pid < 0) { exit(EXIT_FAILURE); }
//We got a good pid, Close the Parent Process
if (pid > 0) { exit(EXIT_SUCCESS); }
//Change File Mask
umask(0);
//Create a new Signature Id for our child
sid = setsid();
if (sid < 0) { exit(EXIT_FAILURE); }
//Close Standard File Descriptors
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
//----------------
//Main Process
//----------------
while(1){
process(automation_user,automation_path,automation_log_path,db_type,db_host,db_user,db_password,db_schemata); //Run our Process
sleep(60); //Sleep for 60 seconds
}
//Close the log
closelog ();
exit(EXIT_FAILURE);
}
You don't appear to have made any effort to write a minimal, complete verifiable example.
This is just a dump of your whole program, including dependencies on a config file you don't provide, and databases whose schemata you don't show. None of which are even relevant to your question.
At least you edited out the company name, but for reference it's still visible in the edit history.
threads in C are writing each other stdout
I can't see any threads in your program, and you don't fork threads anyway - what you have are child processes
... are writing each other stdout
well, stdout is inherited from the parent, so all children (and their sudo/bash/perl/whatever children) are writing to the same place. The writes themselves are locked, but can be interleaved.
If you want their output not to be interleaved, have them write to different places, and figure out how to display/combine them later.
Per-child-process temporary files are a popular choice, but note the parent will have to track child process completion in order to know when to print and then delete each file. Also, you should probably detach child processes from the terminal, close stdin, and consider what to do with stderr.
Is this a memory leak?
No.
I guess (after a quick glimpse) that the problem is in
execv("/usr/bin/sudo",args);
you should make sure that only one instance is running.
this can be done by mutual exclusion.
A good description is on:
http://www.thegeekstuff.com/2012/05/c-mutex-examples/
In short:
// create a global mutex variable
pthread_mutex_t lock;
// initialise it in main
pthread_mutex_init(&lock, NULL);
// enclose your critical path
pthread_mutex_lock(&lock);
execv("/usr/bin/sudo",args);
pthread_mutex_unlock(&lock);
Hope it Helps
,This is a rather long question so bear with me. What I'm trying to do is run two processes at the same time. Each process will read a file foo.txt find the last number, increment it and add it back to the file. Since there will be obvious race conditions, I'm trying to implement Peterson's solution to avoid it.
This is all done in an Minix3 environment. I have already defined a variable shared_val which is initialised for every process which runs on the system. And two syscalls, get_sv which returns the value of shared_val and set_sv which sets a user value to the shared_val.
I'm using the values of shared_val of each process to implement the Peterson's solution. I'm also writing the two process ID's to a .txt file config.txt.
The code compiles okay, but it doesn't write to foo.txt. Can someone explain why it could be happening?
#include <stdio.h>
#include <stdlib.h>
#include "sys/types.h"
#include <unistd.h>
#include <strings.h>
#define MAX 10000
int main(int argc, char *argv[])
{
int yourPID, status = 0, tempid, temp, temp1, i = 0, times;
int ch[10];
int a, b, tempo, x, y;
FILE *fp, *fp1;
times = atoi((argv[1]));
ch[i++] = getpid();
fp = fopen(argv[3], "a");
while(i>=0)
{
fprintf(fp, "%d", ch[1]);
i--;
}
fclose(fp);
if(yourPID == ch[0])
{
set_sv(0, &status);
}
if(yourPID == ch[1])
{
set_sv(0, &status);
}
do
{
yourPID = getpid();
if(yourPID == ch[0])
{
temp = get_sv(ch[0], &status);
}
if(yourPID == ch[1])
{
temp1 = get_sv(ch[1], &status);
}
sleep(1);
a = ~temp & ~temp1;
b = temp & temp1;
sleep(1);
if(yourPID == ch[0] && ((~a & ~b) == 0))
{
char ch1[MAX], len, pos;
fp1 = fopen(argv[2], "r");
while(!feof(fp))
{
fscanf(fp1,"%s", ch1);
}
fclose(fp1);
len = strlen(ch1);
pos = len - 1;
tempo = ch1[pos] + 1;
fp1 = fopen(argv[2], "a");
fprintf(fp1, "%c", tempo);
fclose(fp1);
tempo = get_sv(yourPID, &status);
if(tempo == 0)
{
tempo = 1;
set_sv(tempo, &status);
}
if(tempo == 1)
{
tempo = 0;
set_sv(tempo, &status);
}
sleep(1);
continue;
}
if(yourPID == ch[1] && ((~a & ~b) == 1))
{
char ch1[MAX], len, pos;
fp1 = fopen(argv[2], "r");
while(!feof(fp1))
{
fscanf(fp1, "%s", ch1);
}
fclose(fp1);
len = strlen(ch1);
pos = len - 1;
tempo = ch[pos] + 1;
fp1 = fopen(argv[2], "a");
fprintf(fp1, "%c", tempo);
fclose(fp1);
tempo = get_sv(yourPID, &status);
if(tempo == 1)
{
tempo = 0;
set_sv(tempo, &Status);
}
else
{
tempo = 1;
set_sv(tempo, &status);
}
sleep(1);
continue;
}
times = times - 1;
}while(times > 0);
return 0;
}
I've added sleep(1) statements throughout the code, to give the other process time to catch up.
The bash commands I've used for this are : ./safe_increment 5 foo.txt config.txt & ./safe_increment 5 foo.txt config.txt
Where 5 is the number of times each process will write to the file.
This shouldn't even compile -- the first line that declares yourPID has an error after the initializer for status, and I don't see the declaration of i. This may be a cut-n-paste error, but there are a lot of subsequent things that it's not clear what you're trying to do, or that simply won't work.
In general, just sleep()ing won't give you reliable interprocess synchronization.
I need to read a file from a separate thread, to avoid hiccups in the flow of my opengl program. I already do that for loading textures and blending them using global variables, that works fine.
However now I need some separate threads to read small data files.
I have created a struct, which basically contains 2 args and 1 result.
It seems I can't do that or I got it wrong somewhere (or in many places)
Here is the sample code of my non proof of concept:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#define content_file "/home/tias/content.txt" //this file contains "foobar!!"
typedef struct {
int* reading; // 0 = not reading , 1 = reading , 2 = finished reading
char* content; // content of the file
char* file; // file to read
} struct_file_content;
struct_file_content first_file;
void *thread_read_file ( void* data ) {
struct_file_content *thisdata = (struct_file_content*) data;
long length;
int i = 0;
char readchar;
char * thiscontent = 0;
int reading_finished = 2;
int *ptr_to_reading_finished = (int*)malloc(sizeof(int));
ptr_to_reading_finished = &reading_finished;
fprintf(stdout,"thread_Read_file called with file: %s and reading: %u\n",(*thisdata).file,*(*thisdata).reading);
FILE * f = fopen ((*thisdata).file, "r");
if (f) {
fseek (f, 0, SEEK_END);
length = ftell (f);
fseek (f, 0, SEEK_SET);
if (length > 39) {
fprintf(stderr,"file %s is too big\n",(*thisdata).file);
exit (1);
}
thiscontent = (char*) malloc (length*sizeof(char));
if ( thiscontent ) {
fread (thiscontent, 1, length, f);
}
fclose (f);
thisdata->content = thiscontent;
} else {
fprintf (stderr, "cannot read file %s\n",(*thisdata).file);
exit (1);
}
sleep(1);
thisdata->reading = ptr_to_reading_finished;
fprintf(stdout,"finished reading: %u\n",*(*thisdata).reading);
fprintf(stdout,"content: %s\n",thiscontent);
pthread_exit(NULL);
return NULL;
}
main()
{
pthread_t thread1;
int filename_length = strlen(content_file);
int rfinish = 2;
int rbegin = 1;
first_file.reading = (int*) malloc(sizeof(int));
first_file.file = (char*) malloc((filename_length+1)*sizeof(char));
first_file.reading = &rbegin;
strcpy(first_file.file,content_file);
pthread_create( &thread1, NULL, thread_read_file, (void*) &first_file);
while ( *(first_file.reading) != 2 ) {
fprintf(stdout,"still reading, reading: %u\n",*(first_file.reading));
sleep(1);
}
fprintf(stdout,"exited control loop with file: %s, reading: %u, content: %s\n",first_file.file, *(first_file.reading), first_file.content);
}
Here's the result:
~/repository/thread/test$ ./tt
still reading, reading: 1
thread_Read_file called with file: /home/tias/content.txt and reading: 1
still reading, reading: 1
still reading, reading: 1
still reading, reading: 1
finished reading: 2
content: foobar!!
still reading, reading: 0
still reading, reading: 0
I was expecting reading = 2 to get out of the loop, instead it is 0.
Any idea on what I have to modify to make it work?
I have read about mutex and so, may be it is the way?
I had found my solution elegant and not working, your help is greatly appreciated.
typedef struct {
int* reading; // 0 = not reading , 1 = reading , 2 = finished reading
char* content; // content of the file
char* file; // file to read
} struct_file_content;
Why is reading a pointer type? It is used in the code as a flag, there is absolutely no need to make it a pointer, especially given that you dynamically allocate memory for this structure field. It complicates the design and is unnecessary. Go ahead and make that an int, not a pointer:
typedef struct {
int reading; // 0 = not reading , 1 = reading , 2 = finished reading
char *content; // content of the file
char *file; // file to read
} struct_file_content;
This simplifies the code in main() used to set up first_file:
first_file.reading = 1;
first_file.content = NULL;
first_file.file = content_file;
Note that there is no need to have first_file.file be dynamically allocated memory, since you know the file name (and size) at compile time. Keep things simple.
Next, you ignore a possible error return value from pthread_create(3). It returns non-zero in case of failure, and you should check for that. Something like this will do:
pthread_t thread1;
int thread_res = pthread_create(&thread1, NULL, thread_read_file, &first_file);
if (thread_res != 0) {
fprintf(stderr, "pthread_create(3) error: %s\n", strerror(thread_res));
exit(EXIT_FAILURE);
}
The code to wait for reading to finish is buggy and racy, you need to either synchronize access to the reading field of struct_file_content with a mutex, or properly wait for the thread to terminate before accessing the first_file again. Since the code does nothing but wait for the thread, pthread_join(3) is a much more reasonable choice here. You'd do something like:
int join_res = pthread_join(thread1, NULL);
if (join_res != 0) {
fprintf(stderr, "pthread_join(3) error: %s\n", strerror(join_res));
exit(EXIT_FAILURE);
}
Inside thread_read_file(), you probably want pthread_exit(3) when handling errors instead of exit(2), since the latter will terminate the entire process, not just the local thread. You also need to handle malloc(3) errors.
Here's the code with all of these issues addressed:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#define content_file "/home/tias/content.txt" //this file contains "foobar!!"
typedef struct {
int reading; // 0 = not reading , 1 = reading , 2 = finished reading
char *content; // content of the file
char *file; // file to read
} struct_file_content;
struct_file_content first_file;
void *thread_read_file(void *data) {
struct_file_content *thisdata = data;
long length;
char *thiscontent = NULL;
fprintf(stdout,"thread_Read_file called with file: %s and reading: %u\n", thisdata->file, thisdata->reading);
FILE *f = fopen (thisdata->file, "r");
if (f) {
fseek(f, 0, SEEK_END);
length = ftell(f);
fseek (f, 0, SEEK_SET);
if (length > 39) {
fprintf(stderr,"file %s is too big\n", thisdata->file);
pthread_exit(NULL);
}
thiscontent = malloc(length);
if (thiscontent) {
fread(thiscontent, 1, length, f);
} else {
perror("malloc(3) error");
pthread_exit(NULL);
}
fclose(f);
thisdata->content = thiscontent;
} else {
fprintf(stderr, "cannot open file %s\n", thisdata->file);
pthread_exit(NULL);
}
thisdata->reading = 2;
fprintf(stdout, "finished reading: %u\n", thisdata->reading);
fprintf(stdout, "content: %s\n", thiscontent);
return NULL;
}
int main(void) {
first_file.reading = 1;
first_file.content = NULL;
first_file.file = content_file;
pthread_t thread1;
int thread_res = pthread_create(&thread1, NULL, thread_read_file, &first_file);
if (thread_res != 0) {
fprintf(stderr, "pthread_create(3) error: %s\n", strerror(thread_res));
exit(EXIT_FAILURE);
}
int join_res = pthread_join(thread1, NULL);
if (join_res != 0) {
fprintf(stderr, "pthread_join(3) error: %s\n", strerror(join_res));
exit(EXIT_FAILURE);
}
fprintf(stdout, "Read file %s, reading: %u, content: %s\n", first_file.file, first_file.reading, first_file.content);
return 0;
}
UPDATE
From the comments, it seems like you want to do some additional processing and periodically test (at your convenience) whether the thread is done reading. Your approach of using a flag to test for termination is mostly correct, but you should synchronize access to the reading field of struct_file_content to make sure that you always get consistent values. As such, I suggest adding a mutex to struct_file_content that is used to control concurrent access to the reading field. You should lock the mutex every time you need to read or update reading.
So, the structure definition becomes:
typedef struct {
pthread_mutex_t read_mutex; // synchronize access to reading flag
int reading; // 0 = not reading , 1 = reading , 2 = finished reading
char *content; // content of the file
char *file; // file to read
} struct_file_content;
Then, as part of initializing struct_file_content, you need to remember to initialize the mutex. Here's how you'd do it in main():
int mutex_err = pthread_mutex_init(&first_file.read_mutex, NULL);
if (mutex_err != 0) {
fprintf(stderr, "pthread_mutex_init(3) error: %s\n", strerror(mutex_err));
exit(EXIT_FAILURE);
}
first_file.reading = 1;
first_file.content = NULL;
first_file.file = content_file;
Now, the loop in main() simply locks the mutex, checks the status of the reading field (breaking out if it is equal to 2), and unlocks the mutex. Something like:
int read_done = 0;
while (!read_done) {
mutex_err = pthread_mutex_lock(&first_file.read_mutex);
if (mutex_err != 0) {
fprintf(stderr, "pthread_mutex_lock(3) error: %s\n", strerror(mutex_err));
exit(EXIT_FAILURE);
}
/* Reading is finished when first_file.reading == 2 */
read_done = (first_file.reading == 2);
if (first_file.reading != 2)
printf("Still reading, reading: %u\n", first_file.reading);
mutex_err = pthread_mutex_unlock(&first_file.read_mutex);
if (mutex_err != 0) {
fprintf(stderr, "pthread_mutex_unlock(3) error: %s\n", strerror(mutex_err));
}
}
Of course, you also need to update the thread function to lock the mutex before modifying reading:
void *thread_read_file(void *data) {
struct_file_content *thisdata = data;
int mutex_res;
long length;
char *thiscontent = NULL;
mutex_res = pthread_mutex_lock(&thisdata->read_mutex);
if (mutex_res != 0) {
fprintf(stderr, "thread_read_file() failed to acquire mutex: %s\n", strerror(mutex_res));
pthread_exit(NULL);
}
fprintf(stdout, "thread_read_file() called with file: %s and reading: %u\n", thisdata->file, thisdata->reading);
mutex_res = pthread_mutex_unlock(&thisdata->read_mutex);
if (mutex_res != 0) {
fprintf(stderr, "thread_read_file() failed to release mutex: %s\n", strerror(mutex_res));
pthread_exit(NULL);
}
FILE *f = fopen(thisdata->file, "r");
if (f) {
fseek(f, 0, SEEK_END);
length = ftell(f);
fseek (f, 0, SEEK_SET);
if (length > 39) {
fprintf(stderr, "file %s is too big\n", thisdata->file);
pthread_exit(NULL);
}
thiscontent = malloc(length);
if (thiscontent) {
fread(thiscontent, 1, length, f);
} else {
perror("malloc(3) error");
pthread_exit(NULL);
}
fclose(f);
thisdata->content = thiscontent;
} else {
fprintf(stderr, "cannot open file %s\n", thisdata->file);
pthread_exit(NULL);
}
mutex_res = pthread_mutex_lock(&thisdata->read_mutex);
if (mutex_res != 0) {
fprintf(stderr, "thread_read_file() failed to acquire mutex: %s\n", strerror(mutex_res));
pthread_exit(NULL);
}
thisdata->reading = 2;
fprintf(stdout, "finished reading: %u\n", thisdata->reading);
mutex_res = pthread_mutex_unlock(&thisdata->read_mutex);
if (mutex_res != 0) {
fprintf(stderr, "thread_read_file() failed to release mutex: %s\n", strerror(mutex_res));
pthread_exit(NULL);
}
fprintf(stdout, "content: %s\n", thiscontent);
return NULL;
}
That should be enough. Here's the full code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#define content_file "/home/tias/content.txt" //this file contains "foobar!!"
typedef struct {
pthread_mutex_t read_mutex; // synchronize access to reading flag
int reading; // 0 = not reading , 1 = reading , 2 = finished reading
char *content; // content of the file
char *file; // file to read
} struct_file_content;
struct_file_content first_file;
void *thread_read_file(void *data) {
struct_file_content *thisdata = data;
int mutex_res;
long length;
char *thiscontent = NULL;
mutex_res = pthread_mutex_lock(&thisdata->read_mutex);
if (mutex_res != 0) {
fprintf(stderr, "thread_read_file() failed to acquire mutex: %s\n", strerror(mutex_res));
pthread_exit(NULL);
}
fprintf(stdout, "thread_read_file() called with file: %s and reading: %u\n", thisdata->file, thisdata->reading);
mutex_res = pthread_mutex_unlock(&thisdata->read_mutex);
if (mutex_res != 0) {
fprintf(stderr, "thread_read_file() failed to release mutex: %s\n", strerror(mutex_res));
pthread_exit(NULL);
}
FILE *f = fopen(thisdata->file, "r");
if (f) {
fseek(f, 0, SEEK_END);
length = ftell(f);
fseek (f, 0, SEEK_SET);
if (length > 39) {
fprintf(stderr, "file %s is too big\n", thisdata->file);
pthread_exit(NULL);
}
thiscontent = malloc(length);
if (thiscontent) {
fread(thiscontent, 1, length, f);
} else {
perror("malloc(3) error");
pthread_exit(NULL);
}
fclose(f);
thisdata->content = thiscontent;
} else {
fprintf(stderr, "cannot open file %s\n", thisdata->file);
pthread_exit(NULL);
}
mutex_res = pthread_mutex_lock(&thisdata->read_mutex);
if (mutex_res != 0) {
fprintf(stderr, "thread_read_file() failed to acquire mutex: %s\n", strerror(mutex_res));
pthread_exit(NULL);
}
thisdata->reading = 2;
fprintf(stdout, "finished reading: %u\n", thisdata->reading);
mutex_res = pthread_mutex_unlock(&thisdata->read_mutex);
if (mutex_res != 0) {
fprintf(stderr, "thread_read_file() failed to release mutex: %s\n", strerror(mutex_res));
pthread_exit(NULL);
}
fprintf(stdout, "content: %s\n", thiscontent);
return NULL;
}
int main(void) {
int mutex_err = pthread_mutex_init(&first_file.read_mutex, NULL);
if (mutex_err != 0) {
fprintf(stderr, "pthread_mutex_init(3) error: %s\n", strerror(mutex_err));
exit(EXIT_FAILURE);
}
first_file.reading = 1;
first_file.content = NULL;
first_file.file = content_file;
pthread_t thread1;
int thread_res = pthread_create(&thread1, NULL, thread_read_file, &first_file);
if (thread_res != 0) {
fprintf(stderr, "pthread_create(3) error: %s\n", strerror(thread_res));
exit(EXIT_FAILURE);
}
int read_done = 0;
while (!read_done) {
mutex_err = pthread_mutex_lock(&first_file.read_mutex);
if (mutex_err != 0) {
fprintf(stderr, "pthread_mutex_lock(3) error: %s\n", strerror(mutex_err));
exit(EXIT_FAILURE);
}
/* Reading is finished when first_file.reading == 2 */
read_done = (first_file.reading == 2);
if (first_file.reading != 2)
printf("Still reading, reading: %u\n", first_file.reading);
mutex_err = pthread_mutex_unlock(&first_file.read_mutex);
if (mutex_err != 0) {
fprintf(stderr, "pthread_mutex_unlock(3) error: %s\n", strerror(mutex_err));
}
}
/* Here we don't need to lock because the thread has finished and no other thread is
* using this struct
*/
fprintf(stdout, "Read file %s, reading: %u, content: %s\n", first_file.file, first_file.reading, first_file.content);
free(first_file.content);
mutex_err = pthread_mutex_destroy(&first_file.read_mutex);
if (mutex_err != 0) {
fprintf(stderr, "Warning: Error destroying mutex: %s\n", strerror(mutex_err));
}
return 0;
}
Note that I added cleanup code in the end of main(). Even though it is not necessary (because the program is about to terminate), it is there to make sure you don't forget what kind of cleanup needs to be done once a thread terminates.