XferModule.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  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 int lastTimeTick = 0;
  50. private int lastFilesExpire = 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 int timeStampMS;
  64. }
  65. #region INonSharedRegionModule Members
  66. public void Initialise(IConfigSource config)
  67. {
  68. lastTimeTick = (int)Util.GetTimeStampMS() + 30000;
  69. lastFilesExpire = lastTimeTick + 180000;
  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. int now = (int)Util.GetTimeStampMS();
  108. if(now - lastTimeTick > 750)
  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. int now = (int)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(int 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)
  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)
  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(int 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.Task) >> 10;
  247. burstSize *= remoteClient.PingTimeMS;
  248. burstSize >>= 10; // ping is ms, 1 round trip
  249. if(burstSize > 32)
  250. burstSize = 32;
  251. XferDownLoad transaction =
  252. new XferDownLoad(fileName, fileData, xferID, remoteClient, burstSize);
  253. Transfers.Add(xferID, transaction);
  254. transaction.StartSend();
  255. // The transaction for this file is on its way
  256. RemoveOrDecrementFile(fileName);
  257. }
  258. }
  259. }
  260. else
  261. m_log.WarnFormat("[Xfer]: {0} not found", fileName);
  262. }
  263. }
  264. public void AckPacket(IClientAPI remoteClient, ulong xferID, uint packet)
  265. {
  266. lock (Transfers)
  267. {
  268. if (Transfers.ContainsKey(xferID))
  269. {
  270. if (Transfers[xferID].AckPacket(packet))
  271. Transfers.Remove(xferID);
  272. }
  273. }
  274. }
  275. public void AbortXfer(IClientAPI remoteClient, ulong xferID)
  276. {
  277. lock (Transfers)
  278. {
  279. if (Transfers.ContainsKey(xferID))
  280. {
  281. Transfers[xferID].done();
  282. Transfers.Remove(xferID);
  283. }
  284. }
  285. }
  286. #region Nested type: XferDownLoad
  287. public class XferDownLoad
  288. {
  289. public IClientAPI remoteClient;
  290. public byte[] Data = new byte[0];
  291. public string FileName = String.Empty;
  292. public ulong XferID = 0;
  293. public bool isDeleted = false;
  294. private object myLock = new object();
  295. private int lastACKTimeMS;
  296. private int LastPacket;
  297. private int lastBytes;
  298. private int lastSentPacket;
  299. private int lastAckPacket;
  300. private int burstSize; // additional packets, so can be zero
  301. private int retries;
  302. public XferDownLoad(string fileName, byte[] data, ulong xferID, IClientAPI client, int burstsz)
  303. {
  304. FileName = fileName;
  305. Data = data;
  306. XferID = xferID;
  307. remoteClient = client;
  308. burstSize = burstsz;
  309. }
  310. public XferDownLoad()
  311. {
  312. }
  313. public void done()
  314. {
  315. if(!isDeleted)
  316. {
  317. Data = null;
  318. isDeleted = true;
  319. }
  320. }
  321. /// <summary>
  322. /// Start a transfer
  323. /// </summary>
  324. /// <returns>True if the transfer is complete, false if not</returns>
  325. public void StartSend()
  326. {
  327. lock(myLock)
  328. {
  329. if(Data.Length == 0) //??
  330. {
  331. LastPacket = 0;
  332. lastBytes = 0;
  333. burstSize = 0;
  334. }
  335. else
  336. {
  337. // payload of 1024bytes
  338. LastPacket = Data.Length >> 10;
  339. lastBytes = Data.Length & 0x3ff;
  340. if(lastBytes == 0)
  341. {
  342. lastBytes = 1024;
  343. LastPacket--;
  344. }
  345. }
  346. lastAckPacket = -1;
  347. lastSentPacket = -1;
  348. retries = 0;
  349. SendBurst();
  350. return;
  351. }
  352. }
  353. private void SendBurst()
  354. {
  355. int start = lastAckPacket + 1;
  356. int end = start + burstSize;
  357. if (end > LastPacket)
  358. end = LastPacket;
  359. while (start <= end)
  360. SendPacket(start++);
  361. lastACKTimeMS = (int)Util.GetTimeStampMS() + 1000; // reset timeout with some slack for queues delays
  362. }
  363. private void SendPacket(int pkt)
  364. {
  365. if(pkt > LastPacket)
  366. return;
  367. int pktsize;
  368. uint pktid;
  369. if (pkt == LastPacket)
  370. {
  371. pktsize = lastBytes;
  372. pktid = (uint)pkt | 0x80000000u;
  373. }
  374. else
  375. {
  376. pktsize = 1024;
  377. pktid = (uint)pkt;
  378. }
  379. remoteClient.SendXferPacket(XferID, pktid, Data, pkt << 10, pktsize, true);
  380. lastSentPacket = pkt;
  381. }
  382. /// <summary>
  383. /// Respond to an ack packet from the client
  384. /// </summary>
  385. /// <param name="packet"></param>
  386. /// <returns>True if the transfer is complete, false otherwise</returns>
  387. public bool AckPacket(uint packet)
  388. {
  389. lock(myLock)
  390. {
  391. if(isDeleted)
  392. return true;
  393. packet &= 0x7fffffff;
  394. if (lastAckPacket < packet)
  395. lastAckPacket = (int)packet;
  396. else if (lastAckPacket == LastPacket)
  397. {
  398. done();
  399. return true;
  400. }
  401. lastACKTimeMS = (int)Util.GetTimeStampMS();
  402. retries = 0;
  403. SendPacket(lastSentPacket + 1);
  404. return false;
  405. }
  406. }
  407. public bool checkTime(int now)
  408. {
  409. if (Monitor.TryEnter(myLock))
  410. {
  411. if (!isDeleted)
  412. {
  413. int timeMS = now - lastACKTimeMS;
  414. int tout = 5 * remoteClient.PingTimeMS;
  415. if (tout < 1000)
  416. tout = 1000;
  417. else if(tout > 10000)
  418. tout = 10000;
  419. if (timeMS > tout)
  420. {
  421. if (++retries > 4)
  422. done();
  423. else
  424. {
  425. burstSize = lastSentPacket - lastAckPacket;
  426. SendBurst();
  427. }
  428. }
  429. }
  430. bool isdel = isDeleted;
  431. Monitor.Exit(myLock);
  432. return isdel;
  433. }
  434. return false;
  435. }
  436. }
  437. #endregion
  438. }
  439. }