0

I am trying to build an elevator simulator program and I am getting started with the passenger function. I need to create a thread for each passenger and ask them to input their floor number. I have been able to create the no of passengers (threads) needed and I just need to ask them for their input. But I haven't been able to figure that out

code

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
#define MAX_PASSENGERS 5
#define floor 5
#define NUM_THREAD 6

pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER; /*Mutex Initializer*/

int dest_floor;
int elevator[floor];
int current_floor;
int no_passengers;
int t, user_input;

void *passengers(void *threadid)
{
   // Getting thread ID
   long tid;
   tid = (long)threadid;
   printf("Passenger %d Kindly select your floor number :\n", tid); 
   scanf("%d\n", &user_input); // Where I ask them for input 
   // End of Getting ID

   pthread_exit(NULL);
}

int main()
{
   pthread_t thread[NUM_THREAD];
   int th;

   for (t = 1; t < NUM_THREAD; t++)
   {
      th = pthread_create(&thread[t], NULL, passengers, (void *)t);
      if (th)
      {
         printf("ERROR Creating Thread\n");
         exit(-1);
      }
   }
   pthread_exit(NULL);
}

What I tried:

void *passengers(void *threadid)
{
   // Getting thread ID
   long tid;
   tid = (long)threadid;
   printf("Passenger %d Kindly select your floor number :\n", tid); 
   scanf("%d\n", &user_input); // Where I ask them for input 
   // End of Getting ID

   pthread_exit(NULL);
}

What I was expecting :

Passenger 2 Kindly select your floor number:2
Passenger 3 KIndly select your floor number:4
... and so on

What I got :

Passenger 1 Kindly select your floor number:
Passenger 2 KIndly select your floor number :
Passenger 3 Kindly select your floor number:
Passenger 4 KIndly select your floor number :
Passenger 5 Kindly select your floor number:
2
3
4
5
6

It doesn't allow the user to input a floor number on the prompt, but prints out all the threads and then collects the input.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    `scanf("%d\n", &user_input);` ==> `scanf("%d", &user_input);`. Please see [What is the effect of trailing white space in a scanf() format string?](https://stackoverflow.com/questions/19499060/what-is-the-effect-of-trailing-white-space-in-a-scanf-format-string) Every charatcer in the `scanf` format string has a distinct purpose. The only fluff is where the default behaviour is needlessly replicated, such as with the space here `" %d"`. – Weather Vane Nov 08 '22 at 14:18
  • 2
    It's not easily possible to scan for user input in threads because they all share the same `stdin`. Why not ask for n floor numbers before creating the threads and then pass them instread of `i` (or create a struct that contains both values and pass that) – Ingo Leonhardt Nov 08 '22 at 14:26
  • Otherwise you had to implement a synchronisation between the threads e.g. by using a mutex – Ingo Leonhardt Nov 08 '22 at 14:28
  • Hi, I don't think it's a problem with the trailing space of the format string, I tried implementing what you suggested but it's still the same output it gives. – Weke Moyosore Nov 08 '22 at 14:28
  • 2
    @WekeMoyosore Why don't you think scanning for trailing whitespaces is a problem? Just because fixing _one_ bug doesn't fix _all_ your bugs it doesn't mean that you shouldn't fix it. – Ted Lyngmo Nov 08 '22 at 14:34

2 Answers2

4

this is the updated version based on @Lundin's comment. Thanks for that

Here's a version using the mutex that you have already defined, so only one thread at a time is reading stdin. Please note that also user_input must be a local variable in passengers() because otherwise it is shared between threads and that I've fixed printf() (%ld for tid) and changed the way data is passed to the thread via arg.

Often you pass a pointer to a struct that contains all necessary information and so I do here. You must make sure that every thread receives a distinct copy of that struct. In this example this is solved by allocating new memory before each call of thread_create() which is free()d in the thread itself before exiting.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
#include <stdint.h>
#define MAX_PASSENGERS 5
#define floor 5
#define NUM_THREAD 6

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; /*Mutex Initializer*/

int dest_floor;
int elevator[floor];
int current_floor;
int no_passengers;
int t;

