Function main received incorrect values from the subroutine - c

This code does not work - issues with passing the data from subroutine to main and allocating memory.
Computations are correct inside the subroutine but the values received by the main are incorrect - variables in main has random values, eg sRates.
#include <stdio.h>
#include <malloc.h>
#include "sndfile.h"
int main(int argc, char *argv[])
{
int sRates , sRatem , ret;
long nSamples=0, nSamplem;
float *datas, *datam;
printf("Read Test\n");
if (argc != 3) {
fprintf(stderr, "Expecting two wav file as argument\n");
return 1;
}
ret = readWav(argv[1], nSamples, sRates, &datas );
if (ret != 0) {
printf("Error\n");
}
// Output Info
printf("Read %ld frames from %s, Sample rate: %d, Length: %fs\n",
nSamples, argv[1], sRates, (float)nSamples/sRates);
printf("Read %ld frames from %s, Sample rate: %d, Length: %fs\n",
nSamples, argv[1], sRates, (float)nSamples/sRates);
// free(datas);
return 0;
}
int readWav(char *fname, long *numFrames, int *sRate, float **buffer )
{
// Open sound file
SF_INFO sndInfo;
SNDFILE *sndFile = sf_open(fname, SFM_READ, &sndInfo);
if (sndFile == NULL) {
fprintf(stderr, "Error reading source file '%s': %s\n", fname, sf_strerror(sndFile));
return 1;
}
printf("1Format of the audio file = %i\n", sndInfo.format);
printf("2Number of channels = %i\n", sndInfo.channels);
printf("3Sample Rate = %d\n", sndInfo.samplerate);
printf("4 Sample count = %ld\n", (long)sndInfo.frames);
sRate= sndInfo.samplerate;
// Allocate memory
buffer = (float *)malloc(sndInfo.frames * sndInfo.channels * sizeof(float));
if (buffer == NULL) {
fprintf(stderr, "Could not allocate memory for file\n");
sf_close(sndFile);
return 1;
}
// Load data
numFrames = sf_readf_float(sndFile, buffer, sndInfo.frames);
// Check correct number of samples loaded
if (numFrames != sndInfo.frames) {
fprintf(stderr, "Did not read enough frames for source\n");
sf_close(sndFile);
free(buffer);
// return 1;
}
else {
printf("Successfully read file\n");
numFrames = sndInfo.frames;
}
// Output Info
printf("Read %ld frames from %s, Sample rate: %d, Length: %fs\n",
// numFrames, fname, sndInfo.samplerate, (float)numFrames/sndInfo.samplerate);
numFrames, fname, sRate, (float)numFrames/sndInfo.samplerate);
sf_close(sndFile);
// return(buffer);
return(0);
}

In C all arguments are passed by-value, so if you want a by-ref-like argument you must pass a pointer. And since you want to return a float* you need to pass a float**.
Actually you are passing that, but you are not using it correctly (please use -Wall or equivalent for your compiler to enable warnings).
The code should like more or less like this:
int readWav(const char *fname, long *numFrames, int *sRate, float **buffer)
{
*buffer = malloc(...);
//if you do not feel comfortable writing `*buffer` everywhere:
float *data = *buffer;
///....
*numFrames = sf_readf_float(...);
///....
*sRate = sndInfo.samplerate;
///....
}
int main()
{
long nSamples;
int sRates;
float *datas;
ret = readWav(argv[1], &nSamples, &sRates, &datas);
//...
}

