I am doing an academic exercise for an OS class where we synchronize five detached threads using ONLY mutex locks and unlocks. We "CANNOT force the threads into any serial execution. Once spawned they must be free from external influences (other than the mutexes). The parent should NOT employ a pthread_join."
I am spawning 5 threads, detaching them and then using each threads to read in data and update a global variable. Currently my code spawns the 5 threads but only three of them output their ID's and none of them get into the while loop. Any help/advice here would be appreciated!
Output:
thread: 6156515168
thread: 6156515192
thread: 6156515176
There is a sleep in main which if uncommented provides the expected output, but this would be forcing a serial execution..
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <pthread.h>
pthread_mutex_t mutex_lock = PTHREAD_MUTEX_INITIALIZER; // declaring mutex
int FileNameHelper=1;
int balance=0;
void* detatchedThread(void *param){
long tid = (long)param;
char* base = "data";
char filename[256];
char buf[100];
sprintf(filename, "%s%d.in", base, FileNameHelper); //creates data1.in, data2.in...
FileNameHelper ++;
FILE *inputFile = fopen(filename, "r");
printf ("thread: %ld\n", tid);
// critical sec line
if(fgets(buf, sizeof buf, inputFile) == NULL)
return NULL; // could not read first line
sleep(1); // make sure each thread runs long enough to get the random update behavior required.
pthread_mutex_lock(&mutex_lock); //we are in the critical section, lock mutex
while(fgets(buf, sizeof buf, inputFile) != NULL) {
int val;
if(sscanf(buf, "%d", &val) != 1){
break;
}
printf("%d\n", val);
balance += val;
printf ("Account balance after thread %ld is $%d\n", tid, balance);
}
pthread_mutex_unlock(&mutex_lock);
if(buf[0] != 'W')
return NULL;// last line data was invalid
pthread_exit(NULL);
}
int main(){
pthread_t th[5];
//initialize the mutex
if(pthread_mutex_init(&mutex_lock, NULL) != 0){
printf("\nmutex init has failed\n");
return 1;
}
//call the 5 threads, Detach the threads once they are created.
for (int i = 0; i < 5; i++){
pthread_create(&th[i], NULL, detatchedThread, (void *)&th[i]);
pthread_detach(th[i]);
//sleep(1); uncommenting this line gives me the expected behavior
}
pthread_mutex_destroy(&mutex_lock);
return 0;
}
I am learning some simple producer/consumer examples involving semaphores, but having a hard time explaining to myself why I am getting a certain result (because I am a noobie to C).
When I run two concurrent processes of the following producer, both processes get hung up and never finish:
#include <stdio.h>
#include <sys/types.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/shm.h>
#define BUFF_SIZE 20
typedef struct {
char buffer[BUFF_SIZE];
int nextIn;
int nextOut;
} shared_data;
shared_data *shm, *s;
char sem_name1[] = "mutex";
char sem_name2[] = "empty_slots";
char sem_name3[] = "full_slots";
sem_t *empty_slots;
sem_t *full_slots;
sem_t *mutex;
void Put(char item)
{
sem_wait(empty_slots);
sem_wait(mutex);
s->buffer[s->nextIn] = item;
s->nextIn = (s->nextIn + 1) % BUFF_SIZE;
sem_post(mutex);
printf("Producing %c ... with pid = %d\n", item, getpid());
sem_post(full_slots);
}
void Producer()
{
int i;
for(i = 0; i < 10; i++)
{
sleep(rand()%3);
Put((char)('A'+ i % 26));
}
}
void main()
{
//Create and initialize the semaphores
mutex=sem_open(sem_name1, O_CREAT,0644, 1);
full_slots=sem_open(sem_name3, O_CREAT,0644, 0);
empty_slots=sem_open(sem_name2, O_CREAT,0644, 10);
//Create a key for the segment
key_t key;
key = 1234;
//create the segment
int shmid;
if ((shmid = shmget(key, sizeof(shared_data), IPC_CREAT |0666)) <0)
{
perror("Shmget");
exit(1);
}
//attach to the segment
if ((shm = (shared_data *) shmat(shmid, NULL, 0))==(shared_data *) -1)
{
perror("Shmat");
exit(1);
}
s=shm;
s->nextIn = 0;
Producer();
//detach from the segment
shmdt((void *) shm);
}
The output from the first and second processes, respectively:
Can someone help me understand why this happens (i.e. why do these processes never finish)? Have I created a deadlock? Why or why not? I am completely stumped.
Thanks!
I am trying to manually interrupt the main thread of a program when it is blocked on a read() system call. I do this in a second thread with a call to pthread_kill() however a segmentation fault occurs. However if I place the call to read() in the scond thread, i.e. NOT the main thread and call pthread_kill() from the main thread then all works as expected.
For example, the following code results in a segmentation fault, where I call pthread_kill() in the second thread, approximatelt 2 seconds after it is started. It uses the pthread_t of the main thread obtained by a call (in the main thread) to pthread_self():
Example 1
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <unistd.h>
#include <signal.h>
static int fd = 0;
unsigned char buf[255];
static pthread_t s;
void sigHandler(int sig){
printf("Signal handler called.\n");
}
void * closeFD(void *arg){
printf("Second thread started.\n");
sleep(2);
int r = pthread_kill(s, SIGUSR1);
}
int main(char *argv[], int argc){
struct termios newtio;
pthread_t t1;
unsigned char buf[255];
void *res;
struct sigaction int_handler = {.sa_handler=sigHandler};
sigaction(SIGUSR1,&int_handler,0);
s = pthread_self();
printf("Process id is: %d.\n", getpid());
fd = open("/dev/ttyS0", O_RDONLY | O_NOCTTY);
if (fd != -1){
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = B2400 | CS7 | CLOCAL | CREAD ;
newtio.c_iflag = ICRNL;
newtio.c_oflag = 0;
newtio.c_lflag = ~ICANON;
newtio.c_cc[VMIN] = 14;
tcsetattr(fd,TCSANOW,&newtio);
pthread_create(&t1, NULL, closeFD, NULL);
printf("Reading ..\n");
read(fd,buf,255);
close(fd);
}
return 0;
}
The following code is the same except I place the call to read() in the second thread (in closeFD()) and works as expected. The second thread unblocks and terminates while the main thread waits for it to exit then exits itself.
Example 2:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <unistd.h>
#include <signal.h>
static int fd = 0;
unsigned char buf[255];
static pthread_t s;
void sigHandler(int sig){
printf("Signal handler called.\n");
}
void * closeFD(void *arg){
printf("Second thread started.\n");
read(fd,buf,255);
printf("Read interrupted.\n");
}
int main(char *argv[], int argc){
struct termios newtio;
pthread_t t1;
unsigned char buf[255];
void *res;
struct sigaction int_handler = {.sa_handler=sigHandler};
sigaction(SIGUSR1,&int_handler,0);
s = pthread_self();
printf("Process id is: %d.\n", getpid());
fd = open("/dev/ttyS0", O_RDONLY | O_NOCTTY);
if (fd != -1){
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = B2400 | CS7 | CLOCAL | CREAD ;
newtio.c_iflag = ICRNL;
newtio.c_oflag = 0;
newtio.c_lflag = ~ICANON;
newtio.c_cc[VMIN] = 14;
tcsetattr(fd,TCSANOW,&newtio);
pthread_create(&t1, NULL, closeFD, NULL);
sleep(2);
int r = pthread_kill(t1, SIGUSR1);
pthread_join(t1, &res);
close(fd);
}
return 0;
}
So far I have not been able to find a specific reference stating that terminating the main thread from a second (within the same process) is an illegal operation, so is there something I am doing wrong?
UPDATE #1
Thanks for all those that have replied, however I should make a few points clear:
I am aware that using printf in the signal handler is unsafe however this is an example and it's not the cause of the segmentation fault, though it is a valid point. Taking the printf() out of the signal handler still results in a segmentation fault. Example 2 works with printf() either in or out of the signal handler.
I know sending a SIGUSR will not terminate the program. However by using the pthread_kill(pthread_t thread, int signal) it WILL send a signal to the thread thread and it will unblock (if indeed it is blocked). This is the action I desire, this is what actually happens in Example 2 and this is what I understand should happen in either example, but does not in example 1.
When describing example 1, I used the term 'method' when I meant 'thread', where I mention the call to pthread_kill().
Further, quoting from 'Programming with POSIX Threads', David R. Butenhof, section 6.6.3 p217 'pthread_kill':
Within a process, one thread can send a signal to a specific thread
(including itself) by calling pthread_kill.
With that said, the following example ALSO gives a segmentation fault:
Example 3
#include <stdio.h>
#include <string.h>
#include <string.h>
#include <signal.h>
static pthread_t s;
int value = 0;
void sigHandler(int sig){
value = 1;
}
int main(char *argv[], int argc){
struct sigaction int_handler = {.sa_handler=sigHandler};
sigaction(SIGUSR1,&int_handler,0);
s = pthread_self();
printf("The value of 'value' is %d.\n", value);
printf("Process id is: %d.\n", getpid());
int r = pthread_kill(s, SIGUSR1);
printf("The value of 'value' is %d.\n", value);
return 0;
}
This also fails if instead of a call to sigaction() is replaced by the (non-portable) call to signal(). With the third example in mind, which is very simple, I am not able to find any documentation that expressly states it is an illegal action. In fact the quoted reference indicates it's allowable!
You forgot to #include <pthread.h>. That fixes your segfault for me in example #3 on a recent Linux system.
--- pthread_kill-self.c.orig 2015-01-06 14:08:54.949000690 -0600
+++ pthread_kill-self.c 2015-01-06 14:08:59.820998965 -0600
## -1,6 +1,6 ##
#include <stdio.h>
#include <string.h>
-#include <string.h>
+#include <pthread.h>
#include <signal.h>
and then...
$:- gcc -o pthread_kill-self pthread_kill-self.c -pthread
$:- ./pthread_kill-self
The value of 'value' is 0.
Process id is: 3152.
The value of 'value' is 1.
You're using printf(), which is not async-signal safe, and you're not initializing your struct sigaction properly (in particular, the signal mask is left undefined).
Third, sending a SIGUSR1 signal, with a handler installed, does not and should not terminate the main thread. You're just sending it a signal, that's all.
As Jens Gustedt mentioned in his comment to the original question, both of the programs have undefined behaviour. Therefore, I'm not going to try and guess exactly what part of the undefined behaviour causes the segmentation fault (in the first program).
Instead, I'll show you a working example.
For debugging/testing purposes, I like to start with async-signal safe output functions, based on write(2):
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <termios.h>
#include <pthread.h>
#include <errno.h>
#include <time.h>
#define MYSIGNAL SIGUSR1
#define SECONDS 10
static int wrstr(const int descriptor, const char *p, const char *const q)
{
while (p < q) {
ssize_t n;
n = write(descriptor, p, (size_t)(q - p));
if (n > (ssize_t)0)
p += n;
else
if (n != (ssize_t)-1)
return EIO;
else
if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)
return errno;
}
return 0;
}
static const char *ends(const char *s)
{
if (s)
while (*s != '\0')
s++;
return s;
}
static int wrout(const char *const p)
{
if (p != NULL && *p != '\0') {
int saved_errno, result;
saved_errno = errno;
result = wrstr(STDOUT_FILENO, p, ends(p));
errno = saved_errno;
return result;
} else
return 0;
}
static int wrouti(const int value)
{
char buffer[32];
char *p = buffer + sizeof buffer;
unsigned int u;
if (value < 0)
u = -(long)value;
else
u = value;
do {
*(--p) = '0' + (u % 10U);
u /= 10U;
} while (u > 0U);
if (value < 0)
*(--p) = '-';
return wrstr(STDOUT_FILENO, p, buffer + sizeof buffer);
}
static int wrerr(const char *const p)
{
if (p != NULL && *p != '\0') {
int saved_errno, result;
saved_errno = errno;
result = wrstr(STDERR_FILENO, p, ends(p));
errno = saved_errno;
return result;
} else
return 0;
}
The above functions are async-signal safe, and therefore okay to use in a signal handler. wrout() and wrerr() also retain errno unchanged, which is useful. Saving and restoring errno in a signal handler is usually omitted, by the way, although I do believe there are some odd corner cases it might matter. The wrouti() is just a crude decimal signed integer printer, also async-signal-safe, but it does not retain errno unchanged.
Next, let's define the signal handler itself, and an installer function for it. (I like to do it this way, to make the main() simpler.)
static volatile sig_atomic_t handled = 0;
static void handler(int signum)
{
wrerr("Signal received.\n");
handled = signum;
}
static int install_handler(const int signum)
{
struct sigaction act;
/* memset(&act, 0, sizeof act); */
sigemptyset(&act.sa_mask);
act.sa_handler = handler;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL))
return errno;
return 0;
}
The commented-out memset is recommended, but not required for proper operation. The sigemptyset(), however, is required, to clear the set of blocked signals.
Next, let's look at the thread function. You shouldn't use sleep(), as that interacts with signals; use POSIX.1-2001 nanosleep() instead.
static void *worker(void *target)
{
struct timespec duration, left;
int retval;
wrout("Worker started. Sleeping ");
wrouti((int)SECONDS);
wrout(" seconds...\n");
duration.tv_sec = SECONDS;
duration.tv_nsec = 0;
left.tv_sec = 0;
left.tv_nsec = 0;
while (1) {
retval = nanosleep(&duration, &left);
if (retval == 0)
break;
if (left.tv_sec <= 0 ||
(left.tv_sec == 0 && left.tv_nsec <= 0))
break;
duration = left;
left.tv_sec = 0;
left.tv_nsec = 0;
}
wrout("Sleep complete.\n");
if (target) {
wrout("Sending signal...\n");
retval = pthread_kill(*(pthread_t *)target, MYSIGNAL);
if (retval == 0)
wrout("Signal sent successfully.\n");
else {
const char *const errmsg = strerror(retval);
wrout("Failed to send signal: ");
wrout(errmsg);
wrout(".\n");
}
}
wrout("Thread done.\n");
return NULL;
}
The pointer given to the thread function should point to the thread identifier (pthread_t) the signal is directed to.
Note that above, nanosleep() can be interrupted by a signal delivery, if the signal is delivered to or caught by this particular thread. If that occurs, nanosleep() tells us how much time was left to sleep. The loop above shows how to make sure you sleep at least the specified time, even if interrupted by signal delivery.
Finally, the main(). Instead of opening a specific device, I use standard input. To reproduce OP's program, redirect standard input from /dev/ttyUSB0, i.e. ./program < /dev/ttyUSB0, when executing it.
int main(void)
{
pthread_t main_thread, worker_thread;
pthread_attr_t attrs;
struct termios original, settings;
int result;
if (!isatty(STDIN_FILENO)) {
wrerr("Standard input is not a terminal.\n");
return EXIT_FAILURE;
}
if (tcgetattr(STDIN_FILENO, &original) != 0 ||
tcgetattr(STDIN_FILENO, &settings) != 0) {
const char *const errmsg = strerror(errno);
wrerr("Cannot get terminal settings: ");
wrerr(errmsg);
wrerr(".\n");
return EXIT_FAILURE;
}
settings.c_lflag = ~ICANON;
settings.c_cc[VMIN] = 14;
if (tcsetattr(STDIN_FILENO, TCSANOW, &settings) != 0) {
const char *const errmsg = strerror(errno);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &original);
wrerr("Cannot set terminal settings: ");
wrerr(errmsg);
wrerr(".\n");
return EXIT_FAILURE;
}
wrout("Terminal is now in raw mode.\n");
if (install_handler(MYSIGNAL)) {
const char *const errmsg = strerror(errno);
wrerr("Cannot install signal handler: ");
wrerr(errmsg);
wrerr(".\n");
return EXIT_FAILURE;
}
main_thread = pthread_self();
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, 65536);
result = pthread_create(&worker_thread, &attrs, worker, &main_thread);
if (result != 0) {
const char *const errmsg = strerror(errno);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &original);
wrerr("Cannot create a worker thread: ");
wrerr(errmsg);
wrerr(".\n");
return EXIT_FAILURE;
}
pthread_attr_destroy(&attrs);
wrout("Waiting for input...\n");
while (1) {
char buffer[256];
ssize_t n;
if (handled) {
wrout("Because signal was received, no more input is read.\n");
break;
}
n = read(STDIN_FILENO, buffer, sizeof buffer);
if (n > (ssize_t)0) {
wrout("Read ");
wrouti((int)n);
wrout(" bytes.\n");
continue;
} else
if (n == (ssize_t)0) {
wrout("End of input.\n");
break;
} else
if (n != (ssize_t)-1) {
wrout("read() returned an invalid value.\n");
break;
} else {
result = errno;
wrout("read() == -1, errno == ");
wrouti(result);
wrout(": ");
wrout(strerror(result));
wrout(".\n");
break;
}
}
wrout("Reaping the worker thread..\n");
result = pthread_join(worker_thread, NULL);
if (result != 0) {
wrout("Failed to reap worker thread: ");
wrout(strerror(result));
wrout(".\n");
} else
wrout("Worker thread reaped successfully.\n");
tcsetattr(STDIN_FILENO, TCSAFLUSH, &original);
wrout("Terminal reverted back to original mode.\n");
return EXIT_SUCCESS;
}
Because it's much more fun to test using the terminal, the above tries hard to restore the terminal to its original state before returning.
Note that since the VMIN field in the termios structure is set to 14, the read() blocks until at least 14 bytes are available in the buffer. If a signal is delivered, a short count is returned if there is at least one byte in the buffer. Therefore, you cannot expect the read() to always return 14 bytes, and you cannot expect it to return -1 with errno == EINTR whenever a signal is delivered! Experimenting with this program is very useful, to clarify these in your mind.
I don't remember whether the USB serial drivers in Linux ever produce EPIPE or raise SIGPIPE, but that can definitely occur when using pipes. When using pipes, the most common reason is trying to read after read has already returned zero (end of input). Unless ignored or caught with a signal handler, the process dies much like in a segmentation fault, except that the cause is SIGPIPE signal instead of SIGSEGV. With terminal-like character devices, it depends on the driver, I seem to recall.
Finally, I wrote the above code under the weather (flu), so there might be bugs in tharrr. It should be POSIX.1 C99 code, and gcc -Wall -pedantic does not complain, but having a stuffed head, I'm not making any promises here. Fixes are more than welcome!
Questions? Comments?
I'm trying to migrate a multiprocess application to Docker. Different processes will be placed in different Docker container.
The application uses shared memory to exchange data and semaphores to synchronize. I already recompiled Docker in order to do not use the IPC namespace and I effectively checked with sudo ipcs -m that the shared memory buffers are accessible from the different containers.
The problem is that semaphores are not working. I wrote these simple programs to check the behavior of POSIX semaphores in Docker:
/* To be compiled with -lpthread */
#include <stdio.h>
#include <fcntl.h>
#include <semaphore.h>
int main(void) {
int ret, val;
sem_t * mySem;
printf("[ONE] Opening the semaphore...\n");
mySem = sem_open("sem1", O_CREAT, 0777, 0);
if (mySem == SEM_FAILED) {
printf("[ONE] Error on sem_open()\n");
return -1;
}
ret = sem_post(mySem);
getchar(); // Awful way to block execution of [ONE] for a while...
printf("[ONE] Waiting for [TWO]...\n");
ret = sem_wait(mySem);
printf("[ONE] Wait ended\n");
ret = sem_unlink("sem1");
printf("[ONE] Semaphore destroyed\n");
return 0;
}
The second program is:
/* To be compiled with -lpthread */
#include <stdio.h>
#include <fcntl.h>
#include <semaphore.h>
int main(void) {
int ret;
int val;
sem_t * mySem;
printf("[TWO] Opening the semaphore...\n");
mySem = sem_open("sem1", O_CREAT, 0777, 0);
if (mySem == SEM_FAILED) {
printf("[TWO] Error on sem_open()\n");
return -1;
}
ret = sem_getvalue(mySem, &val);
printf("[TWO] Semaphore's value is %d\n", val);
printf("[TWO] Waiting for [ONE]...\n");
ret = sem_wait(mySem);
printf("[TWO] Wait ended\n");
printf("[ONE] Doing sem_post() on semaphore...\n");
ret = sem_post(mySem);
ret = sem_close(mySem);
printf("[TWO] Semaphore closed\n");
return 0;
}
In both I omitted lots of controls like if (ret != 0) {...} in order to maintain readability of the question.
I run the first program on the host machine, the second one in a Docker container. The result is that the second program waits forever...
The question is: is it possible in some way to use POSIX semaphores between Docker containers or between a container and the host?
I solved the problem using shared memory between Docker containers as explained in this question.
The following code is a modified version of this tutorial.
File server.c
/* To be compiled with -lpthread */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <fcntl.h>
#include <semaphore.h>
#define SHM_SIZE 1000
int main(void) {
int shmid;
key_t key;
char *shm;
sem_t * mySem;
/* We'll name our shared memory segment "5678" */
key = 5678;
/* Create the segment.*/
if ((shmid = shmget(key, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666)) < 0) {
perror("shmget");
exit(1);
}
/* Now we attach the segment to our data space */
if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
perror("shmat");
exit(1);
}
/* Create a new semaphore */
mySem = sem_open("sem1", O_CREAT, 0777, 0);
/* Copy the semaphore on the shared memory segment */
memcpy(shm, mySem, sizeof(*mySem));
/* Do stuff ... */
/* REMEMBER TO USE THE SHARED MEMORY SEGMENT */
/* AND NOT THE LOCAL mySem, USE (sem_t*)shm INSTEAD! */
/* Finally, we wait until the other process
* changes the first character of our memory
* to '*', indicating that it has read what
* we put there.
*/
while (*shm != '*')
sleep(1);
/* Mark the memory segment to be destroyed */
shmctl(shmid, IPC_RMID, NULL);
/* Detach of the memory segment */
shmdt(&shm);
sem_unlink("sem1");
return 0;
}
File client.c
/* To be compiled with -lpthread */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <fcntl.h>
#include <semaphore.h>
#define SHM_SIZE 1000
int main(void) {
int shmid;
key_t key;
char *shm;
int ret, val;
key = 5678;
if ((shmid = shmget(key, SHM_SIZE, 0666)) < 0) {
perror("shmget");
exit(1);
}
if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
perror("shmat");
exit(1);
}
/* SEMAPHORE IS IN THE SHARED MEMORY SEGMENT */
/* USE (sem_t*)shm TO ACCESS IT */
*shm = '*';
shmdt(&shm);
sem_close("sem1");
return 0;
}
The code examples miss lots of controls due to readability purposes.
I run the server on the host machine, the client inside a Docker container and checked that the semaphore was accessible from both processes.
The program is supposed to create x amount of threads based on the arguments that are passed to it. argv[1] is the amount main is supposed to sleep, argv[2] is the number of propucer threads, and argv[3] is the number of consumer threads. The program compiles fine and the command I have been using to run it is: program 10 1 1.
I've been staring at this code for a while now and I can't seem to find what is causing the segmentation fault. Maybe a second set of eyes will be able to pick it quickly.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <pthread.h>
#include "buffer.h"
void *producer(void *);
void *consumer(void *);
// Semaphores
sem_t empty;
sem_t full;
pthread_mutex_t mutex;
// Buffer
int placed = 0;
buffer_item buffer[BUFFER_SIZE];
int insert_item(buffer_item item){
/* INSERT ITEM INTO BUFFER */
int z;
sem_wait(&empty);
//mutex lock
z = pthread_mutex_lock(&mutex);
if (z != 0){
return -1;
}
buffer[placed] = item;
//mutex unlock
z = pthread_mutex_unlock(&mutex);
if (z != 0){
return -1;
}
sem_post(&full);
placed++;
printf("producer produced %d\n", item);
}
int remove_item(buffer_item *item){
/* REMOVE ITEM FROM BUFFER */
int m;
placed--;
sem_wait(&full);
//mutex lock
m = pthread_mutex_lock(&mutex);
if (m != 0){
return -1;
}
buffer[placed] = -1;
//mutex unlock
m = pthread_mutex_unlock(&mutex);
if (m != 0){
return -1;
}
sem_post(&empty);
printf("consumer consumed %d\n", rand);
return 0;
}
// Main
int main(int argc, char *argv[]){
int sleepNum, pThreadNum, cThreadNum, p;
sleepNum = atoi(argv[1]);
pThreadNum = atoi(argv[2]);
cThreadNum = atoi(argv[3]);
// Initialize Semaphores & mutex
sem_init(&empty, 0, BUFFER_SIZE);
sem_init(&full, 0, 0);
pthread_mutex_init(&mutex, NULL);
// Create producer thread
pthread_t tid[pThreadNum];
int g=pThreadNum-1;
while(g >= 0){
p = pthread_create(&tid[g], NULL, producer, NULL);
g--;
}
printf("created prod thread");
// Create consumer thread
pthread_t kid[cThreadNum];
g = cThreadNum-1;
while(g >= 0){
p = pthread_create(&kid[g], NULL, consumer, NULL);
g--;
}
// Sleep for argv[0]
sleep(sleepNum);
// Destroy mutex & semaphores
sem_destroy(&empty);
sem_destroy(&full);
p = pthread_mutex_destroy(&mutex);
// Exit
exit(0);
}
// Producer
void *producer(void *param){
buffer_item rand;
unsigned int *seed;
int b;
while(1){
sleep(2);
rand = rand_r(seed);
b = insert_item(rand);
if (b < 0){
printf("Error producing item.");
}
}
}
// Consumer
void *consumer(void *param){
buffer_item rand;
int d;
while(1){
sleep(2);
d = remove_item(&rand);
if (d < 0){
printf("Error removing item");
}
}
}
Thanks in advance!
On Unix, you can get the backtrace from a segmentation fault by dumping core and examing the corefile in gdb.
$ ulimit -c <max core file size in 1k blocks>
$ gdb program core
gdb> bt
should dump the backtrace, and you can see exactly which line segfaulted.
In the producer you are using an uninitialized pointer. Try allocating some memory for it using malloc. I'm not explaining exactly what it is because you tagged this as homework.
Also don't rely on the output from printf statements to tell you where the program got to when using threads. It helps if you flush the output stream explicitly after each printf, then you'll almost get an idea of the right sequence of events.
compile your program with -g, as gcc -g program.c -lpthread
then
gdb a.out
set breakpoint as
b main
start
and then use s for step by step execution and see where it is falling.
As someone already mentioned that your producer function has uninitialized pointer unsigned int *seed;
also this program has lost wake-up problem associated with it, along with normal unlocking problem ( as in function insert item, what if insert_item thread context-switched before doing placed++. )
Here's a great article for finding the causes of segfaults.
Link
Link mirrored here, in case it ever goes down.... you never know.