0

I'm practicing semaphores using the POSIX library. I am trying to pass threads (representing customers) through one semaphore (representing a server) that sits 8 people down at two tables (each controlled by sempahores). I think the culprit is the order of unlocking and locking multiple semaphores, but I can't seem to target where the Illegal instruction (core dumped) error is coming from.

EDITED - inversed order of mutex init and creating threads loop - added return NULL to end of eat() :

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>


sem_t server_sem;
int server_pshared;
int server_ret;
int server_count = 10;

sem_t tablea_sem;
int tablea_pshared;
int tablea_ret;
int tablea_count = 4;

sem_t tableb_sem;
int tableb_pshared;
int tableb_ret;
int tableb_count = 4;



//server_ret = serm_open("serverSem", O_CREAT | O_EXCL, 0644, server_count);

int customer_count = 10;
pthread_t customer[10];
//pthread_t plates[8]

int plate_count = 8;
pthread_mutex_t plates[8];


void *eat(int n) {
   sem_wait(&server_sem);
   printf("Sitting down at Table A\n");
   //unlock table a semaphore
   sem_wait(&tablea_sem);

   //unlock 

   pthread_mutex_lock(&plates[n]);
   printf("Customer %d is eating\n", n);
   sleep(5);
   pthread_mutex_unlock(&plates[n]);
   printf("Customer %d is finished eating\n", n);
   //sem_post(&server_sem);

   sem_post(&tablea_sem);

   printf("Sitting down at Table A\n");
   //unlock table b semaphore
   sem_wait(&tableb_sem);

   //unlock 
   //sem_wait(&server_sem);
   pthread_mutex_lock(&plates[n]);
   printf("Customer %d is eating\n", n);
   sleep(5);
   pthread_mutex_unlock(&plates[n]);
   printf("Customer %d is finished eating\n", n);


   sem_post(&tableb_sem);

   sem_post(&server_sem);
   return NULL;
}

int main() {


   server_ret = sem_init(&server_sem, 1, server_count);
   tablea_ret = sem_init(&tablea_sem, 1, tablea_count);
   tableb_ret = sem_init(&tableb_sem, 1, tableb_count);

   //customer = (pthread_t[10] *)malloc(sizeof(customer));

   printf ("starting thread, semaphore is unlocked.\n");

   int i;

   for(i=0;i<plate_count;i++)
      pthread_mutex_init(&plates[i],NULL);

   for (i=0;i<customer_count;i++)
      pthread_create(&customer[i],NULL,(void *)eat,(void *)i);


   //for(i=0;i<plate_count;i++)
   //   pthread_mutex_init(&plates[i],NULL);


   for(i=0;i<customer_count;i++)
      pthread_join(customer[i],NULL);

   for(i=0;i<plate_count;i++)
      pthread_mutex_destroy(&plates[i]);

   return 0;
   }

UPDATE:

I've already accepted an answer, as it gave me good insight on what I thought was the initial problem. May still be, and simply be my lack of understanding (understatement) of this topic. Some research (man pages, and this thread) have led me to fix the errors as mentioned by SO answerers and continue to tweak this as best I can.

Now, the updated code below has attempted to address the accepted answer. But, I get the same output...am I still missing the point?

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdint.h>


sem_t server_sem;
int server_pshared;
int server_ret;
int server_count = 10;

sem_t tablea_sem;
int tablea_pshared;
int tablea_ret;
int tablea_count = 4;

sem_t tableb_sem;
int tableb_pshared;
int tableb_ret;
int tableb_count = 4;



//server_ret = serm_open("serverSem", O_CREAT | O_EXCL, 0644, server_count);

int customer_count = 10;
pthread_t customer[10];
//pthread_t plates[8]

int plate_count = 8;
pthread_mutex_t plates[8];