You have several errors in your code
You don't declare readWav() and you call it from main(), it's working by coincidence, namely because it does return int.
You are passing the address of datas to readWav(), note that &datas has type float ** and readWav() is expecting a float *.
If you had compiler warnings turned on, youl'd have noticed this.
You are passing the value of nSamples and sRate to readWav() and you are expecting the nSamples and sRate in your main to get initialized, you need to pass their addresses instead.
You check the return value of readWav() and yet you still try to acces the datas pointer.
This is a fixed version of your code
#include <stdio.h>
#include "sndfile.h"
int readWav(const char *const fname, long *numFrames, int *sRate, float **buffer);
int main(int argc, char *argv[])
{
int sRates, sRatem, ret;
long nSamples = 0, nSamplem;
float *datas, *datam;
printf("Read Test\n");
if (argc != 3) {
fprintf(stderr, "Expecting two wav file as argument\n");
return 1;
}
ret = readWav(argv[1], &nSamples, &sRates, &datas);
if (ret != 0) {
printf("Error\n");
return 1;
}
// Output Info
printf("Read %ld frames from %s, Sample rate: %d, Length: %fs\n",
nSamples, argv[1], sRates, (float)nSamples/sRates);
printf("Read %ld frames from %s, Sample rate: %d, Length: %fs\n",
nSamples, argv[1], sRates, (float)nSamples/sRates);
free(datas);
return 0;
}
int readWav(const char *const fname, long *numFrames, int *sRate, float **buffer)
{
// Open sound file
SF_INFO sndInfo;
if ((sRate == NULL) || (numFrames == NULL) || (buffer == NULL)) {
fprintf(stderr, "Invalid arguments passed to readWav()\n");
return 1;
}
SNDFILE *sndFile = sf_open(fname, SFM_READ, &sndInfo);
if (sndFile == NULL) {
fprintf(stderr, "Error reading source file '%s': %s\n", fname, sf_strerror(sndFile));
return 1;
}
printf("1Format of the audio file = %i\n", sndInfo.format);
printf("2Number of channels = %i\n", sndInfo.channels);
printf("3Sample Rate = %d\n", sndInfo.samplerate);
printf("4 Sample count = %ld\n", (long)sndInfo.frames);
// Allocate memory
*buffer = malloc(sndInfo.frames * sndInfo.channels * sizeof(float));
if (*buffer == NULL) {
fprintf(stderr, "Could not allocate memory for file\n");
sf_close(sndFile);
return 1;
}
*sRate = sndInfo.samplerate;
// Load data
*numFrames = sf_readf_float(sndFile, *buffer, sndInfo.frames);
// Check correct number of samples loaded
if (*numFrames != sndInfo.frames) {
fprintf(stderr, "Did not read enough frames for source\n");
sf_close(sndFile);
free(*buffer);
}
else {
printf("Successfully read file\n");
*numFrames = sndInfo.frames;
}
// Output Info
printf("Read %ld frames from %s, Sample rate: %d, Length: %fs\n",
*numFrames, fname, *sRate, (float)*numFrames/sndInfo.samplerate);
sf_close(sndFile);
return(0);
}
Tip: You should try to write your function in such a way that it has only one exit point, I like using goto for that, despite what religious programmers believe about goto, it makes your code more readable consistent and maintainable.
What I mean is you can have a label where you return the error code from the function and do all the cleanup, something like this
int function()
{
/* set errorCode */
if (firstFailureCondition == 1)
goto cleanup;
if (secondFailureCondition == 1)
goto cleanup;
.
.
.
if (nthFailureCondition == 2)
goto cleanup;
cleanup:
/* do your cleanup */
return errorCode;
}

Related

SDL audio capture callbacks slower than playback

