0

I'm having trouble terminating my server in my multithreaded program (one server, multiple clients).

When the variable global_var, which counts the number of currently connected clients, gets set to 0, the server should terminate, but it doesn't.

What I think is happening is since accept() is blocking , the code never reaches the break condition in main loop.

It's breaking correctly out of thread_func but then it blocks inside the while loop, just before the accept() call and after printing "Exiting thread_func".

volatile int finished = 0; // Gets set to 1 by catching SIGINT/SIGSTOP
int global_var = 0; // When it gets to 0, server should terminate
int server_fd;

void * thread_func(void* arg)
{
    do_some_pre_stuff();
    while(1)
    {
        if(!global_var)
        {
             close(server_fd);
             finished = 1;
             break;
        }

        if(recv(...) > 0)
        {
            do_more_stuff()
        }
        else
        {
            disconnect_client();
            global_var--;
            break;
        }
    }
    free_more_ressources();
    return NULL;
}


int main()
{
   do_initial_stuff();

   init_socket();

   listen();

   while (!finished)
   {
    if( (fd = accept(server_fd,...)) == -1)
         exit(-1);

    global_var++;

    /* Some intermediate code */
    if(!global_var)
        break;

    // Thread for the newly connected player
    if(pthread_create(&thread_id[...], NULL, thread_func, (void*)some_arg)
            exit(-1);
    }
    free_resources();
    puts("Exiting thread_func");

}

I tried the advice listed here without success (except the pipe answer, not trying to mess with pipes).

I'm new to socket programming but what I tried so far looked correct but none of the solutions worked (including semaphores, pthread_cancel,etc)

PS: synchronization has been implemented, just omitted here for readability

returnNULL
  • 25
  • 7
  • Go with the pipes and select, it’s the easiest way. To make it easier still take a look at ZeroMQ – bazza Jun 04 '20 at 20:27
  • Race condition: Reading and writing `global_var` on different threads without memory barrier. Try `std::atomic global_var;`. – Richard Critten Jun 04 '20 at 20:31
  • @bazza never used pipes or select before, wouldn't really know where to start @RichardCritten there are rwLocks protecting read and write acess to `global_var` – returnNULL Jun 04 '20 at 20:40
  • beej has a good section on [the care and feeding of `select`](https://beej.us/guide/bgnet/html/#select). I'd start there. – user4581301 Jun 04 '20 at 20:47
  • Your monitor thread is exiting immediately because the count is initially zero. – user207421 Jun 04 '20 at 20:51
  • Pipes are easier than sockets. Instead of socket(), bind(), accept(), connect(), listen(), you simply use pipe(). Et voila, two file descriptors, one for each end of the pipe. Use with select() or poll() just like a socket. Pipes are one directional, so you write to one of the pipe fds, and read from the other. Sockets are bidirectional, so you read/write from one fd. And that's about it. There's no other real differences to worry about. Where you currently have recv(), use select() and read either the socket or pipe (whichever has become ready to read). Or try ZeroMQ – bazza Jun 06 '20 at 09:38

0 Answers0