Leaked source code of windows server 2003
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.

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