child dies, either deliberately or otherwise, we can spawn another one to replace it,
keeping the number of children within a predefined range.
- Use a singly-linked circular list to keep track of the children, forwarding requests to them
+ Use a doubly-linked circular list to keep track of the children, forwarding requests to them
in an approximately round-robin fashion.
For each child, set up two pipes:
int data_to_parent; /**< Unused. */
int current_num_children; /**< How many children are currently on the list. */
int keepalive; /**< Keepalive time for stateful sessions. */
- char* appname; /**< Name of the application.
+ char* appname; /**< Name of the application. */
/** Points to a circular linked list of children */
struct prefork_child_struct* first_child;
+ /** List of allocated but unused prefork_children, available for reuse. Each one is just
+ raw memory, apart from the "next" pointer used to stitch them together. In particular,
+ there is no child process for them, and the file descriptors are not open. */
+ struct prefork_child_struct* free_list;
transport_client* connection; /**< Connection to Jabber */
} prefork_simple;
int write_status_fd; /**< Child uses to notify parent when it's available again */
int available; /**< Boolean; true when the child is between requests */
int max_requests; /**< How many requests a child can process before terminating */
- char* appname; /**< Name of the application */
+ const char* appname; /**< Name of the application */
int keepalive; /**< Keepalive time for stateful sessions. */
struct prefork_child_struct* next; /**< Linkage pointer for linked list */
+ struct prefork_child_struct* prev; /**< Linkage pointer for linked list */
};
typedef struct prefork_child_struct prefork_child;
static void check_children( prefork_simple* forker, int forever );
static void prefork_child_process_request(prefork_child*, char* data);
static int prefork_child_init_hook(prefork_child*);
-static prefork_child* prefork_child_init(
- int max_requests, int read_data_fd, int write_data_fd,
+static prefork_child* prefork_child_init( prefork_simple* forker,
+ int read_data_fd, int write_data_fd,
int read_status_fd, int write_status_fd );
/* listens on the 'data_to_child' fd and wait for incoming data */
static void prefork_child_wait( prefork_child* child );
static void prefork_clear( prefork_simple* );
-static void prefork_child_free( prefork_child* );
+static void prefork_child_free( prefork_simple* forker, prefork_child* );
static void osrf_prefork_register_routers( const char* appname );
static void osrf_prefork_child_exit( prefork_child* );
prefork->keepalive = 0;
prefork->appname = NULL;
prefork->first_child = NULL;
+ prefork->free_list = NULL;
prefork->connection = client;
return 0;
osrfLogInternal( OSRF_LOG_MARK, "Pipes: %d %d %d %d",
data_fd[0], data_fd[1], status_fd[0], status_fd[1] );
- prefork_child* child = prefork_child_init( forker->max_requests, data_fd[0],
+ prefork_child* child = prefork_child_init( forker, data_fd[0],
data_fd[1], status_fd[0], status_fd[1] );
- child->appname = strdup(forker->appname);
- child->keepalive = forker->keepalive;
-
-
add_prefork_child( forker, child );
if( (pid=fork()) < 0 ) {
}
/**
- @brief Add a prefork_child to the list.
+ @brief Add a prefork_child to the end of the list.
@param forker Pointer to the prefork_simple that owns the list.
@param child Pointer to the prefork_child to be added.
*/
// Simplest case: list is initially empty.
forker->first_child = child;
child->next = child;
- return;
- }
-
- // Reposition forker->first_child to the last node by walking around the circle.
- prefork_child* start_child = forker->first_child;
- while(1) {
- if( forker->first_child->next == start_child )
- break;
- forker->first_child = forker->first_child->next;
+ child->prev = child;
+ } else {
+ // Find the last node in the circular list.
+ prefork_child* last_child = forker->first_child->prev;
+
+ // Insert the new child between the last and first children.
+ last_child->next = child;
+ child->prev = last_child;
+ child->next = forker->first_child;
+ forker->first_child->prev = child;
}
-
- // Insert the new child after forker->first_child.
- forker->first_child->next = child;
- child->next = start_child;
- return;
-
- // What had been the last node in the list is now the first node,
- // and the new node is second.
}
/**
*/
static void del_prefork_child( prefork_simple* forker, pid_t pid ) {
- if( forker->first_child == NULL ) { return; }
+ if( forker->first_child == NULL )
+ return; // Empty list; bail out.
- (forker->current_num_children)--;
osrfLogDebug( OSRF_LOG_MARK, "Deleting Child: %d", pid );
- prefork_child* start_child = forker->first_child; /* starting point */
- prefork_child* cur_child = start_child; /* current pointer */
- prefork_child* prev_child = start_child; /* the trailing pointer */
-
- /* special case where there is only one in the list */
- if( start_child == start_child->next ) {
- if( start_child->pid == pid ) {
- forker->first_child = NULL;
- prefork_child_free( start_child );
- }
- return;
- }
-
-
- /* special case where the first item in the list needs to be removed */
- if( start_child->pid == pid ) {
-
- /* find the last one so we can remove the start_child */
- do {
- prev_child = cur_child;
- cur_child = cur_child->next;
- } while( cur_child != start_child );
-
- /* now cur_child == start_child */
- prev_child->next = cur_child->next;
- forker->first_child = prev_child;
- prefork_child_free( cur_child );
- return;
- }
-
- do {
- prev_child = cur_child;
+ // Find the node in question
+ prefork_child* cur_child = forker->first_child; /* current pointer */
+ while( cur_child->pid != pid && cur_child->next != forker->first_child )
cur_child = cur_child->next;
- if( cur_child->pid == pid ) {
- prev_child->next = cur_child->next;
- prefork_child_free( cur_child );
- return;
+ if( cur_child->pid == pid ) {
+ // We found the right node. Remove it from the list.
+ if( cur_child->next == cur_child )
+ forker->first_child = NULL; // only child in the list
+ else {
+ if( forker->first_child == cur_child )
+ forker->first_child = cur_child->next; // Reseat forker->first_child
+
+ // Stitch the nodes on either side together
+ cur_child->prev->next = cur_child->next;
+ cur_child->next->prev = cur_child->prev;
}
-
- } while(cur_child != start_child);
+ --forker->current_num_children;
+
+ //Destroy the node
+ prefork_child_free( forker, cur_child );
+
+ } // else we didn't find a matching node; bail out
}
/**
@brief Create and initialize a prefork_child.
- @param max_requests How many requests to service before terminating.
+ @param forker Pointer to the prefork_simple that will own the prefork_child.
@param read_data_fd Used by child to read request from parent.
@param write_data_fd Used by parent to write request to child.
@param read_status_fd Used by parent to read status from child.
The calling code is responsible for freeing the prefork_child by calling
prefork_child_free().
*/
-static prefork_child* prefork_child_init(
- int max_requests, int read_data_fd, int write_data_fd,
+static prefork_child* prefork_child_init( prefork_simple* forker,
+ int read_data_fd, int write_data_fd,
int read_status_fd, int write_status_fd ) {
- prefork_child* child = (prefork_child*) safe_malloc(sizeof(prefork_child));
+ // Allocate a prefork_child -- from the free list if possible, or from
+ // the heap if necessary. The free list is a non-circular, singly-linked list.
+ prefork_child* child;
+ if( forker->free_list ) {
+ child = forker->free_list;
+ forker->free_list = child->next;
+ } else
+ child = (prefork_child*) safe_malloc(sizeof(prefork_child));
+
child->pid = 0;
- child->max_requests = max_requests;
child->read_data_fd = read_data_fd;
child->write_data_fd = write_data_fd;
child->read_status_fd = read_status_fd;
child->write_status_fd = write_status_fd;
child->available = 1;
- child->appname = NULL;
- child->keepalive = 0;
+ child->max_requests = forker->max_requests;
+ child->appname = forker->appname; // We don't make a separate copy
+ child->keepalive = forker->keepalive;
child->next = NULL;
+ child->prev = NULL;
return child;
}
// Kill all the child processes with a single call, not by killing each one separately.
// Implication: we can't have more than one prefork_simple outstanding, because
- // destroying one would kill the children of both.
+ // destroying one would kill the children of all.
while( prefork->first_child != NULL ) {
osrfLogInfo( OSRF_LOG_MARK, "Killing child processes ..." );
kill( 0, SIGKILL );
while( (child_pid = waitpid(-1, NULL, WNOHANG)) > 0)
del_prefork_child( prefork, child_pid );
- client_free(prefork->connection); // Close the Jabber connection
+ // Close the Jabber connection
+ client_free(prefork->connection);
+
+ // Physically free the free list of prefork_children.
+ prefork_child* child = prefork->first_child;
+ while( child ) {
+ prefork_child* temp = child->next;
+ free( child );
+ child = temp;
+ }
+
free(prefork->appname);
}
/**
@brief Destroy and deallocate a prefork_child.
+ @param forker Pointer to the prefork_simple that owns the prefork_child.
@param child Pointer to the prefork_child to be destroyed.
*/
-static void prefork_child_free( prefork_child* child ) {
- free(child->appname);
+static void prefork_child_free( prefork_simple* forker, prefork_child* child ) {
close( child->read_data_fd );
close( child->write_data_fd );
close( child->read_status_fd );
close( child->write_status_fd );
- free( child );
+
+ // Stick the prefork_child in a free list for potential reuse. This is a
+ // non-circular, singly linked list.
+ child->prev = NULL;
+ child->next = forker->free_list;
+ forker->free_list = child;
}