SDL capture audio callbacks seem to be called once for every 12 playback callbacks. Am I doing something wrong? This feels like an SDL or PulseAudio bug.
The program below prints "Reading audio..." once every ~12 "Writing audio..." prints. Tested via running the command directly, through gdb, and through Valgrind.
I've tried this in both C and Golang (using github.com/veandco/go-sdl2/sdl), on the same machine.
C code:
// A test program to copy audio in (microphone) to audio out (speaker) via SDL.
//
// Compile: cc inout.c -o inout -lSDL2
// Run: ./inout
#include <SDL2/SDL.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#define BUF_SIZE 1024
// Stereo float32 samples.
static uint8_t saved[BUF_SIZE*2*sizeof(float)];
// Copies audio callback data into the saved buffer.
void audioReader(void* udata, uint8_t* buf, int len) {
fprintf(stderr, "Reading audio: %d -> %ld bytes\n", len, sizeof(saved));
memcpy(saved, buf, len);
}
// Copies saved audio data into the callback buffer.
void audioWriter(void* udata, uint8_t* buf, int len) {
fprintf(stderr, "Writing audio: %ld -> %d bytes\n", sizeof(saved), len);
memcpy(buf, saved, len);
}
// List all devices of the given type, and return the name of the first or NULL.
// Caller must free the returned pointer.
char* ChooseDevice(int is_capture) {
int dev_cnt = SDL_GetNumAudioDevices(is_capture);
if (dev_cnt < 1) {
fprintf(stderr, "No %s devices: %s\n", is_capture ? "capture" : "playback", SDL_GetError());
return NULL;
}
printf("%s devices:\n", is_capture ? "capture" : "playback");
char* dev_name = NULL;
for (int i = 0; i < dev_cnt; i++) {
printf("%c %s\n", !dev_name ? '*' : ' ', SDL_GetAudioDeviceName(i, is_capture));
if (!dev_name) {
const char* tmp = SDL_GetAudioDeviceName(i, is_capture);
dev_name = malloc(strlen(tmp)+1);
strcpy(dev_name, tmp);
}
}
if (!dev_name) {
fprintf(stderr, "No %s devices\n", is_capture ? "capture" : "playback");
}
return dev_name;
}
// Opens and unpauses the first device of the given type, returning its ID, or
// returns 0.
SDL_AudioDeviceID OpenDevice(int is_capture) {
char* dev_name = ChooseDevice(is_capture);
if (!dev_name) return 0;
SDL_AudioSpec spec;
SDL_memset(&spec, 0, sizeof(spec));
spec.freq = 48000;
spec.format = AUDIO_F32;
spec.channels = 2;
spec.samples = BUF_SIZE;
spec.callback = is_capture ? audioReader : audioWriter;
SDL_AudioDeviceID dev_id = SDL_OpenAudioDevice(dev_name, is_capture, &spec, NULL, 0);
if (dev_id == 0) {
fprintf(stderr, "Failed to open %s device %s: %s\n", is_capture ? "input" : "output", dev_name, SDL_GetError());
return 0;
}
free(dev_name);
SDL_PauseAudioDevice(dev_id, SDL_FALSE);
return dev_id;
}
int main(int argc, char** argv) {
SDL_memset(saved, 0, sizeof(saved));
if (SDL_Init(SDL_INIT_AUDIO) < 0) {
fprintf(stderr, "Failed to initialize SDL audio: %s\n", SDL_GetError());
return 1;
}
SDL_AudioDeviceID in_dev_id = OpenDevice(/* is_capture = */ SDL_TRUE);
if (in_dev_id == 0) return 1;
SDL_AudioDeviceID out_dev_id = OpenDevice(/* is_capture = */ SDL_FALSE);
if (out_dev_id == 0) return 1;
SDL_Delay(10000); // 10 seconds
SDL_CloseAudioDevice(in_dev_id);
SDL_CloseAudioDevice(out_dev_id);
SDL_Quit();
return 0;
}

Segmentation fault instead of showing message - reading from a file by using pointers in c

