Majority of stdio functions are deemed to be thread safe ones. I cannot say
“all”
since I didn’t check them one by one. I only had a look at *printf(),
*scanf(),
and some of put() and get().
All these functions use flockfile() and funlockfile() calls in order to gain
an
exclusive access to the resource. These calls are potentially dangerous for
multithreaded processes. Example below demonstrates why.
The flockfile() and funlockfile() lock _stdio_mutex to reach a condvar. If a
thread is cancelled during the time it locks this mutex, the mutex is left
locked
forever blocking all other threads calling flockfile(). Innocent printf()
call may
block your entire process.
A simple solution is to wrap all stdio functions to pthread_setcancelstate()
(see safe_thread() function below). Note that if an “stdio” function is the
only
cancellation point in a thread, after wrapping it with
pthread_setcancelstate()
you have to add other cancellation point outside the wrapping.
I wonder why pthread_setcancelstate() calls are not used inside the
flockfile()
and funlockfile()? They are not cancellation points anyway.
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
/*
- higher number allows to get desirable result quicker
*/
#define NTHREADS 10
void *safe_thread( void *args )
{
int i, state, tid = pthread_self();
for( i = 0; ; i++ ) {
pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &state );
printf( “%d: %08X\r”, tid, i );
pthread_setcancelstate( state, &state );
delay( 1 ); // cancellation point
}
return NULL;
}
void *unsafe_thread( void *args )
{
int i, tid = pthread_self();
for( i = 0; ; i++ ) {
printf( “%d: %08X\r”, tid, i ); // cancellation point
}
return NULL;
}
int main( int argc, char **argv )
{
int i;
pthread_t threadID[ NTHREADS ];
/*
- If one of the threads is canceled while in the flockfile()
- or funlockfile() any other thread calling one these functions
- will be MUTEX blocked until the process is terminated with a signal.
*/
while( 1 ) {
for( i = 0; i < NTHREADS; i++ )
pthread_create( &threadID_, NULL, &unsafe_thread, NULL );
// pthread_create( &threadID, NULL, &safe_thread, NULL );
delay( 1000 );
for( i = 0; i < NTHREADS; i++ ) {
pthread_cancel( threadID );
pthread_join( threadID, NULL );
}
}
return 0;
}_