/** * <summary>Begins a new transfer state to the neighbor connected via con. * </summary> * <param name="con">The connection to the neigbhor we will be transferring * data to.</param> * <param name="ts">The table server we're providing the transfer for. C# * does not allow sub-class objects to have access to their parent objects * member variables, so we pass it in like this.</param> * <remarks> * Step 1: * * Get all the keys between me and my new neighbor. * * Step 2: * * Get all values for those keys, we copy so that we don't worry about * changes to the dht during this interaction. This is only a pointer * copy and since we let the OS deal with removing the contents of an * entry, we don't need to make copies of the actual entry. * * Step 3: * * Generate another list of keys of up to max parallel transfers and begin * transferring, that way we do not need to lock access to the entry * enumerator until non-constructor puts. * * Step 4: * * End constructor, results from puts, cause the next entry to be sent. */ public TransferState(Brunet.Connections.Connection con, TableServer ts) { this._ts = ts; this._con = con; // Get all keys between me and my new neighbor LinkedList <MemBlock> keys; lock (_ts._sync) { keys = _ts._data.GetKeysBetween((AHAddress)_ts._node.Address, (AHAddress)_con.Address); } if (Dht.DhtLog.Enabled) { ProtocolLog.Write(Dht.DhtLog, String.Format( "Starting transfer from {0} to {1}", _ts._node.Address, _con.Address)); } int total_entries = 0; /* Get all values for those keys, we copy so that we don't worry about * changes to the dht during this interaction. This is only a pointer * copy and since we let the OS deal with removing the contents of an * entry, we don't need to make copies of the actual entry. */ foreach (MemBlock key in keys) { Entry[] entries; lock (_ts._sync) { LinkedList <Entry> llentries = _ts._data.GetEntries(key); if (llentries == null) { continue; } entries = new Entry[llentries.Count]; total_entries += llentries.Count; llentries.CopyTo(entries, 0); } key_entries.AddLast(entries); } if (Dht.DhtLog.Enabled) { ProtocolLog.Write(Dht.DhtLog, String.Format( "Total keys: {0}, total entries: {1}.", key_entries.Count, total_entries)); } _entry_enumerator = GetEntryEnumerator(); /* Here we generate another list of keys that we would like to * this is done here, so that we can lock up the _entry_enumerator * only during this stage and not during the RpcManager.Invoke */ LinkedList <Entry> local_entries = new LinkedList <Entry>(); for (int i = 0; i < MAX_PARALLEL_TRANSFERS && _entry_enumerator.MoveNext(); i++) { local_entries.AddLast((Entry)_entry_enumerator.Current); } foreach (Entry ent in local_entries) { Channel queue = new Channel(); queue.CloseAfterEnqueue(); queue.CloseEvent += this.NextTransfer; int ttl = (int)(ent.EndTime - DateTime.UtcNow).TotalSeconds; try { _ts._rpc.Invoke(_con.Edge, queue, "dht.PutHandler", ent.Key, ent.Value, ttl, false); } catch { if (_con.Edge.IsClosed) { _interrupted = true; Done(); break; } } } }
/** <summary>Begins a new transfer state to the neighbor connected via con. </summary> <param name="con">The connection to the neigbhor we will be transferring data to.</param> <param name="ts">The table server we're providing the transfer for. C# does not allow sub-class objects to have access to their parent objects member variables, so we pass it in like this.</param> <remarks> Step 1: Get all the keys between me and my new neighbor. Step 2: Get all values for those keys, we copy so that we don't worry about changes to the dht during this interaction. This is only a pointer copy and since we let the OS deal with removing the contents of an entry, we don't need to make copies of the actual entry. Step 3: Generate another list of keys of up to max parallel transfers and begin transferring, that way we do not need to lock access to the entry enumerator until non-constructor puts. Step 4: End constructor, results from puts, cause the next entry to be sent. */ public TransferState(Brunet.Connections.Connection con, TableServer ts) { this._ts = ts; this._con = con; // Get all keys between me and my new neighbor LinkedList<MemBlock> keys; lock(_ts._sync) { keys = _ts._data.GetKeysBetween((AHAddress) _ts._node.Address, (AHAddress) _con.Address); } if(Dht.DhtLog.Enabled) { ProtocolLog.Write(Dht.DhtLog, String.Format( "Starting transfer from {0} to {1}", _ts._node.Address, _con.Address)); } int total_entries = 0; /* Get all values for those keys, we copy so that we don't worry about * changes to the dht during this interaction. This is only a pointer * copy and since we let the OS deal with removing the contents of an * entry, we don't need to make copies of the actual entry. */ foreach(MemBlock key in keys) { Entry[] entries; lock(_ts._sync) { LinkedList<Entry> llentries = _ts._data.GetEntries(key); if(llentries == null) { continue; } entries = new Entry[llentries.Count]; total_entries += llentries.Count; llentries.CopyTo(entries, 0); } key_entries.AddLast(entries); } if(Dht.DhtLog.Enabled) { ProtocolLog.Write(Dht.DhtLog, String.Format( "Total keys: {0}, total entries: {1}.", key_entries.Count, total_entries)); } _entry_enumerator = GetEntryEnumerator(); /* Here we generate another list of keys that we would like to * this is done here, so that we can lock up the _entry_enumerator * only during this stage and not during the RpcManager.Invoke */ LinkedList<Entry> local_entries = new LinkedList<Entry>(); for(int i = 0; i < MAX_PARALLEL_TRANSFERS && _entry_enumerator.MoveNext(); i++) { local_entries.AddLast((Entry) _entry_enumerator.Current); } foreach(Entry ent in local_entries) { Channel queue = new Channel(); queue.CloseAfterEnqueue(); queue.CloseEvent += this.NextTransfer; int ttl = (int) (ent.EndTime - DateTime.UtcNow).TotalSeconds; try { _ts._rpc.Invoke(_con.Edge, queue, "dht.PutHandler", ent.Key, ent.Value, ttl, false); } catch { if(_con.Edge.IsClosed) { _interrupted = true; Done(); break; } } } }