diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/Agent/Xfer')
-rw-r--r-- | OpenSim/Region/CoreModules/Agent/Xfer/XferModule.cs | 365 |
1 files changed, 269 insertions, 96 deletions
diff --git a/OpenSim/Region/CoreModules/Agent/Xfer/XferModule.cs b/OpenSim/Region/CoreModules/Agent/Xfer/XferModule.cs index 4299726..1b6401a 100644 --- a/OpenSim/Region/CoreModules/Agent/Xfer/XferModule.cs +++ b/OpenSim/Region/CoreModules/Agent/Xfer/XferModule.cs | |||
@@ -28,10 +28,12 @@ | |||
28 | using System; | 28 | using System; |
29 | using System.Collections.Generic; | 29 | using System.Collections.Generic; |
30 | using System.Reflection; | 30 | using System.Reflection; |
31 | using System.Threading; | ||
31 | using Nini.Config; | 32 | using Nini.Config; |
32 | using log4net; | 33 | using log4net; |
33 | using OpenMetaverse; | 34 | using OpenMetaverse; |
34 | using OpenSim.Framework; | 35 | using OpenSim.Framework; |
36 | using OpenSim.Framework.Monitoring; | ||
35 | using OpenSim.Region.Framework.Interfaces; | 37 | using OpenSim.Region.Framework.Interfaces; |
36 | using OpenSim.Region.Framework.Scenes; | 38 | using OpenSim.Region.Framework.Scenes; |
37 | 39 | ||
@@ -45,9 +47,13 @@ namespace OpenSim.Region.CoreModules.Agent.Xfer | |||
45 | private Scene m_scene; | 47 | private Scene m_scene; |
46 | private Dictionary<string, FileData> NewFiles = new Dictionary<string, FileData>(); | 48 | private Dictionary<string, FileData> NewFiles = new Dictionary<string, FileData>(); |
47 | private Dictionary<ulong, XferDownLoad> Transfers = new Dictionary<ulong, XferDownLoad>(); | 49 | private Dictionary<ulong, XferDownLoad> Transfers = new Dictionary<ulong, XferDownLoad>(); |
48 | |||
49 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 50 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
50 | 51 | ||
52 | private object timeTickLock = new object(); | ||
53 | private double lastTimeTick = 0.0; | ||
54 | private double lastFilesExpire = 0.0; | ||
55 | private bool inTimeTick = false; | ||
56 | |||
51 | public struct XferRequest | 57 | public struct XferRequest |
52 | { | 58 | { |
53 | public IClientAPI remoteClient; | 59 | public IClientAPI remoteClient; |
@@ -59,26 +65,30 @@ namespace OpenSim.Region.CoreModules.Agent.Xfer | |||
59 | private class FileData | 65 | private class FileData |
60 | { | 66 | { |
61 | public byte[] Data; | 67 | public byte[] Data; |
62 | public int Count; | 68 | public int refsCount; |
69 | public double timeStampMS; | ||
63 | } | 70 | } |
64 | 71 | ||
65 | #region INonSharedRegionModule Members | 72 | #region INonSharedRegionModule Members |
66 | 73 | ||
67 | public void Initialise(IConfigSource config) | 74 | public void Initialise(IConfigSource config) |
68 | { | 75 | { |
76 | lastTimeTick = Util.GetTimeStampMS() + 30000.0; | ||
77 | lastFilesExpire = lastTimeTick + 180000.0; | ||
69 | } | 78 | } |
70 | 79 | ||
71 | public void AddRegion(Scene scene) | 80 | public void AddRegion(Scene scene) |
72 | { | 81 | { |
73 | m_scene = scene; | 82 | m_scene = scene; |
74 | m_scene.EventManager.OnNewClient += NewClient; | ||
75 | |||
76 | m_scene.RegisterModuleInterface<IXfer>(this); | 83 | m_scene.RegisterModuleInterface<IXfer>(this); |
84 | m_scene.EventManager.OnNewClient += NewClient; | ||
85 | m_scene.EventManager.OnRegionHeartbeatEnd += OnTimeTick; | ||
77 | } | 86 | } |
78 | 87 | ||
79 | public void RemoveRegion(Scene scene) | 88 | public void RemoveRegion(Scene scene) |
80 | { | 89 | { |
81 | m_scene.EventManager.OnNewClient -= NewClient; | 90 | m_scene.EventManager.OnNewClient -= NewClient; |
91 | m_scene.EventManager.OnRegionHeartbeatEnd -= OnTimeTick; | ||
82 | 92 | ||
83 | m_scene.UnregisterModuleInterface<IXfer>(this); | 93 | m_scene.UnregisterModuleInterface<IXfer>(this); |
84 | m_scene = null; | 94 | m_scene = null; |
@@ -104,6 +114,41 @@ namespace OpenSim.Region.CoreModules.Agent.Xfer | |||
104 | 114 | ||
105 | #endregion | 115 | #endregion |
106 | 116 | ||
117 | public void OnTimeTick(Scene scene) | ||
118 | { | ||
119 | // we are on a heartbeat thread we there can be several | ||
120 | if(Monitor.TryEnter(timeTickLock)) | ||
121 | { | ||
122 | if(!inTimeTick) | ||
123 | { | ||
124 | double now = Util.GetTimeStampMS(); | ||
125 | if(now - lastTimeTick > 1750.0) | ||
126 | { | ||
127 | |||
128 | if(Transfers.Count == 0 && NewFiles.Count == 0) | ||
129 | lastTimeTick = now; | ||
130 | else | ||
131 | { | ||
132 | inTimeTick = true; | ||
133 | |||
134 | //don't overload busy heartbeat | ||
135 | WorkManager.RunInThreadPool( | ||
136 | delegate | ||
137 | { | ||
138 | transfersTimeTick(now); | ||
139 | expireFiles(now); | ||
140 | |||
141 | lastTimeTick = now; | ||
142 | inTimeTick = false; | ||
143 | }, | ||
144 | null, | ||
145 | "XferTimeTick"); | ||
146 | } | ||
147 | } | ||
148 | } | ||
149 | Monitor.Exit(timeTickLock); | ||
150 | } | ||
151 | } | ||
107 | #region IXfer Members | 152 | #region IXfer Members |
108 | 153 | ||
109 | /// <summary> | 154 | /// <summary> |
@@ -118,24 +163,45 @@ namespace OpenSim.Region.CoreModules.Agent.Xfer | |||
118 | { | 163 | { |
119 | lock (NewFiles) | 164 | lock (NewFiles) |
120 | { | 165 | { |
166 | double now = Util.GetTimeStampMS(); | ||
121 | if (NewFiles.ContainsKey(fileName)) | 167 | if (NewFiles.ContainsKey(fileName)) |
122 | { | 168 | { |
123 | NewFiles[fileName].Count++; | 169 | NewFiles[fileName].refsCount++; |
124 | NewFiles[fileName].Data = data; | 170 | NewFiles[fileName].Data = data; |
171 | NewFiles[fileName].timeStampMS = now; | ||
125 | } | 172 | } |
126 | else | 173 | else |
127 | { | 174 | { |
128 | FileData fd = new FileData(); | 175 | FileData fd = new FileData(); |
129 | fd.Count = 1; | 176 | fd.refsCount = 1; |
130 | fd.Data = data; | 177 | fd.Data = data; |
178 | fd.timeStampMS = now; | ||
131 | NewFiles.Add(fileName, fd); | 179 | NewFiles.Add(fileName, fd); |
132 | } | 180 | } |
133 | } | 181 | } |
134 | |||
135 | return true; | 182 | return true; |
136 | } | 183 | } |
137 | 184 | ||
138 | #endregion | 185 | #endregion |
186 | public void expireFiles(double now) | ||
187 | { | ||
188 | lock (NewFiles) | ||
189 | { | ||
190 | // hopefully we will not have many files so nasty code will do it | ||
191 | if(now - lastFilesExpire > 120000.0) | ||
192 | { | ||
193 | lastFilesExpire = now; | ||
194 | List<string> expires = new List<string>(); | ||
195 | foreach(string fname in NewFiles.Keys) | ||
196 | { | ||
197 | if(NewFiles[fname].refsCount == 0 && now - NewFiles[fname].timeStampMS > 120000.0) | ||
198 | expires.Add(fname); | ||
199 | } | ||
200 | foreach(string fname in expires) | ||
201 | NewFiles.Remove(fname); | ||
202 | } | ||
203 | } | ||
204 | } | ||
139 | 205 | ||
140 | public void NewClient(IClientAPI client) | 206 | public void NewClient(IClientAPI client) |
141 | { | 207 | { |
@@ -144,6 +210,51 @@ namespace OpenSim.Region.CoreModules.Agent.Xfer | |||
144 | client.OnAbortXfer += AbortXfer; | 210 | client.OnAbortXfer += AbortXfer; |
145 | } | 211 | } |
146 | 212 | ||
213 | public void OnClientClosed(IClientAPI client) | ||
214 | { | ||
215 | client.OnRequestXfer -= RequestXfer; | ||
216 | client.OnConfirmXfer -= AckPacket; | ||
217 | client.OnAbortXfer -= AbortXfer; | ||
218 | } | ||
219 | |||
220 | private void RemoveOrDecrementFile(string fileName) | ||
221 | { | ||
222 | // NewFiles must be locked | ||
223 | |||
224 | if (NewFiles.ContainsKey(fileName)) | ||
225 | { | ||
226 | if (NewFiles[fileName].refsCount == 1) | ||
227 | NewFiles.Remove(fileName); | ||
228 | else | ||
229 | NewFiles[fileName].refsCount--; | ||
230 | } | ||
231 | } | ||
232 | |||
233 | public void transfersTimeTick(double now) | ||
234 | { | ||
235 | XferDownLoad[] xfrs; | ||
236 | lock(Transfers) | ||
237 | { | ||
238 | if(Transfers.Count == 0) | ||
239 | return; | ||
240 | |||
241 | xfrs = new XferDownLoad[Transfers.Count]; | ||
242 | Transfers.Values.CopyTo(xfrs,0); | ||
243 | } | ||
244 | foreach(XferDownLoad xfr in xfrs) | ||
245 | { | ||
246 | if(xfr.checkTime(now)) | ||
247 | { | ||
248 | ulong xfrID = xfr.XferID; | ||
249 | lock(Transfers) | ||
250 | { | ||
251 | if(Transfers.ContainsKey(xfrID)) | ||
252 | Transfers.Remove(xfrID); | ||
253 | } | ||
254 | } | ||
255 | } | ||
256 | } | ||
257 | |||
147 | /// <summary> | 258 | /// <summary> |
148 | /// | 259 | /// |
149 | /// </summary> | 260 | /// </summary> |
@@ -156,78 +267,52 @@ namespace OpenSim.Region.CoreModules.Agent.Xfer | |||
156 | { | 267 | { |
157 | if (NewFiles.ContainsKey(fileName)) | 268 | if (NewFiles.ContainsKey(fileName)) |
158 | { | 269 | { |
159 | if (!Transfers.ContainsKey(xferID)) | 270 | lock(Transfers) |
160 | { | 271 | { |
161 | byte[] fileData = NewFiles[fileName].Data; | 272 | if (!Transfers.ContainsKey(xferID)) |
162 | XferDownLoad transaction = new XferDownLoad(fileName, fileData, xferID, remoteClient); | 273 | { |
163 | 274 | byte[] fileData = NewFiles[fileName].Data; | |
164 | Transfers.Add(xferID, transaction); | 275 | int burstSize = remoteClient.GetAgentThrottleSilent((int)ThrottleOutPacketType.Asset) >> 11; |
165 | 276 | if(Transfers.Count > 1) | |
166 | if (transaction.StartSend()) | 277 | burstSize /= Transfers.Count; |
167 | RemoveXferData(xferID); | 278 | XferDownLoad transaction = |
168 | 279 | new XferDownLoad(fileName, fileData, xferID, remoteClient, burstSize); | |
169 | // The transaction for this file is either complete or on its way | 280 | |
170 | RemoveOrDecrement(fileName); | 281 | Transfers.Add(xferID, transaction); |
171 | 282 | ||
283 | transaction.StartSend(); | ||
284 | |||
285 | // The transaction for this file is on its way | ||
286 | RemoveOrDecrementFile(fileName); | ||
287 | } | ||
172 | } | 288 | } |
173 | } | 289 | } |
174 | else | 290 | else |
175 | m_log.WarnFormat("[Xfer]: {0} not found", fileName); | 291 | m_log.WarnFormat("[Xfer]: {0} not found", fileName); |
176 | |||
177 | } | 292 | } |
178 | } | 293 | } |
179 | 294 | ||
180 | public void AckPacket(IClientAPI remoteClient, ulong xferID, uint packet) | 295 | public void AckPacket(IClientAPI remoteClient, ulong xferID, uint packet) |
181 | { | 296 | { |
182 | lock (NewFiles) // This is actually to lock Transfers | 297 | lock (Transfers) |
183 | { | 298 | { |
184 | if (Transfers.ContainsKey(xferID)) | 299 | if (Transfers.ContainsKey(xferID)) |
185 | { | 300 | { |
186 | XferDownLoad dl = Transfers[xferID]; | ||
187 | if (Transfers[xferID].AckPacket(packet)) | 301 | if (Transfers[xferID].AckPacket(packet)) |
188 | { | 302 | Transfers.Remove(xferID); |
189 | RemoveXferData(xferID); | ||
190 | RemoveOrDecrement(dl.FileName); | ||
191 | } | ||
192 | } | 303 | } |
193 | } | 304 | } |
194 | } | 305 | } |
195 | 306 | ||
196 | private void RemoveXferData(ulong xferID) | ||
197 | { | ||
198 | // NewFiles must be locked! | ||
199 | if (Transfers.ContainsKey(xferID)) | ||
200 | { | ||
201 | XferModule.XferDownLoad xferItem = Transfers[xferID]; | ||
202 | //string filename = xferItem.FileName; | ||
203 | Transfers.Remove(xferID); | ||
204 | xferItem.Data = new byte[0]; // Clear the data | ||
205 | xferItem.DataPointer = 0; | ||
206 | |||
207 | } | ||
208 | } | ||
209 | |||
210 | public void AbortXfer(IClientAPI remoteClient, ulong xferID) | 307 | public void AbortXfer(IClientAPI remoteClient, ulong xferID) |
211 | { | 308 | { |
212 | lock (NewFiles) | 309 | lock (Transfers) |
213 | { | 310 | { |
214 | if (Transfers.ContainsKey(xferID)) | 311 | if (Transfers.ContainsKey(xferID)) |
215 | RemoveOrDecrement(Transfers[xferID].FileName); | 312 | { |
216 | 313 | Transfers[xferID].done(); | |
217 | RemoveXferData(xferID); | 314 | Transfers.Remove(xferID); |
218 | } | 315 | } |
219 | } | ||
220 | |||
221 | private void RemoveOrDecrement(string fileName) | ||
222 | { | ||
223 | // NewFiles must be locked | ||
224 | |||
225 | if (NewFiles.ContainsKey(fileName)) | ||
226 | { | ||
227 | if (NewFiles[fileName].Count == 1) | ||
228 | NewFiles.Remove(fileName); | ||
229 | else | ||
230 | NewFiles[fileName].Count--; | ||
231 | } | 316 | } |
232 | } | 317 | } |
233 | 318 | ||
@@ -236,52 +321,124 @@ namespace OpenSim.Region.CoreModules.Agent.Xfer | |||
236 | public class XferDownLoad | 321 | public class XferDownLoad |
237 | { | 322 | { |
238 | public IClientAPI Client; | 323 | public IClientAPI Client; |
239 | private bool complete; | ||
240 | public byte[] Data = new byte[0]; | 324 | public byte[] Data = new byte[0]; |
241 | public int DataPointer = 0; | ||
242 | public string FileName = String.Empty; | 325 | public string FileName = String.Empty; |
243 | public uint Packet = 0; | ||
244 | public uint Serial = 1; | ||
245 | public ulong XferID = 0; | 326 | public ulong XferID = 0; |
246 | 327 | public bool isDeleted = false; | |
247 | public XferDownLoad(string fileName, byte[] data, ulong xferID, IClientAPI client) | 328 | |
329 | private object myLock = new object(); | ||
330 | private double lastsendTimeMS; | ||
331 | private int LastPacket; | ||
332 | private int lastBytes; | ||
333 | private int lastSentPacket; | ||
334 | private int lastAckPacket; | ||
335 | private int burstSize; | ||
336 | private int retries = 0; | ||
337 | |||
338 | public XferDownLoad(string fileName, byte[] data, ulong xferID, IClientAPI client, int burstsz) | ||
248 | { | 339 | { |
249 | FileName = fileName; | 340 | FileName = fileName; |
250 | Data = data; | 341 | Data = data; |
251 | XferID = xferID; | 342 | XferID = xferID; |
252 | Client = client; | 343 | Client = client; |
344 | burstSize = burstsz; | ||
253 | } | 345 | } |
254 | 346 | ||
255 | public XferDownLoad() | 347 | public XferDownLoad() |
256 | { | 348 | { |
257 | } | 349 | } |
258 | 350 | ||
351 | public void done() | ||
352 | { | ||
353 | if(!isDeleted) | ||
354 | { | ||
355 | Data = new byte[0]; | ||
356 | isDeleted = true; | ||
357 | } | ||
358 | } | ||
359 | |||
259 | /// <summary> | 360 | /// <summary> |
260 | /// Start a transfer | 361 | /// Start a transfer |
261 | /// </summary> | 362 | /// </summary> |
262 | /// <returns>True if the transfer is complete, false if not</returns> | 363 | /// <returns>True if the transfer is complete, false if not</returns> |
263 | public bool StartSend() | 364 | public void StartSend() |
264 | { | 365 | { |
265 | if (Data.Length < 1000) | 366 | lock(myLock) |
266 | { | 367 | { |
267 | // for now (testing) we only support files under 1000 bytes | 368 | if(Data.Length == 0) //?? |
268 | byte[] transferData = new byte[Data.Length + 4]; | 369 | { |
269 | Array.Copy(Utils.IntToBytes(Data.Length), 0, transferData, 0, 4); | 370 | LastPacket = 0; |
270 | Array.Copy(Data, 0, transferData, 4, Data.Length); | 371 | lastBytes = 0; |
271 | Client.SendXferPacket(XferID, 0 + 0x80000000, transferData); | 372 | burstSize = 0; |
272 | complete = true; | 373 | } |
374 | else | ||
375 | { | ||
376 | // payload of 1024bytes | ||
377 | LastPacket = Data.Length >> 10; | ||
378 | lastBytes = Data.Length & 0x3ff; | ||
379 | if(lastBytes == 0) | ||
380 | { | ||
381 | lastBytes = 1024; | ||
382 | LastPacket--; | ||
383 | } | ||
384 | |||
385 | } | ||
386 | |||
387 | lastAckPacket = -1; | ||
388 | lastSentPacket = -1; | ||
389 | |||
390 | double now = Util.GetTimeStampMS(); | ||
391 | |||
392 | SendBurst(now); | ||
393 | return; | ||
394 | } | ||
395 | } | ||
396 | |||
397 | private void SendBurst(double now) | ||
398 | { | ||
399 | int start = lastAckPacket + 1; | ||
400 | int end = start + burstSize; | ||
401 | if (end > LastPacket) | ||
402 | end = LastPacket; | ||
403 | while(start <= end) | ||
404 | SendPacket(start++ , now); | ||
405 | } | ||
406 | |||
407 | private void SendPacket(int pkt, double now) | ||
408 | { | ||
409 | if(pkt > LastPacket) | ||
410 | return; | ||
411 | |||
412 | int pktsize; | ||
413 | uint pktid; | ||
414 | if (pkt == LastPacket) | ||
415 | { | ||
416 | pktsize = lastBytes; | ||
417 | pktid = (uint)pkt | 0x80000000u; | ||
273 | } | 418 | } |
274 | else | 419 | else |
275 | { | 420 | { |
276 | byte[] transferData = new byte[1000 + 4]; | 421 | pktsize = 1024; |
422 | pktid = (uint)pkt; | ||
423 | } | ||
424 | |||
425 | byte[] transferData; | ||
426 | if(pkt == 0) | ||
427 | { | ||
428 | transferData = new byte[pktsize + 4]; | ||
277 | Array.Copy(Utils.IntToBytes(Data.Length), 0, transferData, 0, 4); | 429 | Array.Copy(Utils.IntToBytes(Data.Length), 0, transferData, 0, 4); |
278 | Array.Copy(Data, 0, transferData, 4, 1000); | 430 | Array.Copy(Data, 0, transferData, 4, pktsize); |
279 | Client.SendXferPacket(XferID, 0, transferData); | ||
280 | Packet++; | ||
281 | DataPointer = 1000; | ||
282 | } | 431 | } |
432 | else | ||
433 | { | ||
434 | transferData = new byte[pktsize]; | ||
435 | Array.Copy(Data, pkt << 10, transferData, 0, pktsize); | ||
436 | } | ||
437 | |||
438 | Client.SendXferPacket(XferID, pktid, transferData, false); | ||
283 | 439 | ||
284 | return complete; | 440 | lastSentPacket = pkt; |
441 | lastsendTimeMS = now; | ||
285 | } | 442 | } |
286 | 443 | ||
287 | /// <summary> | 444 | /// <summary> |
@@ -291,30 +448,46 @@ namespace OpenSim.Region.CoreModules.Agent.Xfer | |||
291 | /// <returns>True if the transfer is complete, false otherwise</returns> | 448 | /// <returns>True if the transfer is complete, false otherwise</returns> |
292 | public bool AckPacket(uint packet) | 449 | public bool AckPacket(uint packet) |
293 | { | 450 | { |
294 | if (!complete) | 451 | lock(myLock) |
295 | { | 452 | { |
296 | if ((Data.Length - DataPointer) > 1000) | 453 | if(isDeleted) |
454 | return true; | ||
455 | |||
456 | packet &= 0x7fffffff; | ||
457 | if(lastAckPacket < packet) | ||
458 | lastAckPacket = (int)packet; | ||
459 | |||
460 | if(lastAckPacket == LastPacket) | ||
297 | { | 461 | { |
298 | byte[] transferData = new byte[1000]; | 462 | done(); |
299 | Array.Copy(Data, DataPointer, transferData, 0, 1000); | 463 | return true; |
300 | Client.SendXferPacket(XferID, Packet, transferData); | ||
301 | Packet++; | ||
302 | DataPointer += 1000; | ||
303 | } | 464 | } |
304 | else | 465 | double now = Util.GetTimeStampMS(); |
466 | SendPacket(lastSentPacket + 1, now); | ||
467 | return false; | ||
468 | } | ||
469 | } | ||
470 | |||
471 | public bool checkTime(double now) | ||
472 | { | ||
473 | if(Monitor.TryEnter(myLock)) | ||
474 | { | ||
475 | if(!isDeleted) | ||
305 | { | 476 | { |
306 | byte[] transferData = new byte[Data.Length - DataPointer]; | 477 | double timeMS = now - lastsendTimeMS; |
307 | Array.Copy(Data, DataPointer, transferData, 0, Data.Length - DataPointer); | 478 | if(timeMS > 60000.0) |
308 | uint endPacket = Packet |= (uint) 0x80000000; | 479 | done(); |
309 | Client.SendXferPacket(XferID, endPacket, transferData); | 480 | else if(timeMS > 3500.0 && retries++ < 3) |
310 | Packet++; | 481 | { |
311 | DataPointer += (Data.Length - DataPointer); | 482 | burstSize >>= 1; |
312 | 483 | SendBurst(now); | |
313 | complete = true; | 484 | } |
314 | } | 485 | } |
315 | } | ||
316 | 486 | ||
317 | return complete; | 487 | Monitor.Exit(myLock); |
488 | return isDeleted; | ||
489 | } | ||
490 | return false; | ||
318 | } | 491 | } |
319 | } | 492 | } |
320 | 493 | ||