| /******************************************************************************* | |
| * Copyright (c) 2008 IBM Corporation. | |
| * All rights reserved. This program and the accompanying materials | |
| * are made available under the terms of the Eclipse Public License v1.0 | |
| * which accompanies this distribution, and is available at | |
| * http://www.eclipse.org/legal/epl-v10.html | |
| * | |
| * Contributors: | |
| * IBM Corporation - Initial API and implementation | |
| *******************************************************************************/ | |
| /* | |
| * Message transport implementation. This provides an implementation of | |
| * SDM messages using TCP/IP. It includes SDM startup and wiring up the | |
| * master and server processes. | |
| * | |
| * Typically, the SDM master and server processes are started independently, | |
| * with the servers usually being started using an MPI launcher such as mpirun. | |
| * The order that the SDM master and server processes are started is not defined. | |
| * | |
| * The startup sequence is as follows: | |
| * | |
| * 1. The SDM processes form a tree with a parent/children relationship. A parent | |
| * may have many children, but each child has only one parent. The master process | |
| * is at the top of the tree and is a parent. A server process may be both a parent | |
| * and a child, but is always a child. | |
| * | |
| * 2. All SDM processes (including the master) will wait for the routing information | |
| * to become available. The routing information contains the task ID, hostname, | |
| * and port number for each of the server processes. | |
| * | |
| * 3. The master will attempt to connect to each of its direct children in turn | |
| * using sdm_connect_to_child(). This will try to connect to the child | |
| * CHILD_CONNECT_TRIES times, sleeping for CHILD_CONNECT_SLEEP seconds. If | |
| * this fails, it will increment the child's port number by one and try again. | |
| * This will continue for MAX_PORT_INCREMENT attempts before failing. | |
| * | |
| * 4. Each server attempts to bind to its port number contained in the routing information. | |
| * If the bind fails, it will increment the port number by one and try again. This | |
| * will continue MAX_PORT_INCREMENT times before failing. Once the bind has succeeded, | |
| * the server will wait for an incoming connection on the port. This wait continues | |
| * indefinitely. | |
| * | |
| * 5. Once the server has received a connection from its parent, it will attempt to | |
| * connect to its children (if it has any) using the same strategy as step 3. | |
| * | |
| * 6. Initialization is completed when all children have been connected. | |
| * | |
| * There is obviously a race condition between a parent (master or server) attempting to connect | |
| * to the child and the child setting up the socket. This should be handled by step 3 since | |
| * the parent will retry the connection. However, if the child does not start within | |
| * (CHILD_CONNECT_TRIES * CHILD_CONNECT_SLEEP) seconds of the parent, the debugger launch will | |
| * fail. | |
| * | |
| * The other issue is that the port numbers used to connect to the children must be | |
| * pre-generated so that everyone agrees. However, there is a possibility that one or | |
| * more of the port numbers are already in use. The SDM attempts to deal with this by | |
| * incrementing the port number by one if the bind fails. The problem is that the master | |
| * won't try a new port number for (CHILD_CONNECT_TRIES * CHILD_CONNECT_SLEEP) seconds | |
| * (currently 25) so it may take a long time for the SDM to start (possibly longer | |
| * than the timeout at the frontend). It may actually be better to just fail at this | |
| * point and let the user try a relaunch. | |
| */ | |
| #include <sys/types.h> | |
| #include <sys/socket.h> | |
| #include <sys/select.h> | |
| #include <netdb.h> | |
| #include <errno.h> | |
| #include <unistd.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include "compat.h" | |
| #include "list.h" | |
| #include "serdes.h" | |
| #include "sdm.h" | |
| #include "helloproto.h" | |
| #include "varint.h" | |
| #define MESSAGE_LENGTH_SIZE 4 | |
| struct sdm_message { | |
| unsigned int id; /* ID of the message */ | |
| sdm_idset dest; /* Destinations of the message */ | |
| sdm_idset src; /* Sources of the message */ | |
| sdm_aggregate aggregate; /* Message aggregation */ | |
| unsigned char * payload; /* Payload */ | |
| int payload_len; /* Payload length */ | |
| unsigned char * buf; /* Receive buffer */ | |
| int buf_len; /* Receive buffer length */ | |
| void (*send_complete)(sdm_message msg); | |
| }; | |
| /** | |
| * Base type for a linked list of ids and respective | |
| * socket descriptors | |
| */ | |
| typedef struct id_sockd_map { | |
| sdm_id id; | |
| int sockd; | |
| struct id_sockd_map *next; | |
| } sdm_id_sockd_map, *sdm_id_sockd_map_p; | |
| static void (*sdm_recv_callback)(sdm_message msg) = NULL; | |
| static void (*deliver_callback)(const sdm_message msg) = NULL; | |
| #define MAX_PORT_INCREMENT 1000 /* Max port increment before failing */ | |
| #define CHILD_CONNECT_TRIES 5 /* Number of times to try each port */ | |
| #define CHILD_CONNECT_SLEEP 5 /* Seconds to sleep between each connection attempt */ | |
| sdm_id_sockd_map_p children_sockd_map, parent_sockd_map; /* Leaf case, children = NULL | |
| Root case, parent = NULL */ | |
| /** Begin of functions created by Richard **/ | |
| /** | |
| * Create the maps and fill with the ids of the children and parent | |
| */ | |
| void | |
| sdm_create_sockd_map() | |
| { | |
| sdm_idset all_nodes; | |
| sdm_idset adjacent_nodes; | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] in sdm_create_sockd_map\n", | |
| sdm_route_get_id()); | |
| // Set parent map | |
| if (sdm_route_get_id() == SDM_MASTER) { | |
| parent_sockd_map = NULL; // Root | |
| } else { | |
| parent_sockd_map = (sdm_id_sockd_map_p)malloc(sizeof(sdm_id_sockd_map)); | |
| parent_sockd_map->id = sdm_route_get_parent(); | |
| } | |
| // Get all adjacent nodes | |
| all_nodes = sdm_set_new(); | |
| all_nodes = sdm_set_add_all(all_nodes, sdm_route_get_size()-1); | |
| // Filter the parent node and master | |
| if (parent_sockd_map != NULL) { | |
| sdm_set_remove_element(all_nodes, parent_sockd_map->id); | |
| sdm_set_remove_element(all_nodes, SDM_MASTER); | |
| } | |
| adjacent_nodes = sdm_route_get_route(all_nodes); | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] adjacent nodes: %s\n", | |
| sdm_route_get_id(), _set_to_str(adjacent_nodes)); | |
| children_sockd_map = NULL; | |
| if (!sdm_set_is_empty(adjacent_nodes)) { | |
| // Traverse adjacent nodes adding them to the children linked list | |
| sdm_id child; | |
| sdm_id_sockd_map_p p; | |
| for (child = sdm_set_first(adjacent_nodes); !sdm_set_done(adjacent_nodes); | |
| child = sdm_set_next(adjacent_nodes)) { | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] adding %d to my map\n", | |
| sdm_route_get_id(), child); | |
| p = (sdm_id_sockd_map_p)malloc(sizeof(sdm_id_sockd_map)); | |
| p->next = children_sockd_map; | |
| p->id = child; | |
| children_sockd_map = p; | |
| } | |
| } | |
| sdm_set_free(all_nodes); | |
| } | |
| /** | |
| * Binds and listen to a port. After accepting connection, updates the | |
| * | |
| * @return socket descriptor if successful, -1 if error | |
| */ | |
| int | |
| sdm_parent_port_bind(int parentbaseport) | |
| { | |
| int sockfd, peersockfd; | |
| int parentport = parentbaseport; | |
| struct sockaddr_in sockaddr_info; | |
| struct sockaddr_in peersockaddr_info; | |
| struct in_addr addr = {INADDR_ANY}; | |
| unsigned peeraddr_len = sizeof(struct in_addr); | |
| //.Create a socket and bind to the port | |
| sockfd = socket(PF_INET, SOCK_STREAM, 0); | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] [ACCEPT] sockfd: %d\n", sdm_route_get_id(), sockfd); | |
| memset(&sockaddr_info, 0, sizeof(struct sockaddr_in)); | |
| sockaddr_info.sin_family = AF_INET; | |
| sockaddr_info.sin_port = htons(parentport); | |
| sockaddr_info.sin_addr = addr; | |
| // Try to bind port until MAX_PORT_INCREMENT, except if bind error is not EADDRINUSE | |
| while (parentport < parentbaseport + MAX_PORT_INCREMENT) { | |
| if (bind(sockfd, (struct sockaddr *)&(sockaddr_info), sizeof(struct sockaddr_in)) >= 0) { | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] port bound: %d\n", sdm_route_get_id(), parentport); | |
| break; | |
| } | |
| if (errno != EADDRINUSE) { | |
| // Error! | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] Error binding to the port! - errno %d\n", | |
| sdm_route_get_id(), errno); | |
| return -1; | |
| } | |
| // Increment port number and try again | |
| sockaddr_info.sin_port = htons(++parentport); | |
| } | |
| if (parentport < parentbaseport + MAX_PORT_INCREMENT) { | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] bound to port %d\n", sdm_route_get_id(), parentport); | |
| } else { | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] could not find port to bind to\n", sdm_route_get_id()); | |
| close(sockfd); | |
| return -1; | |
| } | |
| if (listen(sockfd, 5) < 0) { | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] Error listening to the port %d!\n", sdm_route_get_id(), parentport); | |
| return -1; | |
| } | |
| // Wait for a valid client to connect | |
| while (1) { | |
| memset(&peersockaddr_info, 0, sizeof(struct sockaddr_in)); | |
| peersockfd = accept(sockfd, (struct sockaddr *)&(peersockaddr_info), &peeraddr_len); | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] peersockfd: %d\n", sdm_route_get_id(), peersockfd); | |
| if (peersockfd < 0) { | |
| perror("Status of accepting data from parent"); | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] Could not accept port connection on port %d - errno %d!\n", | |
| sdm_route_get_id(), parentport, errno); | |
| return -1; | |
| } | |
| // Check if it is connected to another sdm process before returning sockfd | |
| if (is_client_peer_valid(peersockfd)) { | |
| break; | |
| } | |
| close(peersockfd); | |
| } | |
| // We don't need to wait for connections anymore | |
| close(sockfd); | |
| return peersockfd; | |
| } | |
| /** | |
| * Connect to the child node using hostname. Port is the first that connects between | |
| * childbaseport and childbaseport + MAX_PORT_INCREMENT | |
| * | |
| * Try CHILD_CONNECT_TRIES to connect to each port, delaying CHILD_CONNECT_SLEEP between each attempt. | |
| * | |
| * This function also checks if the protocol is the sdm one. | |
| */ | |
| int | |
| sdm_connect_to_child(char *hostname, int childbaseport) | |
| { | |
| struct addrinfo hints, *result; | |
| int sockfd; | |
| int childport; | |
| int num_tries; | |
| memset(&hints, 0, sizeof(struct addrinfo)); | |
| hints.ai_family = PF_INET; | |
| hints.ai_socktype = SOCK_STREAM; | |
| for (childport = childbaseport;childport < childbaseport + MAX_PORT_INCREMENT; childport++) { | |
| char port_str[10]; | |
| for (num_tries = 0; num_tries < CHILD_CONNECT_TRIES; num_tries++) { | |
| // Get first result from the linked list | |
| sockfd = socket(PF_INET, SOCK_STREAM, 0); | |
| if (sockfd < 0) { | |
| perror("Socket"); | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] socket syscall error\n", sdm_route_get_id()); | |
| return -1; | |
| } | |
| // Connect to the provided address & port | |
| sprintf(port_str, "%d", childport); | |
| if (getaddrinfo(hostname, port_str, &hints, &result)) { | |
| perror("getaddrinfo"); | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] getaddrinfo error. hostname: %s, port: %s\n", | |
| sdm_route_get_id(), hostname, port_str); | |
| return -1; | |
| } | |
| if (connect(sockfd, result->ai_addr, result->ai_addrlen) >= 0) { | |
| break; | |
| } | |
| if( (errno != ECONNREFUSED) && (errno != ENETUNREACH) && | |
| (errno != ETIMEDOUT) ) { | |
| perror("connect"); | |
| freeaddrinfo(result); | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] connect error. hostname: %s, port: %s\n", | |
| sdm_route_get_id(), hostname, port_str); | |
| return -1; | |
| } | |
| close(sockfd); | |
| sleep(CHILD_CONNECT_SLEEP); | |
| } | |
| if (num_tries < CHILD_CONNECT_TRIES){ | |
| freeaddrinfo(result); | |
| // Check if it is connected to another sdm process before returning sockfd | |
| if (is_server_peer_valid(sockfd)) { | |
| return sockfd; | |
| } | |
| close(sockfd); | |
| } | |
| } | |
| // No valid peer found! | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] No port found for the sdm child. hostname: %s\n", | |
| sdm_route_get_id(), hostname); | |
| return -1; | |
| } | |
| /** | |
| * Initializes the message layer using tcp/ip to transport data | |
| */ | |
| int | |
| sdm_tcpip_init() | |
| { | |
| routing_table_entry * entry; | |
| sdm_id_sockd_map_p mapp; | |
| sdm_create_sockd_map(); | |
| // If it isn't the master node bind to the port provided | |
| if (sdm_route_get_id() != SDM_MASTER) { | |
| int parentsockd = -1; | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] This node is a server!\n", sdm_route_get_id()); | |
| for (sdm_routing_table_set(); (entry = sdm_routing_table_next()) != NULL; ) { | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] found entry for node %d\n", sdm_route_get_id(), entry->nodeID); | |
| if(entry->nodeID == sdm_route_get_id()) { | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] found my entry\n", sdm_route_get_id()); | |
| parentsockd = sdm_parent_port_bind(entry->port); | |
| break; | |
| } | |
| } | |
| if (parentsockd < 0) { | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] failed to bind to port\n", sdm_route_get_id()); | |
| return -1; | |
| } | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] Parent %d successfully connected\n", sdm_route_get_id(), parent_sockd_map->id); | |
| // Update the parent socket descriptor | |
| parent_sockd_map->sockd = parentsockd; | |
| } | |
| // If node is leaf, initialization is done | |
| if (children_sockd_map == NULL) { | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] This node is a leaf\n", sdm_route_get_id()); | |
| return 0; | |
| } | |
| // Otherwise, open connections to the children | |
| for (sdm_routing_table_set(); (entry = sdm_routing_table_next()) != NULL; ) { | |
| int childsockd; | |
| mapp = children_sockd_map; | |
| while (mapp != NULL) { | |
| if (mapp->id == entry->nodeID) { | |
| // ID found! Connect to the children and generate a socket descriptor | |
| childsockd = sdm_connect_to_child(entry->hostname, entry->port); | |
| if (childsockd < 0) { | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] failed to connect to child %s:%d\n", sdm_route_get_id(), entry->hostname, entry->port); | |
| return -1; | |
| } | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] Connection to child %d successful\n", sdm_route_get_id(), mapp->id); | |
| // Set the children socket descriptor | |
| mapp->sockd = childsockd; | |
| break; | |
| } | |
| mapp = mapp->next; | |
| } | |
| } | |
| return 0; | |
| } | |
| /** | |
| * Close sockets and free all map structures | |
| */ | |
| void | |
| sdm_clean_maps() | |
| { | |
| if (parent_sockd_map != NULL) { | |
| close(parent_sockd_map->sockd); | |
| free(parent_sockd_map); | |
| } | |
| while (children_sockd_map != NULL) { | |
| sdm_id_sockd_map_p p = children_sockd_map->next; | |
| close(children_sockd_map->sockd); | |
| free(children_sockd_map); | |
| children_sockd_map = p; | |
| } | |
| } | |
| /** | |
| * Given a nodeid, return an associated socket descriptor | |
| * | |
| * @return socket descriptor on success, -1 on error | |
| */ | |
| int | |
| sdm_fetch_sockd(int nodeid) | |
| { | |
| sdm_id_sockd_map_p p; | |
| // Look for nodeid on the children list first | |
| p = children_sockd_map; | |
| while (p != NULL) { | |
| if (p->id == nodeid) { | |
| return p->sockd; | |
| } | |
| p = p->next; | |
| } | |
| // Then, must be the parent | |
| p = parent_sockd_map; | |
| if (p->id == nodeid) { | |
| return p->sockd; | |
| } | |
| // Not mapped! Return an error code! | |
| return -1; | |
| } | |
| /** | |
| * Given a sockid, return an associated id | |
| * | |
| * @return id on success, -1 on error | |
| */ | |
| int | |
| sdm_fetch_nodeid(int sockd) | |
| { | |
| sdm_id_sockd_map_p p; | |
| // Look for nodeid on the children list first | |
| p = children_sockd_map; | |
| while (p != NULL) { | |
| if (p->sockd == sockd) { | |
| return p->id; | |
| } | |
| p = p->next; | |
| } | |
| // Then, must be the parent | |
| p = parent_sockd_map; | |
| if (p->sockd == sockd) { | |
| return p->id; | |
| } | |
| // Not mapped! Return an error code! | |
| return -1; | |
| } | |
| /** | |
| * Inspect all node's sockets for data, returning the socket descriptor | |
| * of a socket which contains data. | |
| * | |
| * @return socket descriptor on success, -1 on error, -2 if no socket has data | |
| */ | |
| int | |
| sdm_get_active_sock_desc() | |
| { | |
| fd_set rfdset; | |
| int fdmax = -1; | |
| int selrv; | |
| sdm_id_sockd_map_p p; | |
| struct timeval time = {0, 10000}; | |
| // Add all valid sockets to the FD_SET | |
| FD_ZERO(&rfdset); | |
| if (parent_sockd_map != NULL) { | |
| FD_SET(parent_sockd_map->sockd, &rfdset); | |
| fdmax = parent_sockd_map->sockd; | |
| } | |
| for (p = children_sockd_map; p != NULL; ) { | |
| FD_SET(p->sockd, &rfdset); | |
| if (p->sockd > fdmax) { | |
| fdmax = p->sockd; | |
| } | |
| p = p->next; | |
| } | |
| if (fdmax < 0) { | |
| // No children socket opened | |
| return -2; | |
| } | |
| // Check if any of the sockets connected | |
| // but don't block | |
| while ((selrv = select(fdmax + 1, &rfdset, NULL, NULL, &time)) < 0) { | |
| if (errno != EINTR) { | |
| DEBUG_PRINTS(DEBUG_LEVEL_MESSAGES, "select syscall failed!\n"); | |
| return -1; | |
| } | |
| } | |
| if (selrv == 0) { // No socket has data. Return appropriated code | |
| return -2; | |
| } | |
| // return the first active socket descriptor | |
| for (p = children_sockd_map; p != NULL; ) { | |
| if (FD_ISSET(p->sockd, &rfdset)) { | |
| return p->sockd; | |
| } | |
| p = p->next; | |
| } | |
| if (parent_sockd_map != NULL) { | |
| if (FD_ISSET(parent_sockd_map->sockd, &rfdset)) { | |
| return parent_sockd_map->sockd; | |
| } | |
| } | |
| // Should not reach this point | |
| return -1; | |
| } | |
| /** | |
| * Provides the number of bytes to be read of the message | |
| * (the size of the header is already subtracted) | |
| */ | |
| int | |
| sdm_tcpip_msgheader_receive(int sockfd, int *length) | |
| { | |
| int i; | |
| int n; | |
| int len; | |
| int msg_len = 0; | |
| unsigned char * p; | |
| unsigned char length_str[MESSAGE_LENGTH_SIZE]; | |
| p = length_str; | |
| len = MESSAGE_LENGTH_SIZE; | |
| while ((n = read(sockfd, p, len)) < len) { | |
| if (n <= 0) { | |
| if (n < 0) { | |
| if (errno == EINTR) { | |
| continue; | |
| } | |
| perror("read"); | |
| } | |
| return -1; | |
| } | |
| p += n; | |
| len -= n; | |
| } | |
| for (i = 0; i < MESSAGE_LENGTH_SIZE; i++) { | |
| msg_len = (msg_len << 8) | (length_str[i] & 0xff); | |
| } | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "MESSAGE_LEN:%d\n", msg_len); | |
| *length = msg_len; | |
| return 0; | |
| } | |
| int | |
| sdm_tcpip_msgbody_receive(int sockfd, unsigned char *buf, int length) | |
| { | |
| int n; | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] About to recv %d\n", sdm_route_get_id(), length); | |
| while ((n = read(sockfd, buf, length)) < length) { | |
| if (n <= 0) { | |
| if (n < 0) { | |
| if (errno == EINTR) { | |
| continue; | |
| } | |
| perror("read"); | |
| } | |
| return -1; | |
| } | |
| buf += n; | |
| length -= n; | |
| } | |
| return 0; | |
| } | |
| int | |
| sdm_tcpip_send(int sockd, unsigned char *buf, int length) | |
| { | |
| int n; | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] About to send %d\n", sdm_route_get_id(), length); | |
| while ((n = write(sockd, (void *)buf, length)) < length) { | |
| if (n <= 0) { | |
| if (n < 0) { | |
| if (errno == EINTR) { | |
| continue; | |
| } | |
| perror("writes"); | |
| } | |
| return -1; | |
| } | |
| buf += n; | |
| length -= n; | |
| } | |
| return 0; | |
| } | |
| /** End of functions created by Richard **/ | |
| /** | |
| * Initialize the runtime abstraction. The jobid is the job ID | |
| * that was allocated by the OpenMPI runtime system. This is used | |
| * to set an environment variable that will enable MPI_Init to | |
| * establish communication. | |
| * | |
| * @return 0 on success, -1 on failure | |
| */ | |
| int | |
| sdm_message_init(int argc, char *argv[]) | |
| { | |
| /* | |
| * Initializes the communication layer | |
| */ | |
| if (sdm_tcpip_init() < 0) { | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| /** | |
| * Finalize the message abstraction. | |
| */ | |
| void | |
| sdm_message_finalize() | |
| { | |
| // Clean the maps | |
| sdm_clean_maps(); | |
| } | |
| /** | |
| * Send a message to the destinations. | |
| * | |
| * @return 0 on success, -1 on failure | |
| */ | |
| int | |
| sdm_message_send(const sdm_message msg) | |
| { | |
| int i; | |
| int len; | |
| int msglen; | |
| int maxlen; | |
| unsigned char * p; | |
| unsigned char * buf; | |
| sdm_id dest_id; | |
| sdm_idset route; | |
| /* | |
| * Remove our id from the destination of the message before forwarding. | |
| */ | |
| if (sdm_set_contains(msg->dest, sdm_route_get_id())) { | |
| sdm_set_remove_element(msg->dest, sdm_route_get_id()); | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] sdm_message_send removing me from dest\n", sdm_route_get_id()); | |
| } | |
| /* | |
| * Compute the immediate destinations for the message | |
| */ | |
| route = sdm_route_get_route(msg->dest); | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] sdm_message_send src %s dest %s route %s\n", sdm_route_get_id(), | |
| _set_to_str(msg->src), | |
| _set_to_str(msg->dest), | |
| _set_to_str(route)); | |
| if (!sdm_set_is_empty(route)) { | |
| /* | |
| * Create a serialized version of the message | |
| * This includes the length of the message at the beginning | |
| */ | |
| maxlen = MESSAGE_LENGTH_SIZE | |
| + MAX_VARINT_LENGTH | |
| + sdm_aggregate_serialized_length(msg->aggregate) | |
| + sdm_set_serialized_length(msg->src) | |
| + sdm_set_serialized_length(msg->dest) | |
| + msg->payload_len; | |
| buf = (unsigned char *)malloc(maxlen); | |
| p = buf + MESSAGE_LENGTH_SIZE; // Points to the body of the message | |
| /* | |
| * Note: maxlen was the maximum length of the serialized buffer, we | |
| * now serialize the buffer and calculate the actual length of the message. | |
| */ | |
| msglen = varint_encode(msg->id, p, &p); | |
| msglen += sdm_aggregate_serialize(msg->aggregate, p, &p); | |
| msglen += sdm_set_serialize(msg->src, p, &p); | |
| msglen += sdm_set_serialize(msg->dest, p, &p); | |
| memcpy(p, msg->payload, msg->payload_len); | |
| msglen += msg->payload_len; | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] Sending len %d\n", sdm_route_get_id(), msglen); | |
| /* | |
| * Add body length to the start of the buffer | |
| */ | |
| for (len = msglen, i = MESSAGE_LENGTH_SIZE - 1; i >= 0; i--) { | |
| buf[i] = len & 0xff; | |
| len = len >> 8; | |
| } | |
| /* | |
| * Send the message to each destination, converting the | |
| * node ID to a proper address before the transmission | |
| */ | |
| for (dest_id = sdm_set_first(route); !sdm_set_done(route); dest_id = sdm_set_next(route)) { | |
| int sockd; | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] Sending to %d\n", sdm_route_get_id(), dest_id); | |
| // Get socket descriptor corresponding to the node where the message will be sent | |
| sockd = sdm_fetch_sockd(dest_id); | |
| if (sockd < 0) { | |
| DEBUG_PRINTS(DEBUG_LEVEL_MESSAGES, "Socket descriptor not found!\n"); | |
| return -1; | |
| } | |
| // Write all message to the socket | |
| if (sdm_tcpip_send(sockd, buf, msglen + MESSAGE_LENGTH_SIZE) < 0) { | |
| DEBUG_PRINTS(DEBUG_LEVEL_MESSAGES, "Error sending message!\n"); | |
| return -1; | |
| } | |
| } | |
| /* | |
| * Free resources. | |
| */ | |
| free(buf); | |
| } | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] About to call send_complete\n", sdm_route_get_id()); | |
| /* | |
| * Notify that the send is complete. | |
| */ | |
| if (msg->send_complete != NULL) { | |
| msg->send_complete(msg); | |
| } | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] Leaving sdm_message_send\n", sdm_route_get_id()); | |
| return 0; | |
| } | |
| /** | |
| * Message progress. Caller is responsible for freeing allocated message resources. | |
| * | |
| * @return 0 on success, -1 on failure | |
| */ | |
| int | |
| sdm_message_progress(void) | |
| { | |
| int n; | |
| int err; | |
| int len; | |
| unsigned char * buf; | |
| // Retrieve the active socket descriptor | |
| int sockfd = sdm_get_active_sock_desc(); | |
| if (sockfd == -1) { | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] Error retrieving socket descriptor\n", sdm_route_get_id()); | |
| return -1; | |
| } | |
| if (sockfd >= 0) { | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] receiving header\n", sdm_route_get_id()); | |
| err = sdm_tcpip_msgheader_receive(sockfd, &len); | |
| if (err != 0) { | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] Error retrieving message size!\n", sdm_route_get_id()); | |
| return -1; | |
| } | |
| buf = (unsigned char *)malloc(len); | |
| sdm_message msg = sdm_message_new(buf, len); | |
| if (sdm_tcpip_msgbody_receive(sockfd, buf, len) < 0) { | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] Error retrieving message!\n", sdm_route_get_id()); | |
| return -1; | |
| } | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] sdm_message_progress received len %d from %d\n", | |
| sdm_route_get_id(), len, sdm_fetch_nodeid(sockfd)); | |
| n = varint_decode((int *)&msg->id, buf, &buf); | |
| len -= n; | |
| if ((n = sdm_aggregate_deserialize(msg->aggregate, buf, &buf)) < 0) { | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] invalid header\n", sdm_route_get_id()); | |
| return -1; | |
| } | |
| len -= n; | |
| if ((n = sdm_set_deserialize(msg->src, buf, &buf)) < 0) { | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] invalid header\n", sdm_route_get_id()); | |
| return -1; | |
| } | |
| len -= n; | |
| if ((n = sdm_set_deserialize(msg->dest, buf, &buf)) < 0) { | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] invalid header\n", sdm_route_get_id()); | |
| return -1; | |
| } | |
| len -= n; | |
| msg->payload = buf; | |
| msg->payload_len = len; | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] sdm_message_progress agg=%s src=%s dest=%s\n", | |
| sdm_route_get_id(), | |
| _aggregate_to_str(msg->aggregate), | |
| _set_to_str(msg->src), | |
| _set_to_str(msg->dest)); | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] About to call recv_callback\n", sdm_route_get_id()); | |
| if (sdm_recv_callback != NULL) { | |
| sdm_recv_callback(msg); | |
| } | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] Finished recv_callback\n", sdm_route_get_id()); | |
| } | |
| return 0; | |
| } | |
| void | |
| sdm_message_set_send_callback(sdm_message msg, void (*callback)(sdm_message msg)) | |
| { | |
| msg->send_complete = callback; | |
| } | |
| void | |
| sdm_message_set_recv_callback(void (*callback)(sdm_message msg)) | |
| { | |
| sdm_recv_callback = callback; | |
| } | |
| sdm_message | |
| sdm_message_new(unsigned char *buf, int len) | |
| { | |
| static unsigned int ids = 0; | |
| sdm_message msg = (sdm_message)malloc(sizeof(struct sdm_message)); | |
| msg->id = ids++; | |
| msg->dest = sdm_set_new(); | |
| msg->src = sdm_set_new(); | |
| sdm_set_add_element(msg->src, sdm_route_get_id()); | |
| msg->buf = buf; | |
| msg->buf_len = len; | |
| msg->payload = msg->buf; | |
| msg->payload_len = msg->buf_len; | |
| msg->aggregate = sdm_aggregate_new(); | |
| msg->send_complete = NULL; | |
| return msg; | |
| } | |
| void | |
| sdm_message_free(sdm_message msg) | |
| { | |
| free(msg->buf); | |
| sdm_set_free(msg->src); | |
| sdm_set_free(msg->dest); | |
| sdm_aggregate_free(msg->aggregate); | |
| free(msg); | |
| } | |
| unsigned int | |
| sdm_message_get_id(const sdm_message msg) | |
| { | |
| return msg->id; | |
| } | |
| void | |
| sdm_message_set_id(const sdm_message msg, unsigned int id) | |
| { | |
| msg->id = id; | |
| } | |
| void | |
| sdm_message_set_destination(const sdm_message msg, const sdm_idset dest_ids) | |
| { | |
| sdm_set_union(msg->dest, dest_ids); | |
| } | |
| sdm_idset | |
| sdm_message_get_destination(const sdm_message msg) | |
| { | |
| return msg->dest; | |
| } | |
| void | |
| sdm_message_set_source(const sdm_message msg, const sdm_idset source) | |
| { | |
| sdm_set_union(msg->src, source); | |
| } | |
| sdm_idset | |
| sdm_message_get_source(const sdm_message msg) | |
| { | |
| return msg->src; | |
| } | |
| void | |
| sdm_message_get_payload(const sdm_message msg, unsigned char **buf, int *len) | |
| { | |
| *buf = msg->payload; | |
| *len = msg->payload_len; | |
| } | |
| sdm_aggregate | |
| sdm_message_get_aggregate(const sdm_message msg) | |
| { | |
| return msg->aggregate; | |
| } | |
| void | |
| sdm_message_set_aggregate(const sdm_message msg, const sdm_aggregate a) | |
| { | |
| sdm_aggregate_copy(msg->aggregate, a); | |
| } | |
| void | |
| sdm_message_set_deliver_callback(void (*callback)(const sdm_message msg)) | |
| { | |
| deliver_callback = callback; | |
| } | |
| void | |
| sdm_message_deliver(const sdm_message msg) | |
| { | |
| if (deliver_callback != NULL) { | |
| DEBUG_PRINTF(DEBUG_LEVEL_MESSAGES, "[%d] sdm_message_deliver \n", sdm_route_get_id()); | |
| deliver_callback(msg); | |
| } | |
| } |