I'm working on a multithreaded producer-consumer program in c that implements semaphores. Each buffer struct has two general semaphores - one for empty buffers and one for full buffers. However, when the program enters the readInput function, the value of those two semaphores change to random values. The semaphore within the thread struct is fine, but the semaphores within the buffer struct within the IO union within the thread struct change for some reason. Any idea why this is happening?
#include "st.h"
#include "semaphore.h"
#include "buffer.h"
/*Definitions here*/
//Input location.
#define DEFAULT_IN stdin
//Output location.
#define DEFAULT_OUT stdout
//Line width
#define LINE_SIZE 80
#define SLEEP_TIME 333333
//Shorthand for structure components
#define INPUT thread->in
#define MUTEX thread->mutex
#define OUTPUT thread->out
/*Global variables here*/
//The input/output can be a stream or a buffer.
typedef union
{
buffer *bufferIO; //Buffer to read from/write to.
FILE *stream; //An I/O stream.
} IO;
//Each thread has an input, a semaphore, and an output.
typedef struct
{
IO in; //Either an input stream or a buffer to read.
semaphore *mutex; //Pointer to a binary semaphore.
IO out; //Either an output stream or a buffer to write.
} Thread;
/*Function prototypes here*/
void *readInput(void *s);
void *processLines(void *s);
void *processAsterisks(void *s);
void *writeOutput(void *s);
int main (int argc, char const *argv[]) //The main function
{
st_init();
/*Binary semaphore*/
semaphore mutex; //Binary semaphore for mutual exclusion.
createSem(&mutex, 1);
printf("Beginning...\n");
/*The three buffers between threads*/
buffer B1, B2, B3;
createBuf(&B1);
createBuf(&B2);
createBuf(&B3);
/*The 5 data locations*/
IO input, buffer1, buffer2, buffer3, output; //stdin, 3 buffers, stdout
/*Set the unions*/
input.stream = DEFAULT_IN; //Input stream
buffer1.bufferIO = &B1; //Contains raw input
buffer2.bufferIO = &B2; //Newlines have become spaces
buffer3.bufferIO = &B3; //** has become ^
output.stream = DEFAULT_OUT; //Output stream, 80 at a time
/*Initialize the threads*/
Thread input_thread = {input, &mutex, buffer1}; //Recieve input
Thread proc1_thread = {buffer1, &mutex, buffer2}; //Returns to spaces
Thread proc2_thread = {buffer2, &mutex, buffer3}; //** to ^
Thread output_thread = {buffer3, &mutex, output}; //Output 80 characters and a newline.
printf("%i\n", input_thread.out.bufferIO->emptyBuffers->value);
/*Create the actual threads*/
if(st_thread_create(readInput, &input_thread, 0, 0) == NULL)
{
perror("st_thread_create for input thread failure");
exit(1);
}
printf("%i\n", input_thread.out.bufferIO->emptyBuffers->value);
if(st_thread_create(processLines, &proc1_thread, 0, 0) == NULL)
{
perror("st_thread_create for line thread failure");
exit(1);
}
if(st_thread_create(processAsterisks, &proc2_thread, 0, 0) == NULL)
{
perror("st_thread_create for asterisk thread failure");
exit(1);
}
if(st_thread_create(writeOutput, &output_thread, 0, 0) == NULL)
{
perror("st_thread_create for output thread failure");
exit(1);
}
st_thread_exit(NULL);
return 0;
}
/*Function definitions here*/
void *readInput(void *s)
{
Thread *thread = s;
char c; //An individual character.
printf("Type some input.\n");
do
{
down(MUTEX);
c = getc(INPUT.stream); //Consume a character from input.
printf("%i\n", OUTPUT.bufferIO->emptyBuffers->value);
deposit(OUTPUT.bufferIO, c); //Produce a character.
up(MUTEX);
st_usleep(SLEEP_TIME); //Wait.
}
while(c != EOF); //Do-while in order to pass on the EOF as an exit flag.
//EOF reached - exit thread.
//assert(c == EOF);
printf("\nDone reading.");
st_thread_exit(NULL);
}...
Upon looking at it with gdb, I found this:
Hardware watchpoint 10: (*(*input_thread.out.bufferIO).emptyBuffers).value
(gdb) c
Continuing.
Hardware watchpoint 10: (*(*input_thread.out.bufferIO).emptyBuffers).value
Old value = 80
New value = 4216725
0x000000395221467c in _dl_runtime_resolve () from /lib64/ld-linux-x86-64.so.2
Not sure what this _dl_runtime_resolve is.
semaphore and buffers are created as stack variables in main thread so when the main thread exits the address pointing to semaphore and buffer are invalid and the behavior is unpredictable. Make the proper design and give proper scope to the variables, for time being make all the variables as global. Make the following things in the above code as global
semaphore mutex; //Binary semaphore for mutual exclusion.
AND
IO input, buffer1, buffer2, buffer3, output; //stdin, 3 buffers, stdout
recompile and execute
Related
EDIT:
Thanks for the suggestions given so far. I changed the program and now the parent handles some of the signals, but it looks like it doesn't handle all of them. New code and results are posted below.
EDIT2:
I changed the random number generation as proposed. Now the parent catches only two signals but it always catches the right bits (two last bits).
"Unfortunately, I am not experienced in C POSIX and I have to write a program that will take one argument (filename containing a binary number) and parse this file. Each bit denoted in the file means that one child should be created (each bit is dedicated to one child). The value of the bit (0 or 1) decides which signals should be sent to the parent (0 - SIGUSR1, 1 - SIGUSR2).
The child process should choose a random interval (10-200ms) and send appropriate signal to the parent.
The parent should receive the signals and print the last 5 bits received every time a new signal arrives.
The final step is the matching process - the parent checks the received signals (bits assigned to SIGUSR1 or SIGUSR2) and if there's a match it prints SUCCESS. If there's no match (whenever a wrong bit is sent - compared to the file) the parent starts matching from the beginning."
The updated version:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <signal.h>
volatile sig_atomic_t last_signal = 0;
char * str;
char * received;
int count = 0;
#define ERR(source) (fprintf(stderr,"%s:%d\n",__FILE__,__LINE__),\
perror(source),kill(0,SIGKILL),\
exit(EXIT_FAILURE))
void sethandler( void (*f)(int), int sigNo)
{
struct sigaction act;
memset(&act, 0, sizeof(struct sigaction));
act.sa_handler = f;
if (-1==sigaction(sigNo, &act, NULL)) ERR("sigaction");
}
char *readFile(char *fileName)
{
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL) return NULL; //could not open file
fseek(file, 0, SEEK_END);
long f_size = ftell(file);
fseek(file, 0, SEEK_SET);
code = malloc(f_size);
received = malloc(f_size);
while ((c = fgetc(file)) != EOF) {
code[n++] = (char)c;
}
code[n] = '\0';
return code;
}
void append(char* s, char c)
{
int len = strlen(s);
s[len] = c;
s[len+1] = '\0';
}
static void sig_handle(int signum)
{
last_signal = signum;
}
void child_w(int number_of)
{
if(str[number_of] == '0')
{
if (kill(getppid(), SIGUSR1)==0) printf("[SIGUSR1] sent \n");
else
{
printf("ERROR kill. \n");
exit(EXIT_FAILURE);
}
}
if(str[number_of] == '1')
{
if (kill(getppid(), SIGUSR2) == 0) printf("[SIGUSR2] sent \n");
else
{
printf("ERROR kill. \n");
exit(EXIT_FAILURE);
}
}
}
void create_children(int n)
{
pid_t s;
int j = n;
int time = rand() % 191 + 10; // range 10 - 200
struct timespec time_wait = { .tv_sec = 0, .tv_nsec = time * 1000000L };
while(j-->0)
{
nanosleep(&time_wait, NULL);
if((s=fork())<0) ERR("Fork ERROR");
if(!s) {
printf("Child %d started ", j);
printf("with bit: %c \n", str[j]);
child_w(j);
exit(EXIT_SUCCESS);
}
}
}
void parent_w(sigset_t oldmask)
{
int count = 0;
int match = 0;
while(1)
{
last_signal = 0;
while(last_signal != SIGUSR1 && last_signal != SIGUSR2)
{
sigsuspend(&oldmask);
}
printf("\n");
if(last_signal == SIGUSR1)
{
received[count] = '0';
for(int i=0; i<sizeof(received); ++i)
{
printf("%c ", received[i]);
}
count++;
}
else if(last_signal == SIGUSR2)
{
received[count] = '1';
for(int i=0; i<sizeof(received); ++i)
{
printf("%c ", received[i]);
}
count++;
}
printf("\n");
}
}
int main(int argc, char ** argv)
{
char filename[250];
if(argc!=2)
{
printf("Provide one parameter - filename. \n");
return EXIT_FAILURE;
}
strcpy(filename, argv[1]);
str = readFile(filename);
printf("FILE: ");
for(int i=0; i<sizeof(str); ++i)
{
printf("%c ", str[i]);
}
printf("\n");
for(int i=0; i<sizeof(received); ++i)
{
received[i] = '-';
}
sethandler(sig_handle, SIGUSR1);
sethandler(sig_handle, SIGUSR2);
sigset_t mask, oldmask;
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
sigaddset(&mask, SIGUSR2);
sigprocmask(SIG_BLOCK, &mask, &oldmask);
create_children(sizeof(str));
parent_w(oldmask);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
free(str);
free(received);
return EXIT_SUCCESS;
}
Now the output always looks like this:
FILE: 1 0 0 1 1 0 1 0
Child 7 started with bit: 0
[SIGUSR1] sent
Child 6 started with bit: 1
[SIGUSR2] sent
Child 5 started with bit: 0
[SIGUSR1] sent
Child 4 started with bit: 1
[SIGUSR2] sent
Child 3 started with bit: 1
[SIGUSR2] sent
Child 2 started with bit: 0
[SIGUSR1] sent
Child 1 started with bit: 0
[SIGUSR1] sent
0 - - - - - - -
Child 0 started with bit: 1
[SIGUSR2] sent
0 1 - - - - - -
Any further suggestions will be appreciated :).
In addtition to the problems mentioned by others, your readFile() function invokes undefined behavior by overrunning the buffer you allocate for the file contents:
char *readFile(char *fileName)
{
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL) return NULL; //could not open file
fseek(file, 0, SEEK_END);
long f_size = ftell(file);
fseek(file, 0, SEEK_SET);
code = malloc(f_size);
received = malloc(f_size);
while ((c = fgetc(file)) != EOF) {
code[n++] = (char)c;
}
code[n] = '\0'; // <- this is f_size + 1 bytes into the code array
return code;
}
When you terminate the data with code[n] = '\0'; you write past the end of the buffer code points to, thus invoking undefined behavior.
And, off-topic...
Strictly speaking you can't use fseek()/ftell() to get the size of a file. In your case, you're opening the file in text mode with FILE *file = fopen(fileName, "r");, but in text mode ftell() does not return a byte offset. Per 7.21.9.4 The ftell function, paragraph 2 of the C11 standard:
The ftell function obtains the current value of the file position indicator for the stream pointed to by stream. For a binary stream, the value is the number of characters from the beginning of the file. For a text stream, its file position indicator contains unspecified information, usable by the fseek function for returning the file position indicator for the stream to its position at the time of the ftell call; the difference between two such return values is not necessarily a meaningful measure of the number of characters written or read.
On POSIX systems, you won't have a problem as POSIX defines ftell() to always return an accurate byte offset. But on Windows you will likely read fewer bytes than the file size would otherwise indicate as the \r\n character sequence actually in the file contents is read as a single \n character.
But on some systems you will truly get "unspecified information" and your code will fail completely.
And seeking to the end of a binary stream isn't portable either. In fact, it's explicitly undefined behavior:
Setting the file position indicator to end-of-file, as with fseek(file, 0, SEEK_END), has undefined behavior for a binary stream...
Again, not a problem on POSIX or Windows systems.
One real problem with fseek()/ftell(), though, is that the long value returned from ftell() doesn't have enough range on many systems to represent file sizes larger than 2 GB. long is 32 bits on 32-bit Linux systems, and its also only 32 bits on all Windows systems, both 32- and 64-bit.
As #KamilCuk observed, it is not necessary or appropriate to re-register signal handlers when they fire. That was standard at one time because of uncertainty about the implementation of the signal() function (which remains to this day): some implementations register handlers such that after they fire once, the signal disposition is reset. With sigaction(), however, once can specify whether they want that "one-shot" behavior or whether they instead want the signal handler to remain registered when it fires, with the latter being the default with that function.
sigaction() allows control of some other details on which signal() implementations vary. In practice, signal() has very few appropriate uses, and none that sigaction() cannot also cover. If you're programming for POSIX then its best to forget that signal() exists.
With all that said, however, I don't think your usage of signal() is the key problem here.
Another issue is that signal handlers are rather restricted in what they may safely do:
they may access file-scope variables of type sig_atomic_t
they may call async-signal-safe standard functions
they may declare and access local variables
they may call any of the program's other functions that comply with these restrictions
HOWEVER, signal handlers are typically called with their own, separate stacks, and these are often very small, so in practice, they cannot safely declare very much in the way of local variables, nor start a very deep call tree. Exactly what the limits are is unspecified, so signal handlers should generally do as little as possible.
In particular, neither printf() nor any of the other stdio functions are async-signal safe. Signal handlers produce undefined behavior if they call any of them. They may call write() if you wish, but there is probably a better alternative here. For example, the parent could pause() or sigsuspend() to await a signal, and then print whatever it needs to do outside the handler. The handler need only set a variable to indicate which signal was received. This will avoid the parent busy-waiting as it presently does, though you still have an issue with potential collisions.
This is more likely to be part of your problem, but I suspect that it's not the key issue, either.
I think the real problem is probably that signals are being lost. Ordinary signals do not queue, so if a signal is received while that signal is already pending for the process then it has no additional effect. The problem is structured to avoid that by asking for each child to delay a random amount of time before firing its signal, but
That's not truly safe, just less likely to manifest a collision, and
Your implementation does not actually delay.
Consider:
int time = rand()%100 + 10;
struct timespec time_wait = {time/1000, 0};
nanosleep(&time_wait, NULL);
Variable time will be assigned a value between 10 and 109, so time / 1000 -- an integer division -- will always evaluate to 0.
Something like this would be more appropriate:
int time = rand() % 191 + 10; // range 10 - 200
struct timespec time_wait = { .tv_sec = 0, .tv_nsec = time * 1000000L };
nanosleep(&time_wait, NULL);
Additionally, instead of seeding a separate (P)RNG in each child, I would seed one, once, in the parent, and generate the delays there, before each fork. Drawing random numbers from the same RNG produces a more uniform distribution.
First of all, good code.
Second:
man pages are your friends. man signal
signal() registers handler for the signal. So after signal(SIGUSR1, some_function) the function some_function will be executed after the signal is received.
Removoe signal() calls from signal handlers (why would you re-register the same handler from inside the handler? It already is the handler for this signal.)
Remove signal() call from the loop in parent. Just register functions once, that's all.
Your sethandler function is the same as signal.
After some fixing:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <signal.h>
char * str; // the array consisting of bits from the file
char * received; //array of the parent receiving the signals
int count = 0;
//Error macro
#define ERR(source) (fprintf(stderr,"%s:%d\n",__FILE__,__LINE__),\
perror(source),exit(EXIT_FAILURE))
//Reading file char by char and returning allocated char array
char *readFile(char *fileName)
{
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL) return NULL; //could not open file
fseek(file, 0, SEEK_END);
long f_size = ftell(file);
fseek(file, 0, SEEK_SET);
code = malloc(f_size);
received = malloc(f_size);
while ((c = fgetc(file)) != EOF) {
code[n++] = (char)c;
}
code[n] = '\0';
return code;
}
// Append the character to the received array
void append(char* s, char c)
{
int len = strlen(s);
s[len] = c;
s[len+1] = '\0';
}
// SIGUSR1 handler. I tried to implement simple counter to check if the parent is receiving the signals, then proceed to printing last 5 bits received. Unfortunately this part seems to not work at all.
static void sig_handle1(int signum) {
count++;
printf("%s %d \n", __func__, count);
}
// Handler for SIGUSR2 - same as handler for SIGUSR1
static void sig_handle2(int signum) {
count++;
printf("%s %d \n", __func__, count);
}
// Child function - set the random interval, wait and then send the appropriate signal to the parent
void child_w(int number_of)
{
srand(time(NULL)*getpid());
int time = rand()%100 + 10;
struct timespec time_wait = {time/1000, 0};
nanosleep(&time_wait, NULL);
if(str[number_of] == '0')
{
if (kill(getppid(), SIGUSR1)==0) printf("[SIGUSR1] sent \n");
else
{
printf("ERROR kill. \n");
exit(EXIT_FAILURE);
}
}
if(str[number_of] == '1')
{
if (kill(getppid(), SIGUSR2) == 0) printf("[SIGUSR2] sent \n");
else
{
printf("ERROR kill. \n");
exit(EXIT_FAILURE);
}
}
}
// Function which will create children (number of children = number of bits in the file)
void create_children(int n)
{
pid_t s;
int j = n;
while(j-->0)
{
if((s=fork())<0) ERR("Fork ERROR");
if(!s) {
printf("Child %d started ", j);
printf("with bit: %c \n", str[j]);
child_w(j);
//if(j==1) kill(getppid(), SIGUSR2);
exit(EXIT_SUCCESS);
}
}
}
// Parent function to check the received signals
void parent_w()
{
signal(SIGUSR1, sig_handle1);
signal(SIGUSR2, sig_handle2);
while(1)
{
pause();
}
}
int main(int argc, char ** argv)
{
char filename[250];
if(argc!=2)
{
printf("Provide one parameter - filename. \n");
return EXIT_FAILURE;
}
strcpy(filename, argv[1]);
str = readFile(filename);
printf("FILE: ");
for(int i=0; i<sizeof(str); ++i)
{
printf("%c ", str[i]);
}
printf("\n");
create_children(sizeof(str)-1);
parent_w();
free(str);
free(received);
return EXIT_SUCCESS;
}
Example exeuction:
FILE: 1 0 0 1 1 0 1
Child 0 started with bit: 1
Child 1 started with bit: 0
Child 2 started with bit: 0
Child 3 started with bit: 1
[SIGUSR2] sent
sig_handle2 1
[SIGUSR1] sent
sig_handle1 2
[SIGUSR1] sent
sig_handle1 3
[SIGUSR2] sent
sig_handle2 4
Child 4 started with bit: 1
Child 5 started with bit: 0
sig_handle2 5
[SIGUSR2] sent
Child 6 started with bit: 1
sig_handle1 6
[SIGUSR1] sent
sig_handle2 7
[SIGUSR2] sent
^C
I'm new to pthread and multithreading, i have written a code like that.
#include <pthread.h>
#include <unistd.h>
void *nfc_read(void *arg)
{
int fd = -1;
int ret;
uint8_t read_data[24];
while(1){
ret = read_block(fd, 8, read_data);
if(!ret){
return (void)read_data;
}
}
}
int main(int argc, char *argv[])
{
pthread_t my_thread;
void *returnValue;
pthread_create(&my_thread, NULL, nfc_read, NULL);
pthread_join(my_thread, &returnValue);
printf("NFC card value is : %s \n", (char)returnValue);
printf("other process");
return 0;
}
Until the return value from nfc_read function, as I will have other code I need to run and I don't want to block in main. How can i do that?
This is a sample where a read thread runs concurrently to the "main" thread which is doing other work (in this case, printing dots and sleeping).
To keep things simple, a simulated the reading from an input device with copying a constant string character by character. I guess, this is reasonable as the synchronization of threads is focused.
For the synchronization of threads, I used atomic_bool with atomic_store() and atomic_load() which are provided by the Atomic Library (since C11).
My sample application test-concurrent-read.c:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdatomic.h>
#include <unistd.h>
/* sampe input */
const char sampleInput[]
= "This is sample input which is consumed as if it was read from"
" a (very slow) external device.";
atomic_bool readDone = ATOMIC_VAR_INIT(0);
void* threadRead(void *pArg)
{
char **pPBuffer = (char**)pArg;
size_t len = 0, size = 0;
int c; const char *pRead;
for (pRead = sampleInput; (c = *pRead++) > 0; sleep(1)) {
if (len + 1 >= size) {
if (!(*pPBuffer = realloc(*pPBuffer, (size + 64) * sizeof(char)))) {
fprintf(stderr, "ERROR! Allocation failed!\n");
break;
}
size += 64;
}
(*pPBuffer)[len++] = c; (*pPBuffer)[len] = '\0';
}
atomic_store(&readDone, 1);
return NULL;
}
int main()
{
/* start thread to read concurrently */
printf("Starting thread...\n");
pthread_t idThreadRead; /* thread ID for read thread */
char *pBuffer = NULL; /* pointer to return buffer from thread */
if (pthread_create(&idThreadRead, NULL, &threadRead, &pBuffer)) {
fprintf(stderr, "ERROR: Failed to start read thread!\n");
return -1;
}
/* start main loop */
printf("Starting main loop...\n");
do {
putchar('.'); fflush(stdout);
sleep(1);
} while (!atomic_load(&readDone));
putchar('\n');
void *ret;
pthread_join(idThreadRead, &ret);
/* after sync */
printf("\nReceived: '%s'\n", pBuffer ? pBuffer : "<NULL>");
free(pBuffer);
/* done */
return 0;
}
Compiled and tested with gcc in cygwin on Windows 10 (64 bit):
$ gcc -std=c11 -pthread -o test-concurrent-read test-concurrent-read.c
$ ./test-concurrent-read
Starting thread...
Starting main loop...
.............................................................................................
Received: 'This is sample input which is consumed as if it was read from a (very slow) external device.'
$
I guess, it is worth to mention why there is no mutex guarding for pBuffer which is used in main() as well as in threadRead().
pBuffer is initialized in main() before pthread_create() is called.
While thread_read() is running, pBuffer is used by it exclusively (via its passed address in pPBuffer).
It is accessed in main() again but not before pthread_join() which grants that threadRead() has ended.
I tried to find a reference by google to confirm that this procedure is well-defined and reasonable. The best, I could find was SO: pthread_create(3) and memory synchronization guarantee in SMP architectures which cites The Open Group Base Specifications Issue 7 - 4.12 Memory Synchronization.
So I'm trying to code a multi-threading server. I've spent an enormous time on the internet figuring out the correct way to do this and the answer as always seems to be it depends. Whenever I execute my code, the client successfully connects, and executes but when the thread terminates and returns to the while loop the whole program segfaults.
I probably could use a good spanking on a few other things as well such as my usage of global variables. The entirety of code is below, sorry for the inconsistent space/tabbing.
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <signal.h>
#include <math.h>
#include <pthread.h>
#include <sys/stat.h>
#include <fcntl.h>
/* ---------------------------------------------------------------------
This is a basic whiteboard server. You can query it, append to it and
clear in it. It understands both encrypted and unencrypted data.
--------------------------------------------------------------------- */
struct whiteboard {
int line;
char type;
int bytes;
char string[1024];
} *Server;
int serverSize, threadcount, id[5];
bool debug = true;
struct whiteboard *Server;
pthread_mutex_t mutex;
pthread_t thread[5];
/* -------------------------------------------
function: sigint_handler
Opens a file "whiteboard.all" in writemode
and writes all white board information in
command mode.
------------------------------------------- */
void sigint_handler(int sig)
{
if (debug) printf("\nInduced SIGINT.\n");
FILE *fp;
fp=fopen("whiteboard.all","w");
int x=0;
for (x;x<serverSize;x++) // Loop Responsible for iterating all the whiteboard entries.
{
if (debug) printf("#%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
fprintf(fp,"#%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
}
if (debug) printf("All values stored.\n");
free(Server); // Free dynamically allocated memory
exit(1);
}
/* -------------------------------------------
function: processMessage
Parses '!' messages into their parts -
returns struct in response.
------------------------------------------- */
struct whiteboard processMessage(char * message)
{
int lineNumber, numBytes;
char stringType, entry[1028];
if (debug) printf("Update Statement!\n");
// Read line sent by Socket
sscanf(message,"%*c%d%c%d\n%[^\n]s",&lineNumber,&stringType,&numBytes,entry);
if (debug) printf("Processed: Line: %d, Text: %s\n",lineNumber,entry);
// Parse information into local Struct
struct whiteboard Server;
Server.line = lineNumber;
Server.type = stringType;
Server.bytes = numBytes;
strcpy(Server.string,entry);
// If there is no bytes, give nothing
if (numBytes == 0)
{
strcpy(Server.string,"");
}
return Server;
}
/* -------------------------------------------
function: handleEverything
Determines type of message recieved and
process and parses accordingly.
------------------------------------------- */
char * handleEverything(char* message, struct whiteboard *Server, char* newMessage)
{
bool updateFlag = false, queryFlag = false;
// If message is an Entry
if (message[0] == '#')
{
if (debug) printf("Triggered Entry!\n");
// Create Temporary Struct
struct whiteboard messageReturn;
messageReturn = processMessage(message);
// Store Temporary Struct in Correct Heap Struct
Server[messageReturn.line] = messageReturn;
sprintf(newMessage,"!%d%c%d\n%s\n",messageReturn.line, messageReturn.type, messageReturn.bytes, messageReturn.string);
return newMessage;
}
// If message is a query
if (message[0] == '?')
{
if (debug) printf("Triggered Query!\n");
int x;
queryFlag = true;
sscanf(message,"%*c%d",&x); // Parse Query
if (x > serverSize) // Check if Query out of Range
{
strcpy(newMessage,"ERROR: Query out of Range.\n");
return newMessage;
}
sprintf(newMessage,"!%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
if (debug) printf("newMessage as of handleEverything:%s\n",newMessage);
return newMessage;
}
}
/* -------------------------------------------
function: readFile
If argument -f given, read file
process and parse into heap memory.
------------------------------------------- */
void readFile(char * filename)
{
FILE *fp;
fp=fopen(filename,"r");
int line, bytes, count = 0, totalSize = 0;
char type, check, string[1028], individualLine[1028];
// Loop to determine size of file. **I know this is sloppy.
while (fgets(individualLine, sizeof(individualLine), fp))
{
totalSize++;
}
// Each line shoud have totalSize - 2 (to account for 0)
// (answer) / 2 to account for string line and instruction.
totalSize = (totalSize - 2) / 2;
serverSize = totalSize+1;
if (debug) printf("Total Size is: %d\n",serverSize);
// Open and Allocate Memory
fp=fopen(filename,"r");
if (debug) printf("File Mode Calloc Initialize\n");
Server = calloc(serverSize+2, sizeof(*Server));
// Write to Heap Loop
while (fgets(individualLine, sizeof(individualLine), fp)) {
if (individualLine[0] == '#') // Case of Header Line
{
sscanf(individualLine,"%c%d%c%d",&check,&line,&type,&bytes);
if (debug) printf("Count: %d, Check:%c, Line:%d, Type: %c, Bytes:%d \n",count,check,line,type,bytes);
Server[count].line = line;
Server[count].type = type;
Server[count].bytes = bytes;
count++;
}
else
{
// For case of no data
if (individualLine[0] == '\n')
{
strcpy(string,"");
}
// Then scan data line
sscanf(individualLine,"%[^\n]s",string);
if (debug) printf("String: %s\n",string);
strcpy(Server[count-1].string,string);
}
}
return;
}
void *threadFunction(int snew)
{
char tempmessage[1024], message[2048];
// Compile and Send Server Message
strcpy(tempmessage, "CMPUT379 Whiteboard Server v0\n");
send(snew, tempmessage, sizeof(tempmessage), 0);
// Recieve Message
char n = recv(snew, message, sizeof(message), 0);
pthread_mutex_lock(&mutex);
if (debug) printf("Attempt to Malloc for newMessage\n");
char * newMessage = malloc(1024 * sizeof(char));
if (debug) printf("goto: handleEverything\n");
newMessage = handleEverything(message, Server, newMessage);
if (debug) printf("returnMessage:%s\n",newMessage);
strcpy(message,newMessage);
free(newMessage);
pthread_mutex_unlock(&mutex);
if (debug) printf("message = %s\n", message);
send(snew, message, sizeof(message), 0);
printf("End of threadFunction\n");
return;
}
/* -------------------------------------------
function: main
Function Body of Server
------------------------------------------- */
int main(int argc, char * argv[])
{
int sock, fromlength, outnum, i, socketNumber, snew;
bool cleanMode;
// Initialize Signal Handling
struct sigaction act;
act.sa_handler = sigint_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
// For correct number of arguments.
if (argc == 4)
{
// If "-n" parameter (cleanMode)
if (strcmp(argv[2], "-n") == 0)
{
// Get size + 1
cleanMode = true;
sscanf(argv[3],"%d",&serverSize);
serverSize += 1;
if (debug) printf("== Clean Mode Properly Initiated == \n");
if (debug) printf("serverSize: %d\n",serverSize);
if (debug) printf("Clean Mode Calloc\n");
Server = calloc(serverSize, sizeof(*Server));
int i = 0;
for (i; i < serverSize; i++) // Initialize allocated Memory
{
Server[i].line = i;
Server[i].type = 'p';
Server[i].bytes = 0;
strcpy(Server[i].string,"");
}
}
// If "-f" parameter (filemode)
else if (strcmp(argv[2], "-f") == 0)
{
// Read File
cleanMode = false;
readFile(argv[3]);
if (debug) printf("== Statefile Mode Properly Initiated == \n");
if (debug) printf("serverSize: %d\n",serverSize);
}
// Otherwise incorrect parameter.
else
{
printf("Incorrect Argument. \n");
printf("Usage: wbs279 pornumber {-n number | -f statefile}\n");
exit(1);
}
sscanf(argv[1],"%d",&socketNumber);
}
// Send Error for Incorrect Number of Arguments
if (argc != 4)
{
printf("Error: Incorrect Number of Input Arguments.\n");
printf("Usage: wbs279 portnumber {-n number | -f statefile}\n");
exit(1);
}
// == Do socket stuff ==
char tempmessage[1024], message[2048];
struct sockaddr_in master, from;
if (debug) printf("Assrt Socket\n");
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
perror ("Server: cannot open master socket");
exit (1);
}
master.sin_family = AF_INET;
master.sin_addr.s_addr = INADDR_ANY;
master.sin_port = htons (socketNumber);
if (bind (sock, (struct sockaddr*) &master, sizeof (master)))
{
perror ("Server: cannot bind master socket");
exit (1);
}
// == Done socket stuff ==
listen (sock, 5);
int threadNumber = 0;
while(1)
{
printf("But what about now.\n");
if (debug) printf("-- Wait for Input --\n");
printf("Enie, ");
fromlength = sizeof (from);
printf("Meanie, ");
snew = accept (sock, (struct sockaddr*) & from, & fromlength);
printf("Miney, ");
if (snew < 0)
{
perror ("Server: accept failed");
exit (1);
}
printf("Moe\n");
pthread_create(&thread[threadNumber],NULL,threadFunction(snew), &id[threadNumber]);
//printf("Can I join?!\n");
//pthread_join(thread[0],NULL);
//printf("Joined?!\n");
threadNumber++;
close (snew);
}
}
I'm also curious as to how exactly to let multiple clients use the server at once. Is how I've allocated the whiteboard structure data appropriate for this process?
I'm very sorry if these don't make any sense.
You seem to somehow expect this:
pthread_create(&thread[threadNumber],NULL,threadFunction(snew), &id[threadNumber]);
/* ... */
close (snew);
To make sense, while it clearly doesn't.
Instead of starting a thread that runs threadFunction, passing it snew, you call the thread function and pass the return value to pthread_create(), which will interpret it as a function pointer. This will break, especially considering that the thread function incorrectly ends with:
return;
This shouldn't compile, since it's declared to return void *.
Also assuming you managed to start the thread, passing it snew to use as its socket: then you immediately close that socket, causing any reference to it from the thread to be invalid!
Please note that pthread_create() does not block and wait for the thread to exit, that would be kind of ... pointless. It starts off the new thread to run in parallel with the main thread, so of course you can't yank the carpet away from under it.
This signal handler is completely unsafe:
void sigint_handler(int sig)
{
if (debug) printf("\nInduced SIGINT.\n");
FILE *fp;
fp=fopen("whiteboard.all","w");
int x=0;
for (x;x<serverSize;x++) // Loop Responsible for iterating all the whiteboard entries.
{
if (debug) printf("#%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
fprintf(fp,"#%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
}
if (debug) printf("All values stored.\n");
free(Server); // Free dynamically allocated memory
exit(1);
}
Per 2.4.3 Signal Actions of the POSIX standard (emphasis added):
The following table defines a set of functions that shall be
async-signal-safe. Therefore, applications can call them, without
restriction, from signal-catching functions. ...
[list of async-signal-safe functions]
Any function not in the above table may be unsafe with respect to signals. Implementations may make other interfaces
async-signal-safe. In the presence of signals, all functions defined
by this volume of POSIX.1-2008 shall behave as defined when called
from or interrupted by a signal-catching function, with the exception
that when a signal interrupts an unsafe function or equivalent
(such as the processing equivalent to exit() performed after a return
from the initial call to main()) and the signal-catching function
calls an unsafe function, the behavior is undefined. Additional
exceptions are specified in the descriptions of individual functions
such as longjmp().
Your signal handler invokes undefined behavior.
Generalities and functioning of my program
NB : you will be able to test my program (only one file, containing the main function). I give you the entire code at the end of this question.
I wrote a program which can be used to illustrate the producer-consumer algorithm, with UNIX-Processes. The producer creates some value, for example 5, writes it into a RAM shared_buffer and then writes the latter into a file test.txt. The consumer assigns to this shared_buffer the content of the file test.txt and takes some value from the RAM buffer, shared_buffer.
I use functions to convert my shared_buffer into the file, and reciprocally : arrayToFile and fileToArray. Both are presented at the end of this question.
The shared_buffer has a size of 1 + 10 cases : the first one contains the number of full cases (ie. : with a 5 writen) and the 10 others can be filled either with 5 or nothing.
The first case is useful for the producer, to know where to write next value (ie. : in which case).
The file of course has also 1 + 10 cases. The file is needed because I use processes and not threads (not-shared memory, thus).
shared_buffer's initialisation is contained in the main function. shared_buffer's accesses (in reading and in writing) are contained in consumer's function and in producer's function, respectively. These codes are presented at the end of this question.
Access to shared_buffer and overall to the file are of course under mutual exclusion and three semaphores are used. The mutexe impedes producer and consumer to access it at the same time, and the two other semaphores are used to guarantee that the producer won't try to put a new element if there isn't enough place, and that the consumer won't try to take an element if there isn't any element. Well, it's just the producer-consumer algorithm.
Finally, producer's process runs until the end of time, and so does the consumer's process.
The declaration and initialisation of these three semaphores are presented at the end of this question.
My problem
There is only one problem : when both producer's process and consumer's process are running until the end of times while(TRUE), arrayToFile and fileToArray tell me that the file's opening failed. If I erase one or both while(TRUE), this error disapears (but thus, my program doesn't make its job).
So this problem appears only when both while(TRUE) are writen.
I think it's because I don't make good use of the mutexe. But I couldn't give you more explanations.
Source
Code is highly commented.
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/sem.h>
#include <stdlib.h>
#define TRUE 1
#define ERR_NBR_ARGS "ERROR. Argument needed. Use as follows.\n"
int fileToArray(char *, int *, int);
int arrayToFile(char *, int *, int);
void check_if_command_arguments_are_correct(int);
void mutual_exclusion_produce(int, char*, int*, int);
void mutual_exclusion_consume(int, char*, int*, int);
int main(int argc, char* argv[]) {
check_if_command_arguments_are_correct(argc);
// FILE'S PATH
char* file_path = argv[1];
// DECLARATION AND INITIALISATION OF THE SHARED RESOURCE
int shared_buffer_number_of_elements = 10;
int shared_buffer[shared_buffer_number_of_elements + 1];
shared_buffer[0] = 0;
arrayToFile(file_path, shared_buffer, shared_buffer_number_of_elements);
// FILE'S KEY (used to make processes able to use the same semaphores)
key_t key = ftok(argv[0], '0');
if(key == - 1) {
perror("ftok");
exit(EXIT_FAILURE);
}
// DECLARATION AND INITIALISATION OF THE THREE SEMAPHORES
int semid = semget(key, 3, IPC_CREAT|IPC_EXCL|0666); // Declaration of the semaphores
if(semid == -1) {
perror("semget");
exit(EXIT_FAILURE);
}
int array_semaphores_values[3];
array_semaphores_values[0] = 1;
array_semaphores_values[1] = shared_buffer_number_of_elements;
array_semaphores_values[2] = 0;
int sem_controller = semctl(semid, 3, SETALL, array_semaphores_values); // Initialisation of the semaphores - 2th parameter is the array's size
if(sem_controller == -1) {
perror("semctl");
exit(EXIT_FAILURE);
}
// THE TWO FORKS - CREATION OF BOTH PRODUCER AND CONSUMER
pid_t producer = fork();
if(producer == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if(producer == 0) { // The producer process
mutual_exclusion_produce(semid, file_path, shared_buffer, shared_buffer_number_of_elements);
} else { // The main process
pid_t consumer = fork();
if(consumer == - 1) {
perror("fork");
exit(EXIT_FAILURE);
} else if(consumer == 0) { // The consumer process
mutual_exclusion_consume(semid, file_path, shared_buffer, shared_buffer_number_of_elements);
}
}
semctl(semid, 0, IPC_RMID, 0); // The semaphores are freed
}
void mutual_exclusion_produce(int semid, char* file_path, int* buffer, int size) {
/* The producer does the following :
* 1. It decrements the free cases semaphore ;
* 2. It decrements the mutex ;
* 3. It writes the buffer
* 4. It increments the mutex ;
* 5. It increments the full cases semaphore ;
* */
while(TRUE) {
// DECREMENTS FREE CASES SEMAPHORE AND DECREMENTS MUTEX
struct sembuf operation_decrement_free_cases;
operation_decrement_free_cases.sem_num = 2;
operation_decrement_free_cases.sem_op = -1;
operation_decrement_free_cases.sem_flg = 0;
struct sembuf operation_decrement_mutex;
operation_decrement_mutex.sem_num = 0;
operation_decrement_mutex.sem_op = -1;
operation_decrement_mutex.sem_flg = 0;
semop(semid, &operation_decrement_free_cases, 0);
semop(semid, &operation_decrement_mutex, 0);
// WRITES THE BUFFER INTO THE FILE
buffer[++buffer[0]] = 5;
arrayToFile(file_path, buffer, size);
// INCREMENTS THE MUTEX AND INCREMENTS THE FULL CASES SEMAPHORE
struct sembuf operation_increment_full_cases;
operation_decrement_free_cases.sem_num = 1;
operation_decrement_free_cases.sem_op = +1;
operation_decrement_free_cases.sem_flg = 0;
struct sembuf operation_increment_mutex;
operation_decrement_mutex.sem_num = 0;
operation_decrement_mutex.sem_op = +1;
operation_decrement_mutex.sem_flg = 0;
semop(semid, &operation_increment_mutex, 0);
semop(semid, &operation_increment_full_cases, 0);
}
}
void mutual_exclusion_consume(int semid, char* file_path, int* buffer, int size) {
/*
* The consumer does the following :
* 1. It decrements the full cases semaphore ;
* 2. It decrements the mutex ;
* 3. It reads the buffer ;
* 4. It increments the mutex ;
* 5. It increments the free cases semaphore ;
* */
while(TRUE) {
// DECREMENTS FULL CASES SEMAPHORE AND DECREMENTS MUTEX
struct sembuf operation_decrement_full_cases;
operation_decrement_full_cases.sem_num = 1;
operation_decrement_full_cases.sem_op = -1;
operation_decrement_full_cases.sem_flg = 0;
struct sembuf operation_decrement_mutex;
operation_decrement_mutex.sem_num = 0;
operation_decrement_mutex.sem_op = -1;
operation_decrement_mutex.sem_flg = 0;
semop(semid, &operation_decrement_full_cases, 0);
semop(semid, &operation_decrement_mutex, 0);
// READS THE FILE AND PUT THE CONTENTS INTO THE BUFFER
fileToArray(file_path, buffer, size);
// INCREMENTS THE MUTEX AND INCREMENTS THE FREE CASES SEMAPHORE
struct sembuf operation_increment_free_cases;
operation_decrement_full_cases.sem_num = 2;
operation_decrement_full_cases.sem_op = +1;
operation_decrement_full_cases.sem_flg = 0;
struct sembuf operation_increment_mutex;
operation_decrement_mutex.sem_num = 0;
operation_decrement_mutex.sem_op = +1;
operation_decrement_mutex.sem_flg = 0;
semop(semid, &operation_increment_mutex, 0);
semop(semid, &operation_increment_free_cases, 0);
}
}
void check_if_command_arguments_are_correct(int argc) {
if(argc != 2) {
fprintf(stderr, ERR_NBR_ARGS);
fprintf(stderr, "program_command <file_buffer>\n");
exit(EXIT_FAILURE);
}
}
int fileToArray(char *pathname, int *tab, int size) {
int cible;
if ( (cible = open(pathname,O_RDONLY)) < 0){
fprintf(stderr,"fileToArray - impossible to open the file\n");
return -1;
}
if (read(cible,tab,(size+1) * sizeof(int)) !=(size+1) * sizeof(int)) {
fprintf(stderr,"fileToArray - impossible to read the file\n");
return -1;
}
close(cible);
return 0;
}
int arrayToFile(char *pathname, int *tab, int size) {
int cible;
if ( (cible = open(pathname,O_WRONLY|O_CREAT|O_TRUNC,0666)) < 0){
fprintf(stderr,"arrayToFile - impossible to open the file\n");
return -1;
}
if (write(cible,tab,(size+1) * sizeof(int)) !=(size+1) * sizeof(int)) {
fprintf(stderr,"arrayToFile - impossible to write the file\n");
return -1;
}
close(cible);
return 0;
}
I am attempting to implement producer/consumer communication through a bounded buffer using semaphores and locks I've already implemented in C. I need to have the producer place "hello world" onto a 5-byte buffer, one character at a time. The producer needs to block if the buffer is full. I am struggling to figure out how to place the string on the buffer. Here is what I have so far:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUF_SIZE 5
// the buffer works like a stack for
// the sake of simplicity, if needed
// we may implement a queue
typedef struct {
int buf[BUF_SIZE]; // the buffer
size_t len; // number of items in the buffer
pthread_mutex_t mutex; // needed to add/remove data from the buffer
pthread_cond_t can_produce; // signaled when items are removed
pthread_cond_t can_consume; // signaled when items are added
} buffer_t;
// produce random numbers
void* producer(void *arg) {
buffer_t *buffer = (buffer_t*)arg;
while(1) {
#ifdef UNDERFLOW
// used to show that if the producer is somewhat "slow"
// the consumer will not fail (i.e. it'll just wait
// for new items to consume)
sleep(rand() % 3);
#endif
pthread_mutex_lock(&buffer->mutex);
if(buffer->len == BUF_SIZE) { // full
// wait until some elements are consumed
pthread_cond_wait(&buffer->can_produce, &buffer->mutex);
}
// in real life it may be some data fetched from
// sensors, the web, or just some I/O
int t = rand();
printf("Produced: %d\n", t);
// append data to the buffer
buffer->buf[buffer->len] = t;
++buffer->len;
// signal the fact that new items may be consumed
pthread_cond_signal(&buffer->can_consume);
pthread_mutex_unlock(&buffer->mutex);
}
// never reached
return NULL;
}
// consume random numbers
void* consumer(void *arg) {
buffer_t *buffer = (buffer_t*)arg;
while(1) {
#ifdef OVERFLOW
// show that the buffer won't overflow if the consumer
// is slow (i.e. the producer will wait)
sleep(rand() % 3);
#endif
pthread_mutex_lock(&buffer->mutex);
if(buffer->len == 0) { // empty
// wait for new items to be appended to the buffer
pthread_cond_wait(&buffer->can_consume, &buffer->mutex);
}
// grab data
--buffer->len;
printf("Consumed: %d\n", buffer->buf[buffer->len]);
// signal the fact that new items may be produced
pthread_cond_signal(&buffer->can_produce);
pthread_mutex_unlock(&buffer->mutex);
}
// never reached
return NULL;
}
int main(int argc, char *argv[]) {
buffer_t buffer = {
.len = 0,
.mutex = PTHREAD_MUTEX_INITIALIZER,
.can_produce = PTHREAD_COND_INITIALIZER,
.can_consume = PTHREAD_COND_INITIALIZER
};
pthread_t prod, cons;
pthread_create(&prod, NULL, producer, (void*)&buffer);
pthread_create(&cons, NULL, consumer, (void*)&buffer);
pthread_join(prod, NULL); // will wait forever
pthread_join(cons, NULL);
return 0;
}
a stack is a Last in, First out. Not what you want.
I would strongly implementing a circular buffer, with a head and tail pointer.
Then use the mutex (no need for a semaphore) to lock the critical code.
(the critical code for the producer is where it is adding/trying to add a char)
(the critical code for the consumer is where it is removing/trying to remove a char)
the mutex is so the two critical variables 'head' and 'tail' are stable
while any one process is accessing the circular queue.