//void *eat(int n) {
void *eat(void *i) {

    //int n = *((int *) i);
    int n = (int)(intptr_t) i;
    //printf("Customer %d is eating", m);
    sem_wait(&server_sem);


    int j;

    for (j = 0; j<4; j++) {
      sem_wait(&tablea_sem);
      pthread_mutex_lock(&plates[j]);
      printf("Customer %d is eating\n", n);
      printf("Plate %d is eaten\n", j);
      sleep(5);
      pthread_mutex_unlock(&plates[j]);
      printf("Customer %d is finished eating\n", n);
      sem_post(&tablea_sem);
    }
    for (j = 4; j<8; j++) {
      sem_wait(&tableb_sem);
      pthread_mutex_lock(&plates[j]);
      printf("Customer %d is eating\n", n);
      printf("Plate %d is eaten\n", j);
      sleep(5);
      pthread_mutex_unlock(&plates[j]);
      printf("Customer %d is finished eating\n", n);
      sem_post(&tableb_sem);
    }
    j--;


    sem_post(&server_sem);



    return (NULL);


}

int main() {


   server_ret = sem_init(&server_sem, 1, server_count);
   tablea_ret = sem_init(&tablea_sem, 1, tablea_count);
   tableb_ret = sem_init(&tableb_sem, 1, tableb_count);

   //customer = (pthread_t[10] *)malloc(sizeof(customer));

   printf ("starting thread, semaphore is unlocked.\n");

   int i;
   int j;
   int k;




   for(i=0;i<plate_count;i++) {
      pthread_mutex_init(&plates[i],NULL);
      printf("Creating mutex for plate %d\n", i);
   }
   sem_wait(&server_sem);
   for (j=0;j<customer_count;j++) {
     pthread_create(&customer[j],NULL,(void *)eat,(void *) (intptr_t) j);
   }

   for(k=0;k<customer_count;k++) {
      pthread_join(customer[k],NULL);
      printf("Joining thread %d\n", k);
   }



   for(i=0;i<plate_count;i++) {
      pthread_mutex_destroy(&plates[i]);
   }

   sem_post(&server_sem);


   return 0;
   }

Output with gdb debugging (no breakpoints):

