Reputation: 35
I'm trying to teach myself about multi-threading and multi-process programming in C (Linux). I wrote a short program which spawns a new thread which goes to a routine that tries to do a blocking read from an empty FIFO, while the main thread continues and prints to STDOUT. (Note: I did create a FIFO using mkfifo newfifo
in my terminal before executing the program)
I was expecting the program to print to screen "Main thread", and then block while waiting for me to put data in the FIFO. Instead, the entire process is blocked, and the message "Main thread" only prints after I've put data into the FIFO.
Am I missing something here? Shouldn't the main thread continue to run even though the spawned thread is blocked? I tried a test using fork and creating a child process and got the same result (both processes blocked by read from empty FIFO).
Code is below:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <pthread.h>
#define NEWPIPE "./newfifo"
typedef struct __attribute__ ((__packed__)) {
int reserved :30;
int threadStarted :1;
int msgRcved :1;
} Status;
void* thread_routine(int fd, char* buffer, Status* myStatus)
{
int great_success = 0;
myStatus->threadStarted = 1;
printf("Side thread\n");
great_success = read(fd, buffer, sizeof(buffer));
if (great_success < 0) {
printf("pipe failed\n");
} else {
myStatus->msgRcved = 1;
}
}
void main()
{
int fd;
int cnt = 0;
char buffer[20];
Status* myStatus;
pthread_t thread_id;
myStatus = (Status*) malloc(sizeof(Status));
myStatus->reserved = 0;
myStatus->threadStarted = 0;
myStatus->msgRcved = 0;
fd = open(NEWPIPE, O_RDONLY);
pthread_create(&thread_id,
NULL,
(void *) thread_routine(fd, buffer, myStatus),
NULL);
printf("Main thread\n");
while (!myStatus->threadStarted) {
printf("Main thread: side thread started!\n");
}
while (!myStatus->msgRcved) {
sleep(1);
cnt++;
}
printf("buffer (cnt = %d): %s\n", cnt, buffer);
}
Edit: Latest Code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <pthread.h>
#define NEWPIPE "./newfifo"
struct Status {
unsigned int reserved :30;
unsigned int threadStarted :1;
unsigned int msgRcved :1;
};
void* thread_routine(void *arg)
{
int great_success = 0;
int fd;
char buffer[20];
struct Status* myStatus;
fd = open(NEWPIPE, O_RDONLY);
myStatus = arg;
myStatus->threadStarted = 1;
printf("Side thread\n");
while (1) {
great_success = read(fd, buffer, 20);
if (great_success < 0) {
printf("pipe failed\n");
} else {
printf("buffer : %s\n", buffer);
printf("great_success = %d\n", great_success);
great_success = 0;
}
}
}
void main()
{
int cnt = 0;
struct Status* myStatus;
pthread_t thread_id;
myStatus = (struct Status*) malloc(sizeof(struct Status));
myStatus->reserved = 0;
myStatus->threadStarted = 0;
myStatus->msgRcved = 0;
pthread_create(&thread_id,
NULL,
&thread_routine,
(void *) myStatus); // arguments to pass to the function!
printf("Main thread\n");
while (!myStatus->msgRcved) {
printf("Main thread: side thread started!\n");
if (myStatus->threadStarted) {
printf("spawned thread started!\n");
}
sleep(1);
}
pthread_exit(NULL);
}
Upvotes: 2
Views: 1571
Reputation: 16540
This code:
typedef struct __attribute__ ((__packed__)) {
int reserved :30;
int threadStarted :1;
int msgRcved :1;
} Status;
will give problems as the code is using signed values and struct definitions should not be typedef'd as the typedef'ing:
here is an example of the preferred method to define a struct (and correct the problem with the bit fields)
struct status
{
unsigned int reserved :30;
unsigned int threadStarted :1;
unsigned int msgRcved :1;
};
There should be no need for the packed attribute as all the bits will fit into a single unsigned int memory area. Reference this struct via: struct status
in variable definitions and function parameter lists.
Upvotes: -1
Reputation: 16540
the following is a way to open a named pipe,
so there is no need for any (before running application)
processing needed.
enum enumReturnStatus create_Pipe( INT32 taskSelector )
{
enum enumReturnStatus returnStatus = eRS_Success; // indicate success
char *pTask_NameString = NULL;
char Pipe_NameString[ 100 ] = {'\0'};
struct stat statInfo; // will contain info on a file
// and is used to determine if the pipe already exists
INT32 mkfifoStatus = 0;
INT32 statStatus = 0;
if( 0 >= Pipe_Parameters[ taskSelector ].Pipe_FD )
{ // then pipe not open
pTask_NameString = get_pTask_NameString( taskSelector );
if( NULL == pTask_NameString )
{ // task not configured
return( eRS_Failure );
}
/* *********************************************************************
* implied else, task configured
* ********************************************************************
*/
// create pipe name string
sprintf( Pipe_NameString, "/tmp/Pipe_2%s", pTask_NameString );
// check if pipe already exists
statStatus = stat(Pipe_NameString, &statInfo);
if( (statStatus)&&(ENOENT == errno) )
{ // then, FIFO pipe does not exist
// create the pipe
umask(0);
// maybe use mknode() instead of mkfifo()
// mknod(pPipe_name, S_IFIFO | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0 );
// 0666 allows anyone to read/write the pipe
mkfifoStatus = mkfifo( Pipe_NameString, 0666 );
if ( -1 == mkfifoStatus )
{
CommonWriteCdsLog( eLL_Critical,
get_pFormatString( eFormat_CFFL_string_string ),
__FILE__, __LINE__,
"LibFunc:mkfifo() failed to create pipe ",
strerror( errno ) );
fprintf(stderr,"mkfifo failed: %s \n",strerror( errno ));
fflush(stderr);
system( "sync; sync;" );
exit( EXIT_FAILURE );
}
} // endif ( pipe doesn't exist )
if( !mkfifoStatus && !statStatus )
{ // then pipe created or already exists
if( 0 >= Pipe_Parameters[taskSelector].Pipe_FD )
{ // then, pipe not yet open
// note: use O_RDWR so open will not hang
Pipe_Parameters[taskSelector].Pipe_FD = open( Pipe_NameString, O_CREAT|O_RDWR );
if( 0 >= Pipe_Parameters[taskSelector].Pipe_FD )
{ // then open failed
CommonWriteCdsLog( eLL_Critical,
get_pFormatString( eFormat_CFFL_string_string ),
__FILE__, __LINE__,
"LibFunc:open() failed for pipe",
strerror( errno ) );
}
else
{ // else open successful
;
} // endif( open for read successful )
} // endif( pipe not already open )
} // endif( pipe created or already exists )
} // endif( pipe not open )
return( returnStatus );
} // end create_Pipe()
Upvotes: 0
Reputation: 58534
Instead, the entire process is blocked, and the message "Main thread" only prints after I've put data into the FIFO.
Am I missing something here?
Your main thread is blocked at this line:
fd = open(NEWPIPE, O_RDONLY);
because a non-blocking, read-only open of a FIFO will block until a writer is available. Your main thread is finally unblocked, not when you write data to the FIFO, but when you simply open the FIFO for writing.
There are other problems in the code as @JohnBollinger discusses in his answer. However, the FIFO semantics are why you are not seeing the initial output you expect.
Upvotes: 1
Reputation: 180151
You are passing the result of calling thread_routine()
to pthread_create()
. The arguments must all be evaluated before the call is executed, so the thread does not get created until that function returns. Probably. Because the thread_routine()
does not return a (*)(void *)
, but pthread_create()
attempts to call the return value as if it were one, the whole program's behavior is undefined. You want to pass a pointer to the function, not the result of calling it:
pthread_create(&thread_id,
NULL,
thread_routine,
NULL);
"But what about the arguments," you ask? That leads me to the next point: function thread_routine()
does not have the correct signature for a thread start routine. A thread start routine must accept a single argument of type void *
. The last argument to pthread_create()
will be passed to the specified function as its (single) argument, and you can make that a pointer to an appropriate struct
in lieu of passing multiple separate arguments.
Finally, your putative thread start function needs to exit either by returning a pointer value (possibly NULL
) or by calling pthread_exit()
. Behavior is undefined when a value-returning function other than main()
reaches its terminal }
without executing a return
statement. (pthread_exit()
solves that because it does not return.)
Note, by the way, that your compiler ought to have spit out several warnings about this code. You should always resolve all compiler warnings, or be certain why it's safe not to do so.
Upvotes: 4