struct passenger_ctx    {
                long tid;
            };

void *passengers(void *ctx )
{
   // Getting thread ID
   struct passenger_ctx *p_ctx = ctx;
   long tid;
   int user_input;

   tid = p_ctx->tid;
   pthread_mutex_lock( &mutex );
   printf("Passenger %ld Kindly select your floor number :\n", tid); 
   scanf("%d", &user_input); // Where I ask them for input 
   pthread_mutex_unlock( &mutex );
   printf( "Passenger %ld selected %d\n", tid, user_input );
   // End of Getting ID

   free( p_ctx );
   pthread_exit(NULL);
}

int main()
{
   pthread_t thread[NUM_THREAD];
   int th;

   for (t = 1; t < NUM_THREAD; t++)
   {
      struct passenger_ctx *p_ctx;
      p_ctx = calloc( 1, sizeof *p_ctx );   // error check to be added
      p_ctx->tid = t;
      th = pthread_create(&thread[t], NULL, passengers, p_ctx );
      if (th)
      {
         printf("ERROR Creating Thread\n");
         exit(-1);
      }
   }
   pthread_exit(NULL);
}

Now it looks like that:

Passenger 1 Kindly select your floor number :
9
Passenger 1 selected 9
Passenger 2 Kindly select your floor number :
8
Passenger 2 selected 8
Passenger 4 Kindly select your floor number :
7
Passenger 4 selected 7
Passenger 3 Kindly select your floor number :
6
Passenger 3 selected 6
Passenger 5 Kindly select your floor number :
5
Passenger 5 selected 5
Ingo Leonhardt
  • 9,435
  • 2
  • 24
  • 33
  • 1
    `(void *)(intptr_t)t` Not something I would teach newbies or anyone else. It would make far more sense for learning purposes to allocate passenger numbers and floors in a suitable storage space (not local), then pass a pointer to that to the callback. Then they have learnt that 1) thread parameters need to be allocated in suitable memory and 2) how to properly pass parameters to a thread callback. Instead of just learning how to _not_ pass parameters to _any_ function, including thread callback functions... That is, gcc gave a warning because of code smell. – Lundin Nov 08 '22 at 15:17
  • @Lundin agreed. I will update my answer (at least if I don't forget to) but not at the moment – Ingo Leonhardt Nov 08 '22 at 17:14
  • Out of curiosity, why one thread per passenger? Is it a requirement given by the instructor? If not, then it seems like an un-necessary complexity. The whole point of threads is that they can do different things concurrently with each other, but what does a passenger do? Not much! They push a button to call the elevator, they wait for it to arrive, and then they push a button in the elevator to select a floor. The end. (Yeah! I know, they get off again, but the elevator's gonna do what it does once the buttons have been pushed. It doesn't care where or even _if_ the passengers ever get off.) – Solomon Slow Nov 09 '22 at 00:43
  • @Lundin I've finally found the time to update the answer. Maybe you'd like to take a look at it – Ingo Leonhardt Nov 09 '22 at 17:55
  • @IngoLeonhardt Nice improvement, I'd say that this is in line with best practices of parameter passing to threads. As for if the callback should return NULL or call pthread_exit, I'm not the right person to tell. An interesting question about that here: https://stackoverflow.com/questions/3844678/pthread-exit-vs-return – Lundin Nov 10 '22 at 08:02
2

Threads are.... multi-threaded. Meaning that the execution is concurrent. You can't/shouldn't assume any particular execution order, because that is handled by the OS. As is the context switch between threads which can happen at any moment. The only way to achieve what you want is to stall all other threads while the current one is executing, which would effectively defeat the purpose of using threads to begin with.

I get it that this is just an artificial academic program to learn threads, but real-world programs are rather arranged so that one single thread handles all user input. And as noted in comments, all threads share the same stdin. So there's not much making sense of this program.

The sensible solution might be to ask for all this info in main() before creating the threads, then pass the floor number as a parameter to the thread. That way you'll also practice parameter passing to threads, which is important to learn. Maybe instead have each thread print "passenger x is waiting on floor y" when it starts?

Lundin
  • 195,001
  • 40
  • 254
  • 396