niu@niu-vb:~/Documents/CSU_OS$ gcc -pthread -o -g  diner diner4.c
diner: In function `_fini':
(.fini+0x0): multiple definition of `_fini'
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crti.o(.debug_info): relocation 0 has invalid symbol index 7
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crti.o(.debug_info): relocation 1 has invalid symbol index 8
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crti.o(.debug_info): relocation 2 has invalid symbol index 9
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crti.o(.debug_ranges): relocation 0 has invalid symbol index 4
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crti.o(.debug_ranges): relocation 1 has invalid symbol index 4
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crti.o(.debug_ranges): relocation 2 has invalid symbol index 5
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crti.o(.debug_ranges): relocation 3 has invalid symbol index 5
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crti.o:(.fini+0x0): first defined here
diner: In function `data_start':
(.data+0x0): multiple definition of `__data_start'
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 0 has invalid symbol index 11
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 1 has invalid symbol index 12
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 2 has invalid symbol index 2
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 3 has invalid symbol index 2
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 4 has invalid symbol index 11
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 5 has invalid symbol index 13
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 6 has invalid symbol index 13
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 7 has invalid symbol index 13
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 8 has invalid symbol index 12
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 9 has invalid symbol index 13
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 10 has invalid symbol index 13
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 11 has invalid symbol index 13
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 12 has invalid symbol index 13
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 13 has invalid symbol index 13
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 14 has invalid symbol index 13
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 15 has invalid symbol index 13
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 16 has invalid symbol index 13
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 17 has invalid symbol index 13
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 18 has invalid symbol index 13
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 19 has invalid symbol index 21
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_line): relocation 0 has invalid symbol index 2
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o:(.data+0x0): first defined here
diner: In function `data_start':
(.data+0x8): multiple definition of `__dso_handle'
/usr/lib/gcc/x86_64-linux-gnu/4.8/crtbegin.o:(.data+0x0): first defined here
diner:(.rodata+0x0): multiple definition of `_IO_stdin_used'
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o:(.rodata.cst4+0x0): first defined here
diner: In function `_start':
(.text+0x0): multiple definition of `_start'
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o:/build/eglibc-oGUzwX/eglibc-2.19/csu/../sysdeps/x86_64/start.S:118: first defined here
diner: In function `_init':
(.init+0x0): multiple definition of `_init'
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crti.o(.debug_line): relocation 0 has invalid symbol index 4
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crti.o(.debug_line): relocation 1 has invalid symbol index 5
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crti.o:/build/eglibc-oGUzwX/eglibc-2.19/csu/../sysdeps/x86_64/crti.S:64: first defined here
/tmp/cc8RaCJg.o:(.data+0x0): multiple definition of `server_count'
diner:(.data+0x10): first defined here
/tmp/cc8RaCJg.o:(.data+0x4): multiple definition of `tablea_count'
diner:(.data+0x14): first defined here
/tmp/cc8RaCJg.o:(.data+0x8): multiple definition of `tableb_count'
diner:(.data+0x18): first defined here
/tmp/cc8RaCJg.o:(.data+0xc): multiple definition of `customer_count'
diner:(.data+0x1c): first defined here
/tmp/cc8RaCJg.o:(.data+0x10): multiple definition of `plate_count'
diner:(.data+0x20): first defined here
/tmp/cc8RaCJg.o: In function `eat':
diner4.c:(.text+0x0): multiple definition of `eat'
diner:(.text+0xed): first defined here
/tmp/cc8RaCJg.o: In function `main':
diner4.c:(.text+0x184): multiple definition of `main'
diner:(.text+0x271): first defined here
/usr/lib/gcc/x86_64-linux-gnu/4.8/crtend.o:(.tm_clone_table+0x0): multiple definition of `__TMC_END__'
diner:(.data+0x28): first defined here
/usr/bin/ld: error in diner(.eh_frame); no .eh_frame_hdr table will be created.
collect2: error: ld returned 1 exit status
niu@niu-vb:~/Documents/CSU_OS$ gdb diner
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.3) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from diner...(no debugging symbols found)...done.
(gdb) run
Starting program: /home/niu/Documents/CSU_OS/diner 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
starting thread, semaphore is unlocked.
Creating mutex for plate 0
Creating mutex for plate 1
Creating mutex for plate 2
Creating mutex for plate 3
Creating mutex for plate 4
Creating mutex for plate 5
Creating mutex for plate 6
Creating mutex for plate 7
[New Thread 0x7ffff77f6700 (LWP 18606)]
Customer 0 is eating
[New Thread 0x7ffff6ff5700 (LWP 18607)]
Customer 1 is eating
[New Thread 0x7ffff67f4700 (LWP 18608)]
Customer 2 is eating
[New Thread 0x7ffff5ff3700 (LWP 18609)]
Customer 3 is eating
[New Thread 0x7ffff57f2700 (LWP 18610)]
[New Thread 0x7ffff4ff1700 (LWP 18611)]
[New Thread 0x7ffff47f0700 (LWP 18612)]
[New Thread 0x7ffff3fef700 (LWP 18613)]
[New Thread 0x7ffff37ee700 (LWP 18614)]
[New Thread 0x7ffff2fed700 (LWP 18615)]
Customer 0 is finished eating
Customer 1 is finished eating
Customer 2 is finished eating
Customer 3 is finished eating
user25976
  • 1,005
  • 4
  • 18
  • 39
  • sounds like homework – Ahmed Masud Oct 05 '17 at 19:11
  • 1
    You initializing the mutexes after creating the threads. – Eugene Sh. Oct 05 '17 at 19:12
  • also make sure that you have a return NULL at the end of your `eat` function. (or something else if you want it to be passed to pthread_join) for other threads to use. See `pthread_create(3)` man-page for details – Ahmed Masud Oct 05 '17 at 19:15
  • So you have edited it. Still the same problem? – Eugene Sh. Oct 05 '17 at 19:18
  • @EugeneSh. beat me by a few seconds. What are the mutex protecting, anyway? You don't do anything inside except sleep()? You have a different mutex for each thread? I don't understand your design. – Martin James Oct 05 '17 at 19:18
  • @EugeneSh. Yes, I just updated code, ran it again, and have the same output to console. – user25976 Oct 05 '17 at 19:18
  • Wait. `pthread_create(&customer[i],NULL,(void *)eat,(void *)i);` - what is that `(void*) i`? I mean I understand the intention, but this is not the way to pass the parameters. – Eugene Sh. Oct 05 '17 at 19:20
  • @MartinJames Nothing yet, for now I was just looking to pass the 'customer' threads through the table semaphores with 'plates' protected by mutex. Have them sit on them for five seconds, before releasing it. – user25976 Oct 05 '17 at 19:21
  • 1
    @user25976 if you want to protect a shared value from access by multiple threads, you must use ONE mutex only. All the threads should lock/unlock the same mutex. – Martin James Oct 05 '17 at 19:40
  • 1
    Your `eat()` function is not of the correct type for a thread-start function. The single argument to a thread-start function must have type `void *`, but `eat()`'s argument is an `int`. Your compiler ought to be warning you about this. In any event, undefined behavior arises from calling a function with actual arguments different from the function's formal parameter types. – John Bollinger Oct 05 '17 at 19:45
  • 1
    Additionally, you have 10 customers and 8 plates. If `eat()` is working as intended despite the argument-type problem, for each customer you attempt to lock a *plate* mutex corresponding to the *customer* number. This accesses non-existent plate mutexes. – John Bollinger Oct 05 '17 at 19:49
  • @JohnBollinger I had been tossing and turning over that warning, but have not been able to understand it. – user25976 Oct 05 '17 at 19:52
  • @MartinJames In this case, there are 8 plates. You're saying that all of the plates are controlled by one mutex, and is therefore initialized only once in the main function instead of in a loop? – user25976 Oct 05 '17 at 19:56

2 Answers2

1

The whole premise here seems a bit odd to me, as I never see any contention which will cause any thread to block. Nonetheless I do see a specific flaw that will cause the program to crash.

There are 8 "plates" (mutexes) and 10 "customers" which is the value "n" in your thread.

pthread_mutex_lock(&plates[n]);

Will work fine up through n=7, then crash when n=8 as it points to invalid memory.

Also- the correct prototype for a pthread entry function is void *function(void* arg) (not int). You must pass the value as a void* and then cast it locally back to int, if that is what you want - but note this may also produce a compiler warning about truncation since int is smaller than void* on many platforms.

Joe Hickey
  • 810
  • 7
  • 8
1

Malformation / undefined behavior

As I described in comments, you have at least two sources of undefined behavior in your program:

  1. You attempt to use eat() as a thread-start function, but it does not have the correct type. A thread-start function must accept a single parameter of type void * and return void *, but eat()'s parameter is of type int. Your call to pthread_create() has undefined behavior on account of the argument type mismatch. To the extent that pthread_create() can be construed to call the pointed-to function, that call will also have its own undefined behavior.

  2. You dispatch ten customer threads, each of which attempts to lock a different plate mutex, but there are only eight plate mutexes available. You must therefore overrun the bounds of the plate mutex array if you in fact suppose that eat() receives the argument values you seem to intend for it to do. Even if you imagine that the overrun results in manipulating accessible memory (whether it actually does is undefined) that memory certainly has not been initialized via pthread_mutex_init() for use as a mutex.

Likely one or both of those are responsible for your segfaults.

Odd behavior

You create and use a bunch of synchronization objects that you don't need. The entire body of the eat() function is protected by semaphore server_sem, and the manner in which you use that semaphore ensures that there is never more than one thread executing that function. Therefore, all usage inside of mutexes and other semaphores is moot -- there can never be any contention for those other synchronization objects.

As eat() is written, you have each customer thread lock each table semaphore in turn, and under protection of each semaphore lock a plate mutex. In terms of what you're trying to model, then, each customer eats twice, once at each table, but from the same plate.

Each customer thread uses a different plate (or attempts to do so), so there is no contention for plates. With no contention, there is no need for mutexes to protect plate access, even if the server semaphore were not also preventing contention.

Overall, it's not clear to me what interactions you are trying to model. If that's not clear to you, either, then that may be responsible for some of your difficulty. I'm inclined to guess that perhaps you want another thread to represent a server, which will cooperate with the customer threads to assign them to available seats. Even then I'm not sure I see a use for the plate mutexes.

Community
  • 1
  • 1
John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Tried to follow your suggestions as well as I understand how. I made some (false) progress and have come back to the same output, and just don't see where what seems like an out of bounds type of error is coming from. Please do revisit the edits I've made if you're willing. – user25976 Oct 06 '17 at 22:56
  • You should *always* pay attention to diagnostics. The ones you report in your update are significant, and in their light there is no reason to pay attention to anything in the debug session. The compiler's `-o` option takes an argument naming the output file, *which must immediately follow*. In your command, what immediately follows is "-g", so that's the name of the binary that was built. The linker errors arise from linking the previous "diner" executable to the result of compiling your C source file. File "diner" is unchanged, so naturally you get the same output when you run it. – John Bollinger Oct 07 '17 at 16:49