1
votes

What is the best way to coordinate the accepts of a listenning socket between multiple processes?

I am thinking of one of the two ways:

  • Have a "Master" process that will send a message to each process when it is its turn to start accepting connections.

    So the sequence will be:

    Master process gives token to Worker A. Worker A accepts connection, gives token back to Master process. Master process gives token to Worker B. etc.

  • Each process will have an accepting thread that will be spinning around a shared mutex. Lock the mutex, accept a connection, free the lock.

Any better ideas?

  • When a connection comes in ALL processes get woken up. Before accepting the connection the they try to lock a shared mutex. The one to lock the mutex first gets to accept the connection.
3
I would go with the master approach if I were you.Florin Stingaciu
Thanks for your suggestion. I have currently implemented this (the Master process sending messages as token) but I think it is too slow. For each accept I make the following "extra" systems calls. Worker calls "send()" to send back the token. Master sleeping is "select()" calls "recv()" to get the token. Then calls "send()" to send the token to Worker B and goes back to sleep in "select()". Worker B calls "recv()" to accept the token. So I think maybe it is too slow...S L

3 Answers

1
votes

I think also that master solution is good choice:

/* Process struct */
typedef struct _process_t {
    unsigned long process_id;
    struct _process_t *next;   /* next process */
    struct _process_t *prev;   /* previous process */
    struct _process_master *master_process;  /* Master process */
    int (*accepting_socket) (struct _process_t *); /* process accepet function */
    char *received_data_buffer;      /* the data received over the socket */
} process_t;

/* List of process */
typedef struct _process_list {
    process_t *head;
    process_t *tail;
    int count;
} process_list;

/* The master process */
typedef struct _process_master {
    process_list socket_listners;   /* All the process listening */
    process_list ready_listners;    /* Process ready to listen and receive*/

    ..... /* Complete this struct */
} process_master;

If you find that the solution with process is slow, you can use threads instead (They shared the same memory), but the code may increase in complexity and track bugs can be hard.

The second solution is not faster than the first one because of the cost of acquiring the mutex and the context switching between all process.

1
votes

1) I'm not sure why you wouldn't want multiple "threads" instead of "processes".

But should you require a pool of worker processes (vs. "worker threads"), then I would recommend:

2) The master process binds, listens ... and accepts all incoming connections

3) Use a "Unix socket" to pass the accepted connection from the master process to the worker process.

4) As far as "synchronization" - easy. The worker simply blocks reading the Unix socket until there's a new file descriptor for it to start using.

5) You can set up a shared memory block for the worker to communicate "busy/free" status to the master.

Here's a discussion of using a "Unix domain socket":

Stevens "Network Programming" is also an excellent resource:

0
votes

Just an update that I finally went with (3): When a connection comes in ALL processes get woken up. Before accepting the connection the they try to lock a shared mutex. The one to lock the mutex first gets to accept the connection.

It was significantly faster than passing the token back and forth.

Thanks all!