I wrote a program, which reads from a file. I use a condition in which I print that the array is too big, but when I use a too big array instead of showing this message I have segmentation fault.
This is my program
#include <stdio.h>
#include <stdlib.h>
#define N 10000 // Maximum array size
int _strlen(char *array) {
int i;
for (i = 0; array[i] != '\0'; ++i);
return i;
}
int readText(FILE *wp, char *s, int max) {
int sum = 0;
if (_strlen(s) > max) {
printf("This array is too big. Maximum size is %d", max);
} else {
while ((*s++ = fgetc(wp)) != EOF) {
sum++;
}
*(s-1) = '\0';
}
return sum;
}
int main(int argc, char *argv[]) {
FILE *wz, *wc;
char *s;
char array[N];
s = array;
if (argc != 3) {
printf("Wrong arguments number\n");
printf("I should run this way:\n");
printf("%s source result\n",argv[0]);
exit(1);
}
if ((wz = fopen(argv[1], "r")) == NULL) {
printf("Open error %s\n", argv[1]);
exit(1);
}
if ((wc = fopen(argv[2], "w")) == NULL) {
printf("Open error %s\n", argv[2]);
exit(2);
}
fprintf(wc, "Read text from file source.txt");
readText(wz, s, 10000);
return 0;
}
In output I want to have: This array is too big. Maximum size is %d
Instead of Segmentation fault core dumped
In addition, I want to say that the program is when I use a smaller array, but I want to show the user a proper message when he uses too big array instead of segmentation fault.
Thanks, I change my program in that way. The only problem is that this program check the if condition in every while loop so this program could be slow.
int readText(FILE *wp, char *s, int max) {
int sum = 0;
if (_strlen(s) > max) {
printf("This array is too big. Maximum size is %d", max);
} else {
while ((*s++ = fgetc(wp)) != EOF) {
sum++;
if (sum > max) {
printf("This array is too big. Maximum size is %d", max);
break;
}
}
*(s-1) = '\0';
}
return sum;
}
The remarks / other answer solve your undefined behavior (segmentation fault in your case).
The only problem is that this program check the if condition in every while loop so this program could be slow.
Your program is not slow because of a 'if' but because you read the file char per char.
Using stat or equivalent function you can get the size of the file to read it throw only one fread :
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#define N 10000 // Maximum array size
int main(int argc, char *argv[]) {
char array[N];
FILE *wz, *wc;
struct stat st;
off_t sz;
if (argc != 3) {
printf("Wrong arguments number\n"
"I should run this way:\n"
"%s source result\n", argv[0]);
exit(1);
}
if ((wz = fopen(argv[1], "r")) == NULL) {
printf("Cannot open %s to read : %s\n", argv[1], strerror(errno));
exit(1);
}
if (stat(argv[1], &st) == -1) {
printf("Cannot get stat of %s : %s\n", argv[1], strerror(errno));
exit(1);
}
if (st.st_size > N-1) {
printf("This array is too big. Maximum size is %d", N-1);
sz = N-1;
}
else
sz = st.st_size;
if (fread(array, 1, sz, wz) != sz) {
printf("cannot read %s : %s", argv[1], strerror(errno));
fclose(wz); /* for valgrind end test etc */
exit(1);
}
array[sz] = 0;
fclose(wz);
if ((wc = fopen(argv[2], "w")) == NULL) {
printf("Cannot open %s to write : %s\n", argv[2], strerror(errno));
fclose(wz); /* for valgrind end test etc */
exit(2);
}
/* ... */
fclose(wc);
return 0;
}
Knowing the size of the file allows to remove that limitation to a constant size and try to read the file while you can allocate enough memory for :
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
int main(int argc, char *argv[]) {
char * array;
FILE *wz, *wc;
struct stat st;
if (argc != 3) {
printf("Wrong arguments number\n"
"I should run this way:\n"
"%s source result\n", argv[0]);
exit(1);
}
if ((wz = fopen(argv[1], "r")) == NULL) {
printf("Cannot open %s to read : %s\n", argv[1], strerror(errno));
exit(1);
}
if (stat(argv[1], &st) == -1) {
printf("Cannot get stat of %s : %s\n", argv[1], strerror(errno));
exit(2);
}
if ((array = malloc(st.st_size + 1)) == NULL) {
printf("Not enough memory to memorize the file %s\n", argv[1]);
exit(3);
}
if (fread(array, 1, st.st_size, wz) != st.st_size) {
printf("cannot read %s : %s", argv[1], strerror(errno));
fclose(wz); /* for valgrind end test etc */
free(array); /* for valgrind etc */
exit(4);
}
array[st.st_size] = 0;
fclose(wz);
if ((wc = fopen(argv[2], "w")) == NULL) {
printf("Cannot open %s to write : %s\n", argv[2], strerror(errno));
free(array); /* for valgrind etc */
exit(5);
}
/* ... */
fclose(wc);
free(array); /* for valgrind etc */
return 0;
}
Anyway because of the usage of the program "source result" may be you want to copy the file specified by argv[1] in the file specified by argv[2], in that case better to read and write block by block rather than to read all to not use a lot of memory for nothing and to manage the case the input file size is greater than the memory size.
You cannot measure the length of the destination array with _strlen(s), the size is given as an argument and reading an uninitialized array with _strlen() has undefined behavior.
Furthermore, you store fgetc(fp) to *s++ before testing for EOF. This is incorrect in all cases:
if char type is signed, EOF cannot be distinguished from a valid byte value of \377.
if char is unsigned, EOF cannot be tested because it has been converted as a char value of 0xff, hence the loop runs forever, writing beyond the end of the destination array until this causes a crash.
You simply want to add a test in the reading loop to stop reading bytes from the file when the buffer is full and read the bytes into an int variable so you can test for end of file reliably.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#define N 10000 // Maximum array size
int readText(FILE *wp, char *s, int max) {
int i = 0, c;
while (i < max - 1 && (c = fgetc(wp)) != EOF) {
s[i++] = c;
}
s[i] = '\0';
return i;
}
int main(int argc, char *argv[]) {
FILE *wz, *wc;
char array[N];
int nread;
if (argc != 3) {
printf("Wrong arguments number\n");
printf("I should run this way:\n");
printf("%s source result\n", argv[0]);
exit(1);
}
if ((wz = fopen(argv[1], "r")) == NULL) {
printf("Open error %s\n", argv[1]);
exit(1);
}
if ((wc = fopen(argv[2], "w")) == NULL) {
printf("Open error %s\n", argv[2]);
exit(2);
}
fprintf(wc, "Read text from file source.txt\n");
nread = readText(wz, array, N);
printf("Read %d bytes\n", nread);
return 0;
}

