Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

708 lines
18 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. SESSION.C
  5. Abstract:
  6. Management of the session structures
  7. Author:
  8. Aaron Ogus (aarono)
  9. Environment:
  10. Win32/COM
  11. Revision History:
  12. Date Author Description
  13. ====== ====== ============================================================
  14. 12/10/96 aarono Original
  15. 6/6/98 aarono Turn on throttling and windowing
  16. --*/
  17. #include <windows.h>
  18. #include "newdpf.h"
  19. #include <mmsystem.h>
  20. #include <dplay.h>
  21. #include <dplaysp.h>
  22. #include <dplaypr.h>
  23. #include "mydebug.h"
  24. #include "arpd.h"
  25. #include "arpdint.h"
  26. #include "macros.h"
  27. #include "mytimer.h"
  28. /*=============================================================================
  29. Note on Session Locking and reference counting:
  30. ===============================================
  31. Sessions have a refcount that controls their existence. When the refcount
  32. is non-zero, the session continues to exist. When the refcount hits zero,
  33. the session is destroyed.
  34. On creation, the refcount for a session is set to 1. Sessions are created
  35. when DirectPlay calls ProtocolCreatePlayer. The session state is set to
  36. Running during creation. Each Session created also creates a reference
  37. count on the Protocol object. This is so that the protocol does not
  38. shut down before all Sessions have been closed.
  39. When DirectPlay calls ProtocolDeletePlayer, the session's state is set
  40. to Closing and the reference count is reduced by one. We then must wait
  41. until the session has been destroyed before we return from
  42. ProtocolDeletePlayer, otherwise, the id may be recycled before we have
  43. freed up the slot in the session.
  44. Sends do NOT create references on the Session. When a send thread starts
  45. referencing a Send, it creates a reference on the session the send is
  46. on. When the send thread is no longer referencing the send, it deletes
  47. its' reference on the session. When the reference count on the session
  48. is 0, it is safe to complete all pending sends with an error and free
  49. their resources.
  50. Receives do NOT create references on the Session. When the protocol's
  51. receive handler is called, a reference to the Session is created on
  52. behalf of the receive thread. When the receive thread is done processing,
  53. the reference is removed. This prevents the Session from going away
  54. while a receive thread is processing. When the reference count on the
  55. session is 0, it is safe to throw out all pending receives. Ideally an
  56. ABORT message of some kind should be sent to the transmitter, but this
  57. is optional since it should time-out the transaction.
  58. If the reference count on the session hits 0, the session must be shut
  59. down and freed. All pending sends are completed with an error. All
  60. pending receives are thrown out and cleaned up. All pending stats are
  61. thrown out.
  62. Session States:
  63. ===============
  64. Open - set at creation.
  65. Closing - set when we receive a call to ProtocolDeletePlayer.
  66. No new receives or sends are accepted in closing state.
  67. Closed - Refcount is 0, we are now freeing everything.
  68. -----------------------------------------------------------------------------*/
  69. /*=============================================================================
  70. pPlayerFromId()
  71. Description:
  72. Parameters:
  73. Return Values:
  74. -----------------------------------------------------------------------------*/
  75. LPDPLAYI_PLAYER pPlayerFromId(PPROTOCOL pProtocol, DPID idPlayer)
  76. {
  77. LPDPLAYI_PLAYER pPlayer;
  78. LPDPLAYI_DPLAY lpDPlay;
  79. UINT index;
  80. lpDPlay=pProtocol->m_lpDPlay;
  81. if(idPlayer == DPID_SERVERPLAYER){
  82. pPlayer = lpDPlay->pServerPlayer;
  83. } else {
  84. index = ((idPlayer ^ pProtocol->m_dwIDKey)&INDEX_MASK);
  85. ASSERT(index < 65535);
  86. pPlayer = (LPDPLAYI_PLAYER)(lpDPlay->pNameTable[index].dwItem);
  87. }
  88. return pPlayer;
  89. }
  90. /*=============================================================================
  91. CreateNewSession
  92. Description:
  93. Parameters:
  94. Return Values:
  95. -----------------------------------------------------------------------------*/
  96. HRESULT CreateNewSession(PPROTOCOL pProtocol, DPID idPlayer)
  97. {
  98. DWORD dwUnmangledID;
  99. DWORD iPlayer;
  100. DWORD iSysPlayer;
  101. UINT i;
  102. HRESULT hr=DPERR_NOMEMORY;
  103. PSESSION (*pSessionsNew)[];
  104. PSESSION pSession;
  105. LPDPLAYI_PLAYER pPlayer;
  106. if(idPlayer != DPID_SERVERPLAYER) {
  107. // Convert the ID to an integer
  108. dwUnmangledID = idPlayer ^ pProtocol->m_dwIDKey;
  109. iPlayer = dwUnmangledID & INDEX_MASK;
  110. pPlayer=(LPDPLAYI_PLAYER)(pProtocol->m_lpDPlay->pNameTable[iPlayer].dwItem);
  111. ASSERT(pPlayer);
  112. // Note: System players are always created before non-system players.
  113. dwUnmangledID = pPlayer->dwIDSysPlayer ^ pProtocol->m_dwIDKey;
  114. iSysPlayer = dwUnmangledID & INDEX_MASK;
  115. #ifdef DEBUG
  116. if(iSysPlayer==iPlayer){
  117. DPF(9,"PROTOCOL: CREATING SYSTEM PLAYER\n");
  118. }
  119. #endif
  120. DPF(9,"PROTOCOL: Creating Player id x%x %dd iPlayer %d iSysPlayer %d\n",idPlayer, idPlayer, iPlayer, iSysPlayer);
  121. if(iPlayer >= 0xFFFF){
  122. // use 0xFFFF to map messages starting in 'play' to 'pl'0xFFFF
  123. // so we can't have a real player at this iPlayer.
  124. DPF(0,"PROTOCOL: not allowing creation of player iPlayer %x\n",iPlayer);
  125. goto exit;
  126. }
  127. Lock(&pProtocol->m_SessionLock);
  128. //
  129. // Adjust session list size (if necessary).
  130. //
  131. if(pProtocol->m_SessionListSize <= iPlayer){
  132. // not long enough, reallocate - go over 16 to avoid thrashing on every one.
  133. pSessionsNew=My_GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT,(iPlayer+16)*sizeof(PSESSION));
  134. if(pSessionsNew){
  135. // Copy the old entries to the new list.
  136. if(pProtocol->m_pSessions){
  137. for(i=0;i<pProtocol->m_SessionListSize;i++){
  138. (*pSessionsNew)[i]=(*pProtocol->m_pSessions)[i];
  139. }
  140. // Free the old list.
  141. My_GlobalFree(pProtocol->m_pSessions);
  142. }
  143. // Put the new list in its place.
  144. pProtocol->m_pSessions=pSessionsNew;
  145. pProtocol->m_SessionListSize=iPlayer+16;
  146. DPF(9,"PROTOCOL: Grew sessionlist to %d entries\n",pProtocol->m_SessionListSize);
  147. }
  148. }
  149. // Allocate a session
  150. pSession=(PSESSION)My_GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT, sizeof(SESSION));
  151. if(!pSession){
  152. goto exit2;
  153. }
  154. (*pProtocol->m_pSessions)[iPlayer]=pSession;
  155. } else {
  156. // SERVERPLAYER
  157. pPlayer = pPlayerFromId(pProtocol,idPlayer);
  158. iPlayer = SERVERPLAYER_INDEX;
  159. Lock(&pProtocol->m_SessionLock);
  160. pSession=(PSESSION)My_GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT, sizeof(SESSION));
  161. if(!pSession){
  162. goto exit2;
  163. }
  164. ASSERT(!pProtocol->m_pServerPlayerSession);
  165. pProtocol->m_pServerPlayerSession=pSession;
  166. ASSERT(pPlayer->dwIDSysPlayer != DPID_SERVERPLAYER);
  167. // Note: System players are always created before non-system players.
  168. dwUnmangledID = pPlayer->dwIDSysPlayer ^ pProtocol->m_dwIDKey;
  169. iSysPlayer = dwUnmangledID & INDEX_MASK;
  170. DPF(8,"PROTOCOL:CreatePlayer: Creating SERVERPLAYER: pPlayer %x iPlayer %x iSysPlayer %x\n",pPlayer,iPlayer,iSysPlayer);
  171. }
  172. pProtocol->m_nSessions++;
  173. //
  174. // Session structure initialization
  175. //
  176. SET_SIGN(pSession, SESSION_SIGN);
  177. pSession->pProtocol=pProtocol;
  178. InitializeCriticalSection(&pSession->SessionLock);
  179. pSession->RefCount=1; // One Ref for creation.
  180. pSession->eState=Open;
  181. // Initialize the SendQ
  182. InitBilink(&pSession->SendQ);
  183. pSession->dpid=idPlayer;
  184. pSession->iSession=iPlayer;
  185. pSession->iSysPlayer=iSysPlayer;
  186. InitializeCriticalSection(&pSession->SessionStatLock);
  187. InitBilink(&pSession->DGStatList);
  188. pSession->DGFirstMsg = 0;
  189. pSession->DGLastMsg = 0;
  190. pSession->DGOutMsgMask = 0;
  191. pSession->nWaitingForDGMessageid=0;
  192. pSession->FirstMsg = 0;
  193. pSession->LastMsg = 0;
  194. pSession->OutMsgMask = 0;
  195. pSession->nWaitingForMessageid=0;
  196. // we start the connection using small headers, if we see we can
  197. // get better perf with a large header during operation, then we
  198. // switch to the large header.
  199. pSession->MaxCSends = 1;/*MAX_SMALL_CSENDS - initial values adjusted after 1st ACK*/
  200. pSession->MaxCDGSends = 1;/*MAX_SMALL_DG_CSENDS*/
  201. pSession->WindowSize = 1;/*MAX_SMALL_WINDOW*/
  202. pSession->DGWindowSize = 1;/*MAX_SMALL_WINDOW*/
  203. pSession->fFastLink = FALSE; // start assuming slow link.
  204. pSession->fSendSmall = TRUE;
  205. pSession->fSendSmallDG = TRUE;
  206. pSession->fReceiveSmall = TRUE;
  207. pSession->fReceiveSmallDG = TRUE;
  208. #if 0
  209. // use this code to START with large packet headers
  210. pSession->MaxCSends = MAX_LARGE_CSENDS;
  211. pSession->MaxCDGSends = MAX_LARGE_DG_CSENDS;
  212. pSession->WindowSize = MAX_LARGE_WINDOW;
  213. pSession->DGWindowSize = MAX_LARGE_WINDOW;
  214. pSession->fSendSmall = FALSE;
  215. pSession->fSendSmallDG = FALSE;
  216. #endif
  217. pSession->MaxPacketSize = pProtocol->m_dwSPMaxFrame;
  218. pSession->FirstRlyReceive=pSession->LastRlyReceive=0;
  219. pSession->InMsgMask = 0;
  220. InitBilink(&pSession->pRlyReceiveQ);
  221. InitBilink(&pSession->pDGReceiveQ);
  222. InitBilink(&pSession->pRlyWaitingQ);
  223. InitSessionStats(pSession);
  224. pSession->SendRateThrottle = 28800;
  225. pSession->FpAvgUnThrottleTime = Fp(1); // assume 1 ms to schedule unthrottle (first guess)
  226. pSession->tNextSend=pSession->tLastSAK=timeGetTime();
  227. Unlock(&pProtocol->m_SessionLock);
  228. hr=DP_OK;
  229. exit:
  230. return hr;
  231. exit2:
  232. hr=DPERR_OUTOFMEMORY;
  233. return hr;
  234. }
  235. /*=============================================================================
  236. GetDPIDIndex - lookup a session based on the Index
  237. Description:
  238. Parameters:
  239. DWORD index - Find the dpid for this index
  240. Return Values:
  241. DPID - dpid of the index
  242. -----------------------------------------------------------------------------*/
  243. DPID GetDPIDByIndex(PPROTOCOL pProtocol, DWORD index)
  244. {
  245. PSESSION pSession;
  246. DPID dpid;
  247. Lock(&pProtocol->m_SessionLock);
  248. if(index == SERVERPLAYER_INDEX){
  249. dpid=DPID_SERVERPLAYER;
  250. } else if(index < pProtocol->m_SessionListSize &&
  251. (pSession=(*pProtocol->m_pSessions)[index]))
  252. {
  253. Lock(&pSession->SessionLock);
  254. dpid=pSession->dpid;
  255. Unlock(&pSession->SessionLock);
  256. } else {
  257. dpid=0xFFFFFFFF;
  258. DPF(1,"GetDPIDByIndex, no id at index %d player may me gone or not yet created locally.\n",index);
  259. }
  260. Unlock(&pProtocol->m_SessionLock);
  261. return dpid;
  262. }
  263. WORD GetIndexByDPID(PPROTOCOL pProtocol, DPID dpid)
  264. {
  265. DWORD dwUnmangledID;
  266. if(dpid == DPID_SERVERPLAYER){
  267. return SERVERPLAYER_INDEX;
  268. }
  269. dwUnmangledID = dpid ^ pProtocol->m_dwIDKey;
  270. return (WORD)(dwUnmangledID & INDEX_MASK);
  271. }
  272. /*=============================================================================
  273. GetSysSessionByIndex - lookup a session based on the Index
  274. Description:
  275. Parameters:
  276. DWORD index - Find the session for this index
  277. Return Values:
  278. NULL - if the SESSION is not found,
  279. else pointer to the Session.
  280. Notes:
  281. Adds a reference to the SESSION. When done with the session pointer
  282. the caller must call DecSessionRef.
  283. -----------------------------------------------------------------------------*/
  284. PSESSION GetSysSessionByIndex(PPROTOCOL pProtocol, DWORD index)
  285. {
  286. PSESSION pSession;
  287. Lock(&pProtocol->m_SessionLock);
  288. DPF(9,"==>GetSysSessionByIndex at index x%x\n", index);
  289. if( (index < pProtocol->m_SessionListSize) || (index == SERVERPLAYER_INDEX) ){
  290. // ptr to session at requested index.
  291. if(index == SERVERPLAYER_INDEX){
  292. pSession = pProtocol->m_pServerPlayerSession;
  293. } else {
  294. pSession = (*pProtocol->m_pSessions)[index];
  295. }
  296. if(pSession){
  297. // ptr to system session for session at requested index.
  298. pSession=(*pProtocol->m_pSessions)[pSession->iSysPlayer];
  299. if(pSession){
  300. Lock(&pSession->SessionLock);
  301. if(pSession->eState==Open){
  302. pSession->RefCount++;
  303. Unlock(&pSession->SessionLock);
  304. } else {
  305. // Session is closing or closed, don't give ptr.
  306. Unlock(&pSession->SessionLock);
  307. pSession=NULL;
  308. }
  309. }
  310. }
  311. } else {
  312. pSession=NULL;
  313. }
  314. DPF(9,"<===GetSysSessbyIndex pSession %x\n",pSession);
  315. Unlock(&pProtocol->m_SessionLock);
  316. return pSession;
  317. }
  318. /*=============================================================================
  319. GetSysSession - lookup a the system session of a session based on the DPID
  320. Description:
  321. Parameters:
  322. DPID idPlayer - Find the session for this player.
  323. Return Values:
  324. NULL - if the SESSION is not found, or the DPID has been re-cycled.
  325. else pointer to the Session.
  326. Notes:
  327. Adds a reference to the SESSION. When done with the session pointer
  328. the caller must call DecSessionRef.
  329. -----------------------------------------------------------------------------*/
  330. PSESSION GetSysSession(PPROTOCOL pProtocol, DPID idPlayer)
  331. {
  332. PSESSION pSession;
  333. DWORD dwUnmangledID;
  334. DWORD index;
  335. Lock(&pProtocol->m_SessionLock);
  336. if(idPlayer != DPID_SERVERPLAYER){
  337. dwUnmangledID = idPlayer ^ pProtocol->m_dwIDKey;
  338. index = dwUnmangledID & INDEX_MASK;
  339. pSession=(*pProtocol->m_pSessions)[index];
  340. } else {
  341. pSession=pProtocol->m_pServerPlayerSession;
  342. }
  343. if(pSession){
  344. if(pSession->dpid == idPlayer){
  345. pSession=(*pProtocol->m_pSessions)[pSession->iSysPlayer];
  346. if(pSession){
  347. Lock(&pSession->SessionLock);
  348. if(pSession->eState == Open){
  349. pSession->RefCount++;
  350. Unlock(&pSession->SessionLock);
  351. } else {
  352. // Closing, don't return value
  353. Unlock(&pSession->SessionLock);
  354. pSession=NULL;
  355. }
  356. } else {
  357. DPF(0,"GetSysSession: Looking on Session, Who's SysSession is gone!\n");
  358. ASSERT(0);
  359. }
  360. } else {
  361. //BUGBUG: break out here!
  362. DPF(1,"PROTOCOL: got dplay id that has been recycled (%x), now (%x)?\n",idPlayer,pSession->dpid);
  363. ASSERT(0);
  364. pSession=NULL;
  365. }
  366. }
  367. Unlock(&pProtocol->m_SessionLock);
  368. return pSession;
  369. }
  370. /*=============================================================================
  371. GetSession - lookup a session based on the DPID
  372. Description:
  373. Parameters:
  374. DPID idPlayer - Find the session for this player.
  375. Return Values:
  376. NULL - if the SESSION is not found, or the DPID has been re-cycled.
  377. else pointer to the Session.
  378. Notes:
  379. Adds a reference to the SESSION. When done with the session pointer
  380. the caller must call DecSessionRef.
  381. -----------------------------------------------------------------------------*/
  382. PSESSION GetSession(PPROTOCOL pProtocol, DPID idPlayer)
  383. {
  384. PSESSION pSession;
  385. DWORD dwUnmangledID;
  386. DWORD index;
  387. Lock(&pProtocol->m_SessionLock);
  388. if(idPlayer != DPID_SERVERPLAYER){
  389. dwUnmangledID = idPlayer ^ pProtocol->m_dwIDKey;
  390. index = dwUnmangledID & INDEX_MASK;
  391. pSession=(*pProtocol->m_pSessions)[index];
  392. } else {
  393. pSession=pProtocol->m_pServerPlayerSession;
  394. }
  395. if(pSession){
  396. if(pSession->dpid == idPlayer){
  397. Lock(&pSession->SessionLock);
  398. pSession->RefCount++;
  399. Unlock(&pSession->SessionLock);
  400. } else {
  401. //BUGBUG: break out here!
  402. DPF(1,"PROTOCOL: got dplay id that has been recycled (%x), now (%x)?\n",idPlayer,pSession->dpid);
  403. ASSERT(0);
  404. pSession=NULL;
  405. }
  406. }
  407. Unlock(&pProtocol->m_SessionLock);
  408. return pSession;
  409. }
  410. VOID ThrowOutReceiveQ(PPROTOCOL pProtocol, BILINK *pHead)
  411. {
  412. PRECEIVE pReceiveWalker;
  413. BILINK *pBilink;
  414. pBilink = pHead->next;
  415. while( pBilink != pHead ){
  416. pReceiveWalker=CONTAINING_RECORD(pBilink, RECEIVE, pReceiveQ);
  417. ASSERT_SIGN(pReceiveWalker, RECEIVE_SIGN);
  418. ASSERT(!pReceiveWalker->fBusy);
  419. pBilink=pBilink->next;
  420. Lock(&pReceiveWalker->ReceiveLock);
  421. if(!pReceiveWalker->fBusy){
  422. Delete(&pReceiveWalker->pReceiveQ);
  423. Unlock(&pReceiveWalker->ReceiveLock);
  424. DPF(8,"Throwing Out Receive %x from Q %x\n",pReceiveWalker, pHead);
  425. FreeReceive(pProtocol,pReceiveWalker);
  426. } else {
  427. Unlock(&pReceiveWalker->ReceiveLock);
  428. }
  429. }
  430. }
  431. /*=============================================================================
  432. DecSessionRef
  433. Description:
  434. Parameters:
  435. Return Values:
  436. -----------------------------------------------------------------------------*/
  437. INT DecSessionRef(PSESSION pSession)
  438. {
  439. PPROTOCOL pProtocol;
  440. INT count;
  441. PSEND pSendWalker;
  442. BILINK *pBilink;
  443. if(!pSession){
  444. return 0;
  445. }
  446. Lock(&pSession->SessionLock);
  447. count = --pSession->RefCount;
  448. Unlock(&pSession->SessionLock);
  449. if(!count){
  450. // No more references! Blow it away.
  451. DPF(9,"DecSessionRef:(firstchance) pSession %x, count=%d, Session Closed, called from %x \n",pSession,count,_ReturnAddress());
  452. pProtocol=pSession->pProtocol;
  453. Lock(&pProtocol->m_SessionLock);
  454. Lock(&pSession->SessionLock);
  455. if(!pSession->RefCount){
  456. // Remove any referece to the session from the protocol.
  457. if(pSession->iSession != SERVERPLAYER_INDEX){
  458. (*pProtocol->m_pSessions)[pSession->iSession]=NULL;
  459. } else {
  460. pProtocol->m_pServerPlayerSession=NULL;
  461. }
  462. pProtocol->m_nSessions--;
  463. } else {
  464. count=pSession->RefCount;
  465. }
  466. Unlock(&pSession->SessionLock);
  467. Unlock(&pProtocol->m_SessionLock);
  468. // Session is floating free and we own it. No one can reference
  469. // so we can safely blow it all away, don't need to lock during
  470. // these ops since no-one can reference any more...
  471. if(!count){
  472. DPF(9,"DecSessionRef(second chance): pSession %x, count=%d, Session Closed, called from %x \n",pSession,count,_ReturnAddress());
  473. //DEBUG_BREAK();
  474. pSession->eState=Closed;
  475. if(pSession->uUnThrottle){
  476. //timeKillEvent(pSession->uUnThrottle);
  477. CancelMyTimer(pSession->uUnThrottle, pSession->UnThrottleUnique);
  478. }
  479. // Free up Datagram send statistics.
  480. DeleteCriticalSection(&pSession->SessionStatLock);
  481. pBilink=pSession->DGStatList.next;
  482. while(pBilink != & pSession->DGStatList){
  483. PSENDSTAT pStat = CONTAINING_RECORD(pBilink, SENDSTAT, StatList);
  484. pBilink=pBilink->next;
  485. ReleaseSendStat(pStat);
  486. }
  487. // Complete pending sends.
  488. pBilink=pSession->SendQ.next;
  489. while(pBilink!=&pSession->SendQ){
  490. pSendWalker=CONTAINING_RECORD(pBilink, SEND, SendQ);
  491. pBilink=pBilink->next;
  492. CancelRetryTimer(pSendWalker);
  493. DoSendCompletion(pSendWalker, DPERR_CONNECTIONLOST);
  494. DecSendRef(pProtocol, pSendWalker);
  495. }
  496. //
  497. // throw out pending receives.
  498. //
  499. ThrowOutReceiveQ(pProtocol, &pSession->pDGReceiveQ);
  500. ThrowOutReceiveQ(pProtocol, &pSession->pRlyReceiveQ);
  501. ThrowOutReceiveQ(pProtocol, &pSession->pRlyWaitingQ);
  502. //
  503. // Free the session
  504. //
  505. if(pSession->hClosingEvent){
  506. DPF(5,"DecSessionRef: pSession %x Told Protocol DeletePlayer to continue\n",pSession);
  507. SetEvent(pSession->hClosingEvent);
  508. }
  509. UNSIGN(pSession->Signature);
  510. DeleteCriticalSection(&pSession->SessionLock);
  511. My_GlobalFree(pSession);
  512. }
  513. } else {
  514. DPF(9,"DecSessionRef: pSession %x count %d, called from %x\n",pSession, count, _ReturnAddress());
  515. }
  516. return count;
  517. }