XferModule.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections.Generic;
  29. using System.Reflection;
  30. using System.Threading;
  31. using Nini.Config;
  32. using log4net;
  33. using OpenMetaverse;
  34. using OpenSim.Framework;
  35. using OpenSim.Framework.Monitoring;
  36. using OpenSim.Region.Framework.Interfaces;
  37. using OpenSim.Region.Framework.Scenes;
  38. using Mono.Addins;
  39. namespace OpenSim.Region.CoreModules.Agent.Xfer
  40. {
  41. [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "XferModule")]
  42. public class XferModule : INonSharedRegionModule, IXfer
  43. {
  44. private Scene m_scene;
  45. private Dictionary<string, FileData> NewFiles = new Dictionary<string, FileData>();
  46. private Dictionary<ulong, XferDownLoad> Transfers = new Dictionary<ulong, XferDownLoad>();
  47. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  48. private object timeTickLock = new object();
  49. private double lastTimeTick = 0.0;
  50. private double lastFilesExpire = 0.0;
  51. private bool inTimeTick = false;
  52. public struct XferRequest
  53. {
  54. public IClientAPI remoteClient;
  55. public ulong xferID;
  56. public string fileName;
  57. public DateTime timeStamp;
  58. }
  59. private class FileData
  60. {
  61. public byte[] Data;
  62. public int refsCount;
  63. public double timeStampMS;
  64. }
  65. #region INonSharedRegionModule Members
  66. public void Initialise(IConfigSource config)
  67. {
  68. lastTimeTick = Util.GetTimeStampMS() + 30000.0;
  69. lastFilesExpire = lastTimeTick + 180000.0;
  70. }
  71. public void AddRegion(Scene scene)
  72. {
  73. m_scene = scene;
  74. m_scene.RegisterModuleInterface<IXfer>(this);
  75. m_scene.EventManager.OnNewClient += NewClient;
  76. m_scene.EventManager.OnRegionHeartbeatEnd += OnTimeTick;
  77. }
  78. public void RemoveRegion(Scene scene)
  79. {
  80. m_scene.EventManager.OnNewClient -= NewClient;
  81. m_scene.EventManager.OnRegionHeartbeatEnd -= OnTimeTick;
  82. m_scene.UnregisterModuleInterface<IXfer>(this);
  83. m_scene = null;
  84. }
  85. public void RegionLoaded(Scene scene)
  86. {
  87. }
  88. public Type ReplaceableInterface
  89. {
  90. get { return null; }
  91. }
  92. public void Close()
  93. {
  94. }
  95. public string Name
  96. {
  97. get { return "XferModule"; }
  98. }
  99. #endregion
  100. public void OnTimeTick(Scene scene)
  101. {
  102. // we are on a heartbeat thread we there can be several
  103. if(Monitor.TryEnter(timeTickLock))
  104. {
  105. if(!inTimeTick)
  106. {
  107. double now = Util.GetTimeStampMS();
  108. if(now - lastTimeTick > 1750.0)
  109. {
  110. if(Transfers.Count == 0 && NewFiles.Count == 0)
  111. lastTimeTick = now;
  112. else
  113. {
  114. inTimeTick = true;
  115. //don't overload busy heartbeat
  116. WorkManager.RunInThreadPool(
  117. delegate
  118. {
  119. transfersTimeTick(now);
  120. expireFiles(now);
  121. lastTimeTick = now;
  122. inTimeTick = false;
  123. },
  124. null,
  125. "XferTimeTick");
  126. }
  127. }
  128. }
  129. Monitor.Exit(timeTickLock);
  130. }
  131. }
  132. #region IXfer Members
  133. /// <summary>
  134. /// Let the Xfer module know about a file that the client is about to request.
  135. /// Caller is responsible for making sure that the file is here before
  136. /// the client starts the XferRequest.
  137. /// </summary>
  138. /// <param name="fileName"></param>
  139. /// <param name="data"></param>
  140. /// <returns></returns>
  141. public bool AddNewFile(string fileName, byte[] data)
  142. {
  143. lock (NewFiles)
  144. {
  145. double now = Util.GetTimeStampMS();
  146. if (NewFiles.ContainsKey(fileName))
  147. {
  148. NewFiles[fileName].refsCount++;
  149. NewFiles[fileName].Data = data;
  150. NewFiles[fileName].timeStampMS = now;
  151. }
  152. else
  153. {
  154. FileData fd = new FileData();
  155. fd.refsCount = 1;
  156. fd.Data = data;
  157. fd.timeStampMS = now;
  158. NewFiles.Add(fileName, fd);
  159. }
  160. }
  161. return true;
  162. }
  163. #endregion
  164. public void expireFiles(double now)
  165. {
  166. lock (NewFiles)
  167. {
  168. // hopefully we will not have many files so nasty code will do it
  169. if(now - lastFilesExpire > 120000.0)
  170. {
  171. lastFilesExpire = now;
  172. List<string> expires = new List<string>();
  173. foreach(string fname in NewFiles.Keys)
  174. {
  175. if(NewFiles[fname].refsCount == 0 && now - NewFiles[fname].timeStampMS > 120000.0)
  176. expires.Add(fname);
  177. }
  178. foreach(string fname in expires)
  179. NewFiles.Remove(fname);
  180. }
  181. }
  182. }
  183. public void NewClient(IClientAPI client)
  184. {
  185. client.OnRequestXfer += RequestXfer;
  186. client.OnConfirmXfer += AckPacket;
  187. client.OnAbortXfer += AbortXfer;
  188. }
  189. public void OnClientClosed(IClientAPI client)
  190. {
  191. client.OnRequestXfer -= RequestXfer;
  192. client.OnConfirmXfer -= AckPacket;
  193. client.OnAbortXfer -= AbortXfer;
  194. }
  195. private void RemoveOrDecrementFile(string fileName)
  196. {
  197. // NewFiles must be locked
  198. if (NewFiles.ContainsKey(fileName))
  199. {
  200. if (NewFiles[fileName].refsCount == 1)
  201. NewFiles.Remove(fileName);
  202. else
  203. NewFiles[fileName].refsCount--;
  204. }
  205. }
  206. public void transfersTimeTick(double now)
  207. {
  208. XferDownLoad[] xfrs;
  209. lock(Transfers)
  210. {
  211. if(Transfers.Count == 0)
  212. return;
  213. xfrs = new XferDownLoad[Transfers.Count];
  214. Transfers.Values.CopyTo(xfrs,0);
  215. }
  216. foreach(XferDownLoad xfr in xfrs)
  217. {
  218. if(xfr.checkTime(now))
  219. {
  220. ulong xfrID = xfr.XferID;
  221. lock(Transfers)
  222. {
  223. if(Transfers.ContainsKey(xfrID))
  224. Transfers.Remove(xfrID);
  225. }
  226. }
  227. }
  228. }
  229. /// <summary>
  230. ///
  231. /// </summary>
  232. /// <param name="remoteClient"></param>
  233. /// <param name="xferID"></param>
  234. /// <param name="fileName"></param>
  235. public void RequestXfer(IClientAPI remoteClient, ulong xferID, string fileName)
  236. {
  237. lock (NewFiles)
  238. {
  239. if (NewFiles.ContainsKey(fileName))
  240. {
  241. lock(Transfers)
  242. {
  243. if (!Transfers.ContainsKey(xferID))
  244. {
  245. byte[] fileData = NewFiles[fileName].Data;
  246. int burstSize = remoteClient.GetAgentThrottleSilent((int)ThrottleOutPacketType.Asset) >> 11;
  247. if(Transfers.Count > 1)
  248. burstSize /= Transfers.Count;
  249. XferDownLoad transaction =
  250. new XferDownLoad(fileName, fileData, xferID, remoteClient, burstSize);
  251. Transfers.Add(xferID, transaction);
  252. transaction.StartSend();
  253. // The transaction for this file is on its way
  254. RemoveOrDecrementFile(fileName);
  255. }
  256. }
  257. }
  258. else
  259. m_log.WarnFormat("[Xfer]: {0} not found", fileName);
  260. }
  261. }
  262. public void AckPacket(IClientAPI remoteClient, ulong xferID, uint packet)
  263. {
  264. lock (Transfers)
  265. {
  266. if (Transfers.ContainsKey(xferID))
  267. {
  268. if (Transfers[xferID].AckPacket(packet))
  269. Transfers.Remove(xferID);
  270. }
  271. }
  272. }
  273. public void AbortXfer(IClientAPI remoteClient, ulong xferID)
  274. {
  275. lock (Transfers)
  276. {
  277. if (Transfers.ContainsKey(xferID))
  278. {
  279. Transfers[xferID].done();
  280. Transfers.Remove(xferID);
  281. }
  282. }
  283. }
  284. #region Nested type: XferDownLoad
  285. public class XferDownLoad
  286. {
  287. public IClientAPI Client;
  288. public byte[] Data = new byte[0];
  289. public string FileName = String.Empty;
  290. public ulong XferID = 0;
  291. public bool isDeleted = false;
  292. private object myLock = new object();
  293. private double lastsendTimeMS;
  294. private int LastPacket;
  295. private int lastBytes;
  296. private int lastSentPacket;
  297. private int lastAckPacket;
  298. private int burstSize;
  299. private int retries = 0;
  300. public XferDownLoad(string fileName, byte[] data, ulong xferID, IClientAPI client, int burstsz)
  301. {
  302. FileName = fileName;
  303. Data = data;
  304. XferID = xferID;
  305. Client = client;
  306. burstSize = burstsz;
  307. }
  308. public XferDownLoad()
  309. {
  310. }
  311. public void done()
  312. {
  313. if(!isDeleted)
  314. {
  315. Data = new byte[0];
  316. isDeleted = true;
  317. }
  318. }
  319. /// <summary>
  320. /// Start a transfer
  321. /// </summary>
  322. /// <returns>True if the transfer is complete, false if not</returns>
  323. public void StartSend()
  324. {
  325. lock(myLock)
  326. {
  327. if(Data.Length == 0) //??
  328. {
  329. LastPacket = 0;
  330. lastBytes = 0;
  331. burstSize = 0;
  332. }
  333. else
  334. {
  335. // payload of 1024bytes
  336. LastPacket = Data.Length >> 10;
  337. lastBytes = Data.Length & 0x3ff;
  338. if(lastBytes == 0)
  339. {
  340. lastBytes = 1024;
  341. LastPacket--;
  342. }
  343. }
  344. lastAckPacket = -1;
  345. lastSentPacket = -1;
  346. double now = Util.GetTimeStampMS();
  347. SendBurst(now);
  348. return;
  349. }
  350. }
  351. private void SendBurst(double now)
  352. {
  353. int start = lastAckPacket + 1;
  354. int end = start + burstSize;
  355. if (end > LastPacket)
  356. end = LastPacket;
  357. while(start <= end)
  358. SendPacket(start++ , now);
  359. }
  360. private void SendPacket(int pkt, double now)
  361. {
  362. if(pkt > LastPacket)
  363. return;
  364. int pktsize;
  365. uint pktid;
  366. if (pkt == LastPacket)
  367. {
  368. pktsize = lastBytes;
  369. pktid = (uint)pkt | 0x80000000u;
  370. }
  371. else
  372. {
  373. pktsize = 1024;
  374. pktid = (uint)pkt;
  375. }
  376. byte[] transferData;
  377. if(pkt == 0)
  378. {
  379. transferData = new byte[pktsize + 4];
  380. Array.Copy(Utils.IntToBytes(Data.Length), 0, transferData, 0, 4);
  381. Array.Copy(Data, 0, transferData, 4, pktsize);
  382. }
  383. else
  384. {
  385. transferData = new byte[pktsize];
  386. Array.Copy(Data, pkt << 10, transferData, 0, pktsize);
  387. }
  388. Client.SendXferPacket(XferID, pktid, transferData, false);
  389. lastSentPacket = pkt;
  390. lastsendTimeMS = now;
  391. }
  392. /// <summary>
  393. /// Respond to an ack packet from the client
  394. /// </summary>
  395. /// <param name="packet"></param>
  396. /// <returns>True if the transfer is complete, false otherwise</returns>
  397. public bool AckPacket(uint packet)
  398. {
  399. lock(myLock)
  400. {
  401. if(isDeleted)
  402. return true;
  403. packet &= 0x7fffffff;
  404. if(lastAckPacket < packet)
  405. lastAckPacket = (int)packet;
  406. if(lastAckPacket == LastPacket)
  407. {
  408. done();
  409. return true;
  410. }
  411. double now = Util.GetTimeStampMS();
  412. SendPacket(lastSentPacket + 1, now);
  413. return false;
  414. }
  415. }
  416. public bool checkTime(double now)
  417. {
  418. if(Monitor.TryEnter(myLock))
  419. {
  420. if(!isDeleted)
  421. {
  422. double timeMS = now - lastsendTimeMS;
  423. if(timeMS > 60000.0)
  424. done();
  425. else if(timeMS > 3500.0 && retries++ < 3)
  426. {
  427. burstSize >>= 1;
  428. SendBurst(now);
  429. }
  430. }
  431. Monitor.Exit(myLock);
  432. return isDeleted;
  433. }
  434. return false;
  435. }
  436. }
  437. #endregion
  438. }
  439. }