How to I make a temporary filename that's safe for concurrent execution?

In the following code, I need a unique filename, do some stuff with it, and let it be. It is about converting a .class file to binary, let us call it compilation.
It works perfectly when run in isolation or done 3 times at a time; however, I run into issues when I start up many multiple processes (e.g., 7) where one or more of my compilations fail.
This is the code:
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
static unsigned int numFiles = 0;
static unsigned long numBytes = 0;
FILE* rawf;
char* raw_file_name_end = ".raw_ujc";
char * rawfilename;
static void byte(unsigned char v){
if(numBytes) printf(", ");
printf((numBytes & 0x0F) ? "0x%02X" : "\n\t0x%02X", v);
fwrite(&v,sizeof(v),1,rawf);
numBytes++;
}
int main(int argc, char** argv){
const char* self = argv[0];
int c;
const char* classCvt = 0;
long len;
if(argc == 1){
fprintf(stderr, "USAGE: %s [-c <path_to_classCvt>] <file 1> [<file 2> [ <file 3> [...]]] > result.c\n", self);
return -1;
}
argv++;
argc--;
if(argv[0][0] == '-' && argv[0][1] == 'c' && !argv[0][2]){
classCvt = argv[1];
argv += 2;
argc -= 2;
}
printf("\nService optimized bytecode = {\n\t");
while(argc--){
char* filename = *argv;
rawfilename = malloc(sizeof(char) * (strlen(filename)-strlen(".class")) + sizeof(char) * strlen(raw_file_name_end)+1);
strncpy(rawfilename,filename,(strlen(filename)-strlen(".class")));
strcat(rawfilename,raw_file_name_end);
fprintf(stderr, "rawfilename after alloc: %s \n", rawfilename);
if(classCvt){
char* t;
filename = tempnam(NULL, NULL);
if(!filename){
fprintf(stderr, "%s: failed to create a tempfile: %d\n", self, errno);
return -10;
}
t = malloc(strlen(filename) + strlen(classCvt) + strlen(*argv) + 32);
if(!t){
fprintf(stderr, "%s: failed to alloc a small string. This is unlikely\n", self);
free(t);
return -11;
}
sprintf(t, "%s < %s > %s", classCvt, *argv, filename);
if(system(t)){
fprintf(stderr, "%s: system() fail: %d\n", self, errno);
free(t);
return -12;
}
free(t);
}
printf("filename is %s\n",filename);
FILE* f = fopen(filename, "r");
rawf = fopen(rawfilename, "wb");
if(filename != *argv){
unlink(filename);
free(filename);
}
if(!f){
fprintf(stderr, "%s: failed to open '%s': %d\n", self, *argv, errno);
fclose(f);
return -2;
}
if(!f){
fprintf(stderr, "%s: failed to open '%s': %d\n", self, *argv, errno);
fclose(f);
return -2;
}
if(fseek(f, 0, SEEK_END)){
fprintf(stderr, "%s: failed to seek(1) in '%s': %d\n", self, *argv, errno);
fclose(f);
return -3;
}
len = ftell(f);
if(len < 0){
fprintf(stderr, "%s: failed to tell in '%s': %d\n", self, *argv, errno);
fclose(f);
return -4;
}
if(fseek(f, 0, SEEK_SET)){
fprintf(stderr, "%s: failed to seek(2) in '%s': %d\n", self, *argv, errno);
fclose(f);
return -5;
}
if(len > 0x00FFFFFFUL){
fprintf(stderr, "%s: file '%s' is %lu bytes, while maximum allowable size is %lu.\n", self, *argv, len, 0x00FFFFFFUL);
fclose(f);
return -6;
}
byte(len >> 16);
byte(len >> 8);
byte(len);
while((c = fgetc(f)) != EOF){
byte(c);
}
numFiles++;
fclose(f);
fclose(rawf);
argv++;
}
byte(0);
byte(0);
byte(0);
printf("\n};\n");
fprintf(stderr, "%s: processed %u files, producing %lu (0x%lX) bytes of output\n", self, numFiles, numBytes, numBytes);
fprintf(stderr, "rawfilename at end: %s \n", rawfilename);
free(rawfilename);
return 0;
}
After looking around, people recommend using mkstemp(); however, as you can see, I actually do need the filename in several places.
I tried adjusting this but keep running into errors. How can I safely adjust this work method?
From the manpage for mkstemp
int mkstemp(char *template);
The mkstemp() function generates a unique temporary filename from template, creates and opens the file, and returns an open file descriptor for the file.
The last six characters of template must be "XXXXXX" and these are
replaced with a string that makes the filename unique. Since it will
be modified, template must not be a string constant, but should be
declared as a character array.
The file is created with permissions 0600, that is, read plus write
for owner only. The returned file descriptor provides both read and
write access to the file. The file is opened with the open(2) O_EXCL
flag, guaranteeing that the caller is the process that creates the
file.
so if you need the filename, you can find it in the template argument passed to mkstemp.

