/*! Unregisters an image from the specified team. */ status_t unregister_image(Team *team, image_id id) { status_t status = B_ENTRY_NOT_FOUND; mutex_lock(&sImageMutex); struct image *image = sImageTable->Lookup(id); if (image != NULL && image->team == team->id) { list_remove_link(image); sImageTable->Remove(image); status = B_OK; } mutex_unlock(&sImageMutex); if (status == B_OK) { // notify the debugger user_debug_image_deleted(&image->info.basic_info); // notify listeners sNotificationService.Notify(IMAGE_REMOVED, image); free(image); } return status; }
status_t set_sem_owner(sem_id id, team_id newTeamID) { if (sSemsActive == false) return B_NO_MORE_SEMS; if (id < 0) return B_BAD_SEM_ID; if (newTeamID < 0) return B_BAD_TEAM_ID; int32 slot = id % sMaxSems; // get the new team Team* newTeam = Team::Get(newTeamID); if (newTeam == NULL) return B_BAD_TEAM_ID; BReference<Team> newTeamReference(newTeam, true); InterruptsSpinLocker semListLocker(sSemsSpinlock); SpinLocker semLocker(sSems[slot].lock); if (sSems[slot].id != id) { TRACE(("set_sem_owner: invalid sem_id %ld\n", id)); return B_BAD_SEM_ID; } list_remove_link(&sSems[slot].u.used.team_link); list_add_item(&newTeam->sem_list, &sSems[slot].u.used.team_link); sSems[slot].u.used.owner = newTeam->id; return B_OK; }
status_t set_port_owner(port_id id, team_id newTeamID) { TRACE(("set_port_owner(id = %ld, team = %ld)\n", id, newTeamID)); if (id < 0) return B_BAD_PORT_ID; int32 slot = id % sMaxPorts; MutexLocker locker(sPorts[slot].lock); if (sPorts[slot].id != id) { TRACE(("set_port_owner: invalid port_id %ld\n", id)); return B_BAD_PORT_ID; } InterruptsSpinLocker teamLocker(gTeamSpinlock); struct team* team = team_get_team_struct_locked(newTeamID); if (team == NULL) { T(OwnerChange(sPorts[slot], newTeamID, B_BAD_TEAM_ID)); return B_BAD_TEAM_ID; } // transfer ownership to other team list_remove_link(&sPorts[slot].team_link); list_add_item(&team->port_list, &sPorts[slot].team_link); sPorts[slot].owner = newTeamID; T(OwnerChange(sPorts[slot], newTeamID, B_OK)); return B_OK; }
static status_t delete_sem_internal(sem_id id, bool checkPermission) { if (sSemsActive == false) return B_NO_MORE_SEMS; if (id < 0) return B_BAD_SEM_ID; int32 slot = id % sMaxSems; cpu_status state = disable_interrupts(); GRAB_SEM_LIST_LOCK(); GRAB_SEM_LOCK(sSems[slot]); if (sSems[slot].id != id) { RELEASE_SEM_LOCK(sSems[slot]); RELEASE_SEM_LIST_LOCK(); restore_interrupts(state); TRACE(("delete_sem: invalid sem_id %ld\n", id)); return B_BAD_SEM_ID; } if (checkPermission && sSems[slot].u.used.owner == team_get_kernel_team_id()) { RELEASE_SEM_LOCK(sSems[slot]); RELEASE_SEM_LIST_LOCK(); restore_interrupts(state); dprintf("thread %ld tried to delete kernel semaphore %ld.\n", thread_get_current_thread_id(), id); return B_NOT_ALLOWED; } if (sSems[slot].u.used.owner >= 0) { list_remove_link(&sSems[slot].u.used.team_link); sSems[slot].u.used.owner = -1; } else panic("sem %ld has no owner", id); RELEASE_SEM_LIST_LOCK(); char* name; uninit_sem_locked(sSems[slot], &name); SpinLocker schedulerLocker(gSchedulerLock); scheduler_reschedule_if_necessary_locked(); schedulerLocker.Unlock(); restore_interrupts(state); free(name); return B_OK; }
/* Remove connection from current position in connection list and move it to * new correct position according to conn_cmp_func. O(2n). */ static void conn_reinsert (ASDownload *dl, ASDownConn *conn) { List *link; /* Find link. */ link = list_find (dl->conns, conn); assert (link); /* Remove entry. */ dl->conns = list_remove_link (dl->conns, link); /* Reinsert at correct pos. */ dl->conns = list_insert_sorted (dl->conns, (CompareFunc) conn_cmp_func, conn); #ifdef VERIFY_CONN_LIST verify_connections (dl); #endif }
status_t delete_port(port_id id) { TRACE(("delete_port(id = %ld)\n", id)); if (!sPortsActive || id < 0) return B_BAD_PORT_ID; int32 slot = id % sMaxPorts; MutexLocker locker(sPorts[slot].lock); if (sPorts[slot].id != id) { TRACE(("delete_port: invalid port_id %ld\n", id)); return B_BAD_PORT_ID; } T(Delete(sPorts[slot])); { InterruptsSpinLocker teamLocker(gTeamSpinlock); list_remove_link(&sPorts[slot].team_link); } uninit_port_locked(sPorts[slot]); locker.Unlock(); MutexLocker _(sPortsLock); // update the first free slot hint in the array if (slot < sFirstFreeSlot) sFirstFreeSlot = slot; sUsedPorts--; return B_OK; }
static int fst_plugin_session_callback (FSTSession *session, FSTSessionMsg msg_type, FSTPacket *msg_data) { switch (msg_type) { /* session management messages */ case SessMsgConnected: { /* determine local ip */ FST_PLUGIN->local_ip = net_local_ip (session->tcpcon->fd, NULL); FST_DBG_3 ("CONNECTED to %s:%d, local ip: %s", session->node->host, session->node->port, net_ip_str (FST_PLUGIN->local_ip)); break; } case SessMsgEstablished: { FST_PLUGIN->stats->sessions++; FST_DBG_3 ("ESTABLISHED session to %s:%d (total sessions: %d)", session->node->host, session->node->port, FST_PLUGIN->stats->sessions); break; } case SessMsgDisconnected: { List *item; /* zero stats */ if (session->was_established) { assert(FST_PLUGIN->stats->sessions > 0); FST_PLUGIN->stats->sessions--; FST_DBG_3 ("DISCONNECTED session to %s:%d (total sessions: %d)", session->node->host, session->node->port, FST_PLUGIN->stats->sessions); if (FST_PLUGIN->stats->sessions == 0) { FST_PLUGIN->stats->users = 0; FST_PLUGIN->stats->files = 0; FST_PLUGIN->stats->size = 0; } /* Terminate all queries sent to this session */ fst_searchlist_session_disconnected (FST_PLUGIN->searches, session); } /* close old session */ if (FST_PLUGIN->session == session) { FST_PLUGIN->session = NULL; } else if ((item = list_find (FST_PLUGIN->sessions, session))) { FST_PLUGIN->sessions = list_remove_link (FST_PLUGIN->sessions, item); } else { /* We have no record of this session yet it was disconnected. This * is not good! */ assert (0); } /* remove old node from node cache */ assert (session->node); if (session->node) { fst_nodecache_remove (FST_PLUGIN->nodecache, session->node); } /* free session */ fst_session_free (session); fst_plugin_connect_next (); return FALSE; } /* FastTrack messages */ case SessMsgNodeList: { int added_nodes = 0; time_t now = time (NULL); while (fst_packet_remaining (msg_data) >= 8) { unsigned long ip = fst_packet_get_uint32 (msg_data); unsigned short port = ntohs (fst_packet_get_uint16 (msg_data)); unsigned int last_seen = fst_packet_get_uint8 (msg_data); unsigned int load = fst_packet_get_uint8 (msg_data); FSTNode *node; #if 0 FST_DBG_4 ("node: %s:%d load: %d%% last_seen: %d mins ago", net_ip_str(ip), port, load, last_seen); #else #ifdef DUMP_NODES fprintf (stderr, "%08x %08x:%d %08x:%d %d %d\n", now, session->tcpcon->host, session->tcpcon->port, ip, port, load, last_seen); #endif #endif /* Only add routable ips to cache */ if (fst_utils_ip_routable ((in_addr_t)ip)) { node = fst_nodecache_add (FST_PLUGIN->nodecache, NodeKlassSuper, net_ip_str (ip), port, load, now - last_seen * 60); if (node && last_seen == 0) fst_peer_insert (FST_PLUGIN->peers, session->node, &session->peers, node); added_nodes++; } } #ifdef DUMP_NODES fprintf (stderr, "\n"); #endif /* sort the cache again */ fst_nodecache_sort (FST_PLUGIN->nodecache); FST_DBG_1 ("added %d received supernode IPs to nodes list", added_nodes); /* save some new nodes for next time (but not too often) */ if (FST_PLUGIN->session == session) save_nodes (); /* now that we have some more nodes, try to continue connecting */ fst_plugin_connect_next (); /* if we got this from an index node disconnect now and use a supernode */ if (session->node->klass == NodeKlassIndex) { FST_DBG ("disconnecting from index node"); /* this calls us back with SessMsgDisconnected */ fst_session_disconnect (session); return FALSE; } break; } case SessMsgNetworkStats: { unsigned int mantissa, exponent; unsigned int prev_users = FST_PLUGIN->stats->users; /* get stats only from primary supernode */ if (session != FST_PLUGIN->session) break; if (fst_packet_remaining (msg_data) < 12) break; FST_PLUGIN->stats->users = ntohl (fst_packet_get_uint32 (msg_data)); FST_PLUGIN->stats->files = ntohl (fst_packet_get_uint32 (msg_data)); mantissa = ntohs(fst_packet_get_uint16 (msg_data)); /* mantissa of size */ exponent = ntohs(fst_packet_get_uint16 (msg_data)); /* exponent of size */ if (exponent >= 30) FST_PLUGIN->stats->size = mantissa << (exponent - 30); else FST_PLUGIN->stats->size = mantissa >> (30 - exponent); /* what follows in the packet is the number of files and their size * per media type (6 times). * Then optionally the different network names and the number of * their users. * we do not currently care for those */ FST_DBG_3 ("received network stats: %d users, %d files, %d GB", FST_PLUGIN->stats->users, FST_PLUGIN->stats->files, FST_PLUGIN->stats->size); /* if we connected to a splitted network segment move on */ if (FST_PLUGIN->stats->users < FST_MIN_USERS_ON_CONNECT && prev_users == 0) { FST_DBG ("disconnecting from splitted network segment"); /* this calls us back with SessMsgDisconnected */ fst_session_disconnect (session); return FALSE; } #if 0 fst_udp_discover_ping_node (FST_PLUGIN->discover, session->node); #endif break; } case SessMsgNetworkName: { FSTPacket *packet; char *net_name = STRDUP_N (msg_data->data, fst_packet_size(msg_data)); FST_DBG_2 ("received network name: \"%s\", sending ours: \"%s\"", net_name ,FST_NETWORK_NAME); free (net_name); if (! (packet = fst_packet_create ())) break; /* not overly important, just don't send it */ fst_packet_put_ustr (packet, FST_NETWORK_NAME, strlen (FST_NETWORK_NAME)); if (!fst_session_send_message (session, SessMsgNetworkName, packet)) { fst_packet_free (packet); fst_session_disconnect (session); return FALSE; } fst_packet_free (packet); break; } case SessMsgExternalIp: { FST_PLUGIN->external_ip = fst_packet_get_uint32 (msg_data); FST_DBG_1 ("received external ip: %s", net_ip_str (FST_PLUGIN->external_ip)); /* resend our info with new external ip */ fst_session_send_info (session); /* upload our shares to supernode. * we do this here because we have to make sure we are accessible * from the outside since we don't push yet. */ if (fst_share_do_share (session)) { FST_DBG_1 ("registering shares with new supernode %s", session->node->host); if (!fst_share_register_all (session)) FST_DBG ("registering shares with new supernode failed"); } /* resend queries for all running searches */ fst_searchlist_send_queries (FST_PLUGIN->searches, session); break; } case SessMsgProtocolVersion: { /* Note: We are not really sure if this is the protocol version. */ FSTPacket *packet; fst_uint32 version; if ((packet = fst_packet_create ())) { version = ntohl (fst_packet_get_uint32 (msg_data)); FST_HEAVY_DBG_1 ("received protocol version: 0x%02X", version); fst_packet_put_uint32 (packet, htonl (version)); fst_session_send_message (session, SessMsgProtocolVersion, packet); fst_packet_free (packet); } break; } case SessMsgQueryReply: { /* forward results from all sessions */ fst_searchlist_process_reply (FST_PLUGIN->searches, session, msg_type, msg_data); break; } case SessMsgQueryEnd: { fst_searchlist_process_reply (FST_PLUGIN->searches, session, msg_type, msg_data); break; } default: /* FST_DBG_2 ("unhandled message: type = 0x%02x, length = %d", msg_type, fst_packet_size(msg_data)); printf("\nunhandled message: type = 0x%02x, length = %d", msg_type, fst_packet_size(msg_data)); print_bin_data(msg_data->data, fst_packet_remaining(msg_data)); */ break; } return TRUE; }
/* take necessary steps to maintain man->connections sessions */ static as_bool sessman_maintain (ASSessMan *man) { ASSession *session; unsigned int connected = list_length (man->connected); unsigned int connecting = list_length (man->connecting); int len; if (man->connections == 0) { /* disconnect everything */ man->connecting = list_foreach_remove (man->connecting, (ListForeachFunc)sessman_disconnect_itr, man); man->connected = list_foreach_remove (man->connected, (ListForeachFunc)sessman_disconnect_itr, man); } else if (man->connections <= connected) { /* We have more connections than needed. First stop all discovery. */ man->connecting = list_foreach_remove (man->connecting, (ListForeachFunc)sessman_disconnect_itr, man); /* Now remove superfluous connections. * TODO: Be smart about which connections we remove. */ len = connected - man->connections; while (len > 0) { session = (ASSession *) man->connected->data; as_session_disconnect (session, FALSE); /* notify node manager */ as_nodeman_update_disconnected (AS->nodeman, session->host); as_session_free (session); man->connected = list_remove_link (man->connected, man->connected); len--; } } else if (man->connections > connected) { /* We need more connections. Fill up discovery queue. */ len = AS_SESSION_PARALLEL_ATTEMPTS - connecting; while (len > 0) { ASSession *session; ASNode *node; /* Get next node */ if (!(node = as_nodeman_next (AS->nodeman))) { /* FIXME: Use Ares http cache by adding download code to * node manager and calling it from here. */ /* only warn if this is likely to be a * problem, to avoid confusion */ if (!man->connected) AS_ERR ("Ran out of nodes"); return FALSE; } /* Create session */ if (!(session = as_session_create (session_state_cb, session_packet_cb))) { AS_ERR ("Insufficient memory"); as_nodeman_update_failed (AS->nodeman, node->host); return FALSE; /* hmm */ } session->udata = man; #if 0 AS_HEAVY_DBG_3 ("Trying node %s:%d, weight: %.02f", net_ip_str (node->host), node->port, node->weight); #endif /* Connect to node */ if (!(as_session_connect (session, node->host, node->port))) { as_nodeman_update_failed (AS->nodeman, node->host); as_session_free (session); continue; /* try next node */ } /* Add session to connecting list */ man->connecting = list_prepend (man->connecting, session); len--; } } connected = list_length (man->connected); connecting = list_length (man->connecting); AS_HEAVY_DBG_3 ("session_maintain: requested: %d, connected: %d, connecting: %d", man->connections, connected, connecting); /* Let NetInfo know what's going on */ as_netinfo_handle_connect (AS->netinfo, man->connections, connected); return TRUE; }
/* This mangels chunks as follows: * - Leave active chunks alone * - Split half complete chunks in a complete and an empty chunk * - Merge consecutive complete chunks * - Merge consecutive empty chunks */ static as_bool consolidate_chunks (ASDownload *dl) { List *link, *last_link, *new_link; ASDownChunk *chunk, *last_chunk, *new_chunk; size_t new_start, new_size; link = dl->chunks; last_link = NULL; last_chunk = NULL; while (link) { chunk = link->data; /* Split chunk if it is not active and half complete */ if (!chunk->udata && chunk->received > 0 && chunk->received < chunk->size) { AS_HEAVY_DBG_3 ("Splitting half complete chunk (%u, %u, %u)", chunk->start, chunk->size, chunk->received); /* create the new empty chunk */ new_start = chunk->start + chunk->received; new_size = chunk->size - chunk->received; if (!(new_chunk = as_downchunk_create (new_start, new_size))) { /* This is not recoverable if we rely on chunk downloads always * beginning at chunk->received == 0 so quit here. */ AS_ERR_2 ("Couldn't create chunk (%u,%u)", new_start, new_size); return FALSE; } /* Shorten old chunk. */ chunk->size = chunk->received; /* Insert new chunk after old one. */ new_link = list_prepend (NULL, new_chunk); list_insert_link (link, new_link); assert (chunk->received == chunk->size); assert (new_chunk->received == 0); /* Adjust last_link and link. */ last_link = link; link = new_link; last_chunk = last_link->data; chunk = link->data; } /* Merge last_chunk and chunk if they are either both complete or both * empty. */ if (last_chunk && !chunk->udata && !last_chunk->udata && ((last_chunk->received == last_chunk->size && chunk->received == chunk->size) || (last_chunk->received == 0 && chunk->received == 0))) { AS_HEAVY_DBG_5 ("Merging %s chunks (%u,%u) and (%u,%u)", (chunk->received == 0) ? "empty" : "complete", last_chunk->start, last_chunk->size, chunk->start, chunk->size); /* increase last chunk by size of chunk */ last_chunk->size += chunk->size; last_chunk->received += chunk->received; /* free chunk and remove link */ as_downchunk_free (chunk); last_link = list_remove_link (last_link, link); /* adjust link so loop can continue */ link = last_link; } /* Move on to next chunk */ last_link = link; link = link->next; last_chunk = last_link->data; }; return TRUE; }
/* Assign new connections to unused chunks. Split large chunks if there are * unused connections. Since our connection list is sorted by bandwidth this * will pick fast connections first. */ static as_bool start_chunks (ASDownload *dl) { List *chunk_l; List *conn_l, *tmp_l, *slow_conn_l; ASDownChunk *chunk, *new_chunk; ASDownConn *conn, *slow_conn; time_t now = time (NULL); size_t remaining, new_start, new_size; int active_conns, pending_conns, max_new_conns; unsigned int speed; if (!(conn_l = dl->conns)) return TRUE; /* nothing to do */ /* We do nothing if there are already enough connections. If not we limit * the number of our attempts to (maximum - active) * 2, assuming half of * the connection attempts will fail. * TODO: Is 2 a good factor? Does it reflect the real failure rate? */ active_conns = active_conns_from_list (dl, &pending_conns); if (active_conns >= AS_CONF_INT (AS_DOWNLOAD_MAX_ACTIVE_SOURCES)) return TRUE; max_new_conns = (AS_CONF_INT (AS_DOWNLOAD_MAX_ACTIVE_SOURCES) - active_conns) * 2; if (pending_conns >= max_new_conns) return TRUE; max_new_conns -= pending_conns; AS_HEAVY_DBG_1 ("Attempting a maximum of %d new connections", max_new_conns); /* First assign connections to unused chunks */ for (chunk_l = dl->chunks; chunk_l; chunk_l = chunk_l->next) { chunk = chunk_l->data; /* skip active and complete chunks */ if (chunk->udata || chunk->received == chunk->size) continue; /* find a connection for this unused chunk */ while (conn_l) { conn = conn_l->data; if (!conn->udata2 && (conn->state != DOWNCONN_QUEUED || conn->queue_next_try <= now)) { /* associate chunk with connection */ chunk->udata = conn; conn->udata2 = chunk; /* We always split half complete chunks in consolidate_chunks * before starting them. */ assert (chunk->received == 0); /* use the connection we found with this chunk */ if (!as_downconn_start (conn, dl->hash, chunk->start, chunk->size)) { /* download start failed, remove connection. */ AS_DBG_2 ("Failed to start download from %s:%d, removing source.", net_ip_str (conn->source->host), conn->source->port); as_downconn_free (conn); chunk->udata = NULL; tmp_l = conn_l->next; dl->conns = list_remove_link (dl->conns, conn_l); conn_l = tmp_l; /* try next connection */ continue; } max_new_conns--; AS_HEAVY_DBG_5 ("Started unused chunk (%u,%u) of \"%s\" with %s:%d", chunk->start, chunk->size, dl->filename, net_ip_str (conn->source->host), conn->source->port); /* move on to next chunk */ conn_l = conn_l->next; break; } conn_l = conn_l->next; } if (!conn_l || max_new_conns <= 0) break; /* no more suitable connections */ } /* Now loop through remaining connections and create new chunks for them * by splitting large ones. */ while (conn_l && max_new_conns > 0) { conn = conn_l->data; /* if we see a used connection we can stop since there are no more * unused ones */ if (conn->udata2) break; /* skip queued connections */ if (conn->state == DOWNCONN_QUEUED && conn->queue_next_try > now) { conn_l = conn_l->next; continue; } /* Find chunk with largest remaining size */ remaining = 0; tmp_l = NULL; for (chunk_l = dl->chunks; chunk_l; chunk_l = chunk_l->next) { chunk = chunk_l->data; if (chunk->size - chunk->received > remaining) { remaining = chunk->size - chunk->received; tmp_l = chunk_l; } } chunk_l = tmp_l; if (!chunk_l || remaining <= AS_DOWNLOAD_MIN_CHUNK_SIZE * 2) { /* No more chunks to break up */ break; } /* break up this chunk in middle of remaining size */ chunk = chunk_l->data; new_size = (chunk->size - chunk->received) / 2; new_start = chunk->start + chunk->size - new_size; if (!(new_chunk = as_downchunk_create (new_start, new_size))) { /* Nothing we can do but bail out. Overall state should still be * consistent and other connections and chunks can go on. */ AS_ERR_2 ("Couldn't create chunk (%u,%u)", new_start, new_size); return FALSE; } /* associate new chunk with connection */ new_chunk->udata = conn; conn->udata2 = new_chunk; /* Start new connection */ if (!as_downconn_start (conn, dl->hash, new_chunk->start, new_chunk->size)) { /* download start failed, remove connection. */ AS_WARN_2 ("Failed to start download from %s:%d, removing source.", net_ip_str (conn->source->host), conn->source->port); as_downchunk_free (new_chunk); as_downconn_free (conn); tmp_l = conn_l->next; dl->conns = list_remove_link (dl->conns, conn_l); conn_l = tmp_l; /* We now have a chunk in the list which has no source. Fixing * this here would be complicated so we break and leave it to * a later maintenance timer invokation. */ break; } max_new_conns--; AS_HEAVY_DBG_5 ("Started new chunk (%u,%u) of \"%s\" with %s:%d", new_chunk->start, new_chunk->size, dl->filename, net_ip_str (conn->source->host), conn->source->port); /* Shorten old chunk. */ chunk->size -= new_size; AS_HEAVY_DBG_4 ("Reduced old chunk from (%u,%u) to (%u,%u)", chunk->start, chunk->size + new_size, chunk->start, chunk->size); /* Insert new chunk after old one */ tmp_l = list_prepend (NULL, new_chunk); list_insert_link (chunk_l, tmp_l); /* go on with next connection */ conn_l = conn_l->next; } /* If there are still unused sources left at this point we are in endgame * mode. Replace slow connections with unused faster ones. The * max_new_conns check prevents looping through a lot of unused sources * if we are not yet in endgame mode. */ while (conn_l && max_new_conns > 0) { conn = conn_l->data; /* if we see a used connection we can stop since there are no more * unused ones */ if (conn->udata2) break; /* Skip queued sources. */ if (conn->state == DOWNCONN_QUEUED && conn->queue_next_try > now) { conn_l = conn_l->next; continue; } /* Find slowest connection to replace. Ideally we would start at the * end of the list since that's where the slowest connections are. Use * current b/w of sources instead of historical one so we have a value * other than 0 for first time requests. */ speed = 0xFFFFFFFF; slow_conn_l = NULL; for (tmp_l = conn_l->next; tmp_l; tmp_l = tmp_l->next) { slow_conn = tmp_l->data; if (slow_conn->udata2 && as_downconn_speed (slow_conn) < speed) { speed = as_downconn_speed (slow_conn); slow_conn_l = tmp_l; } } /* Changing the connection must be worth it so only do it if we * can become at least 1 kb/s faster. */ if (!slow_conn_l || speed + 1024 >= as_downconn_speed (conn)) break; slow_conn = slow_conn_l->data; AS_HEAVY_DBG_3 ("Removing slow source %s (%2.2f kb/s) for potentially faster %2.2f kb/s", net_ip_str (slow_conn->source->host), (float)speed / 1024, (float)as_downconn_speed (conn) / 1024); /* cancel slow connection */ as_downconn_cancel (slow_conn); /* disassociate chunk */ chunk = slow_conn->udata2; slow_conn->udata2 = NULL; chunk->udata = NULL; /* Remove old connection, don't need it anymore. This is safe because * the old connection comes after conn_l. */ dl->conns = list_remove_link (dl->conns, slow_conn_l); as_downconn_free (slow_conn); /* Start chunk again with faster connection. This is the only point * where we start a chunk without chunk->received being 0. */ chunk->udata = conn; conn->udata2 = chunk; if (!as_downconn_start (conn, dl->hash, chunk->start + chunk->received, chunk->size - chunk->received)) { /* Download start failed, remove connection. This leaves the chunk * list in a non-consolidated state which is bad form but still * works since consolidate_chunks will be called later before we * do anything else. */ AS_WARN_2 ("Failed to start replacement download from %s:%d, removing source.", net_ip_str (conn->source->host), conn->source->port); /* We removed a slow connection and couldn't replace it. */ max_new_conns++; as_downconn_free (conn); chunk->udata = NULL; tmp_l = conn_l->next; dl->conns = list_remove_link (dl->conns, conn_l); conn_l = tmp_l; /* What we should do now is use the next unused source for the * chunk we just disconnected and couldn't reconnect. Since that * would become pretty ugly to do here and this is an unlikely * error case we just break the loop and let the maintenance timer * handle it later. */ break; } conn_l = conn_l->next; } /* Get entire connection list in order. A bit inefficient. Would be better * to have two connection lists, one unsorted for used and one sorted for * unused connections. */ dl->conns = list_sort (dl->conns, (CompareFunc) conn_cmp_func); return TRUE; }