Passing data and retrieving data via same struct using threads

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.

Linux, reading the tape device

I'm using plain C and trying to read data from stream device (single tape)
size_t res=0;
size_t total=0;
char data[512];
FILE *f = fopen("/dev/st0","r");
if(!f)
{
perror ("Error:");
printf( "Value: %d\n", errno );
return;
}
while((res=fread(data,1, sizeof data,f))>0)
{
total+=res;
}
fclose(f);
printf("read: %ld bytes\n", total);
It doesn't work this way, so I assume there should be some specific way to do it.
I didn't find something useful in google. May be source code of mt tool can help, but again it doesn't read/write to tape.
The result is
read: 0 bytes
To read the first sizeof(data) bytes (if any) from the device specified do like so:
#define _POSIX_SOURCE /* for ferror */
#include <stdio.h>
#define DEVICENAME "/dev/st0"
int main(void)
{
int result = EXIT_SUCCESS; /* Be optimistic. */
size_t total = 0;
char data[1024];
FILE * f = fopen(DEVICENAME, "r");
if (NULL == f)
{
fprintf(stderr, "Failed to open '%s'.\n", DEVICENAME);
result = EXIT_FAILURE;
}
else
{
total = fread(data, 1, sizeof(data), f);
if (ferror(f))
{
fprintf(stderr, "Error reading from '%s'.\n", DEVICENAME);
result = EXIT_FAILURE;
}
fclose(f);
}
printf("Read %zd bytes from '%s'.\n", total, DEVICENAME);
return result;
}

Resources