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.

1434 lines
34 KiB

  1. /*++
  2. Copyright (c) 1995-1996 Microsoft Corporation
  3. Module Name:
  4. OrSvr.cxx
  5. Abstract:
  6. Object resolver server side class implementations. CServerOxid, CServerOid,
  7. CServerSet and CServerSetTable classes are implemented here.
  8. Author:
  9. Mario Goertzel [MarioGo]
  10. Revision History:
  11. MarioGo 04-03-95 Combined many smaller .cxx files
  12. MarioGo 01-12-96 Locally unique IDs
  13. --*/
  14. #include<or.hxx>
  15. //
  16. // ScanForOrphanedPinnedOids
  17. //
  18. // This function is called from CServerOxid::ProcessRelease when
  19. // the soxid is deregistered or the server dies.
  20. //
  21. void
  22. ScanForOrphanedPinnedOids(CServerOxid* pDeadOxid)
  23. {
  24. CListElement* pLE;
  25. CServerOid* pOid;
  26. CServerOid* pNextOid;
  27. ASSERT(gpServerLock->HeldExclusive());
  28. // Walk the list, unpinning oids which were owned by the
  29. // dead soxid.
  30. pLE = gpServerPinnedOidList->First();
  31. pOid = pLE ? CServerOid::ContainingRecord2(pLE) : NULL;
  32. while (pOid)
  33. {
  34. pLE = pLE->Next();
  35. pNextOid = pLE ? CServerOid::ContainingRecord2(pLE) : NULL;
  36. if (pOid->GetOxid() == pDeadOxid)
  37. {
  38. // This will remove it from the pinned list
  39. pOid->SetPinned(FALSE);
  40. }
  41. pOid = pNextOid;
  42. }
  43. ASSERT(gpServerLock->HeldExclusive());
  44. return;
  45. }
  46. //
  47. // CServerOid methods
  48. //
  49. void
  50. CServerOid::Reference()
  51. {
  52. ASSERT(gpServerLock->HeldExclusive());
  53. BOOL fRemove = (this->References() == 0);
  54. // We may remove something from a PList more then once; this won't
  55. // hurt anything. fRemove is used to avoid trying to remove it more
  56. // often then necessary without taking lock.
  57. this->CIdTableElement::Reference();
  58. if (fRemove)
  59. {
  60. CPListElement * t = Remove();
  61. ASSERT(t == &this->_plist || t == 0);
  62. }
  63. }
  64. DWORD
  65. CServerOid::Release()
  66. {
  67. ASSERT(gpServerLock->HeldExclusive());
  68. DWORD c = this->CReferencedObject::Dereference();
  69. if (0 == c)
  70. {
  71. // If another thread is already running this down it
  72. // means we got referenced and released during the rundown
  73. // callback. That thread will figure out what to do.
  74. if (IsRunningDown())
  75. {
  76. KdPrintEx((DPFLTR_DCOMSS_ID,
  77. DPFLTR_WARNING_LEVEL,
  78. "OR: Oid running down in release: %p\n",
  79. this));
  80. }
  81. else if (IsFreed() || this->_pOxid->IsRunning() == FALSE)
  82. {
  83. // Server freed this OID already; no need to run it down
  84. KdPrintEx((DPFLTR_DCOMSS_ID,
  85. DPFLTR_INFO_LEVEL,
  86. "OR: OID %p freed by server so not rundown\n",
  87. this));
  88. SetRundown(TRUE);
  89. delete this;
  90. }
  91. else
  92. {
  93. // All serverset (or pinned) references have been released. Put
  94. // ourselves into the oid plist so we can be rundown.
  95. ASSERT(!IsPinned());
  96. Insert();
  97. }
  98. }
  99. // this pointer maybe invalid.
  100. return(c);
  101. }
  102. CServerOid::~CServerOid()
  103. {
  104. ASSERT(gpServerLock->HeldExclusive());
  105. ASSERT(_pOxid);
  106. ASSERT(_fRunningDown);
  107. ASSERT(!IsPinned());
  108. _pOxid->Release();
  109. gpServerOidTable->Remove(this);
  110. }
  111. void
  112. CServerOid::KeepAlive()
  113. // A client has removed this oid from its set. This keeps
  114. // the oid alive for another timeout period.
  115. {
  116. ASSERT(gpServerLock->HeldExclusive());
  117. if (IsRunningDown() == FALSE && References() == 0)
  118. {
  119. // It's in the rundown list, move it to the end.
  120. CPListElement *pT = Remove();
  121. ASSERT(pT == &this->_plist);
  122. Insert();
  123. }
  124. }
  125. void
  126. CServerOid::SetPinned(BOOL fPinned)
  127. {
  128. ASSERT(gpServerLock->HeldExclusive());
  129. // Assert that this is a state switch.
  130. ASSERT(_fPinned ? !fPinned : fPinned);
  131. // Set new state
  132. _fPinned = fPinned;
  133. // When we are pinned, we take an extra reference to avoid further
  134. // rundown attempts. When unpinned, we remove the reference.
  135. if (_fPinned)
  136. {
  137. this->Reference();
  138. // Now we should not be in any list
  139. ASSERT(_list.NotInList());
  140. ASSERT(_plist.NotInList());
  141. gpServerPinnedOidList->Insert(&_list);
  142. }
  143. else
  144. {
  145. // We should be in the gpServerPinnedOidList list, but not the plist
  146. ASSERT(_list.InList());
  147. ASSERT(_plist.NotInList());
  148. gpServerPinnedOidList->Remove(&_list);
  149. // This Release call may put us in the oidplist
  150. this->Release();
  151. }
  152. ASSERT(gpServerLock->HeldExclusive());
  153. return;
  154. }
  155. //
  156. // CServerOidPList method
  157. //
  158. CServerOid *
  159. CServerOidPList::RemoveEligibleOidWithMatchingOxid(
  160. IN CTime &when,
  161. IN CServerOxid* pOxid
  162. )
  163. {
  164. CPListElement *ple;
  165. CServerOid *poid;
  166. ASSERT(pOxid->OkayForMoreRundownCalls());
  167. CMutexLock lock(&this->_lock);
  168. ple = (CPListElement *)CPList::First();
  169. // Look thru the list. Stop when we see an oid whose
  170. // timeout has not yet expired, or when we find an expired
  171. // oid which "matches" the caller-supplied oid.
  172. while ((ple != NULL) && (*ple->GetTimeout() < when))
  173. {
  174. poid = CServerOid::ContainingRecord(ple);
  175. if (poid->GetOxid() == pOxid)
  176. {
  177. Remove(ple);
  178. return(poid);
  179. }
  180. ple = ple->Next();
  181. }
  182. return(0);
  183. }
  184. /*
  185. Routine Desciption
  186. This function looks for the next "eligible" oid
  187. in the list. An "eligible" oid is defined as
  188. 1) having a timeout older than 'when'; and
  189. 2) belonging to an oxid which can handle additional
  190. rundown calls.
  191. */
  192. CServerOid*
  193. CServerOidPList::RemoveEligibleOid(
  194. IN CTime& when)
  195. {
  196. CPListElement *ple;
  197. CServerOid *poid;
  198. CMutexLock lock(&this->_lock);
  199. ple = (CPListElement *)CPList::First();
  200. // Look thru the list. Stop when we see an oid whose
  201. // timeout has not yet expired, or when we find an expired
  202. // oid whose oxid can accept another rundown call
  203. while ((ple != NULL) && (*ple->GetTimeout() < when))
  204. {
  205. poid = CServerOid::ContainingRecord(ple);
  206. if (poid->GetOxid()->OkayForMoreRundownCalls())
  207. {
  208. Remove(ple);
  209. return(poid);
  210. }
  211. ple = ple->Next();
  212. }
  213. return(0);
  214. }
  215. //
  216. // CServerOidPList method
  217. //
  218. void
  219. CServerOidPList::ResetAllOidTimeouts()
  220. /*++
  221. Routine Desciption
  222. Resets the timeout of all oids currently in the
  223. list to (now + _timeout)(ie, as if all oids had been
  224. inserted into the list just this instant). No
  225. ordering changes are made among the list elements.
  226. Arguments:
  227. none
  228. Return Value:
  229. void
  230. --*/
  231. {
  232. CPListElement* ple = NULL;
  233. CServerOid* poid = NULL;
  234. CTime newtimeout;
  235. CMutexLock lock(&this->_lock);
  236. newtimeout.SetNow();
  237. newtimeout += _timeout;
  238. ple = (CPListElement *)CPList::First();
  239. while (ple != NULL)
  240. {
  241. poid = CServerOid::ContainingRecord(ple);
  242. poid->_plist.SetTimeout(newtimeout);
  243. ple = ple->Next();
  244. }
  245. return;
  246. }
  247. BOOL
  248. CServerOidPList::AreThereAnyEligibleOidsLyingAround(CTime& when, CServerOxid* pOxid)
  249. /*++
  250. Routine Desciption
  251. Searches the list to see if there are any oids from the specified soxid
  252. that are ready to be rundown.
  253. Arguments:
  254. when - the timeout to match against
  255. pOxid - the server oxid from which we should match oids from
  256. Return Value:
  257. TRUE - there is at least one oid from the specified server oxid which is ready
  258. to be rundown
  259. FALSE - there are no oids form the specified soxid ready to be rundown
  260. --*/
  261. {
  262. CPListElement *ple;
  263. CServerOid *poid;
  264. CMutexLock lock(&this->_lock);
  265. ple = (CPListElement *)CPList::First();
  266. // Look thru the list. Stop when we see an oid whose
  267. // timeout has not yet expired, or we see an expired oid
  268. // from the specified soxid
  269. while ((ple != NULL) && (*ple->GetTimeout() < when))
  270. {
  271. poid = CServerOid::ContainingRecord(ple);
  272. if (poid->GetOxid() == pOxid)
  273. {
  274. // aha, found a winner.
  275. return TRUE;
  276. }
  277. ple = ple->Next();
  278. }
  279. // did not find any matching oids for the specified criteria
  280. return FALSE;
  281. }
  282. //
  283. // CServerOxid methods
  284. //
  285. void
  286. CServerOxid::ProcessRelease()
  287. /*++
  288. Routine Desciption
  289. The server process owning this OXID has either died
  290. or deregistered this oxid. Releases the oxid and
  291. nulls the pProcess pointer.
  292. Arguments:
  293. n/a
  294. Return Value:
  295. n/a
  296. --*/
  297. {
  298. ASSERT(gpServerLock->HeldExclusive());
  299. ASSERT(_pProcess);
  300. _fRunning = FALSE;
  301. ScanForOrphanedPinnedOids(this);
  302. Release();
  303. // This pointer may now be invalid, _pProcess pointer maybe invalid.
  304. }
  305. CServerOxid::~CServerOxid(void)
  306. {
  307. ASSERT(gpServerLock->HeldExclusive());
  308. ASSERT(_pProcess);
  309. ASSERT(!IsRunning()); // implies that the oxid has been removed from the table
  310. ASSERT(_dwRundownCallsInProgress == 0);
  311. if (_hRpcRundownHandle)
  312. {
  313. ASSERT(0 && "Still have binding handle despite no outstanding calls");
  314. RpcBindingFree(&_hRpcRundownHandle);
  315. }
  316. _pProcess->Release();
  317. }
  318. ORSTATUS
  319. CServerOxid::GetInfo(
  320. OUT OXID_INFO *poxidInfo,
  321. IN BOOL fLocal
  322. )
  323. // Server lock held shared.
  324. {
  325. ORSTATUS status;
  326. DUALSTRINGARRAY *psa;
  327. if (!IsRunning())
  328. {
  329. // Server crashed, info is not needed.
  330. return(OR_NOSERVER);
  331. }
  332. if (fLocal)
  333. {
  334. psa = _pProcess->GetLocalBindings();
  335. }
  336. else
  337. {
  338. psa = _pProcess->GetRemoteBindings();
  339. }
  340. if (!psa)
  341. {
  342. return(OR_NOMEM);
  343. }
  344. // copy the data
  345. memcpy(poxidInfo, &_info, sizeof(_info));
  346. poxidInfo->psa = psa;
  347. return(OR_OK);
  348. }
  349. void
  350. CServerOxid::RundownOids(ULONG cOids,
  351. CServerOid* aOids[])
  352. // Note: Returns without the server lock held.
  353. {
  354. RPC_STATUS status = RPC_S_OK;
  355. ASSERT(cOids > 0);
  356. ASSERT(cOids <= MAX_OID_RUNDOWNS_PER_CALL);
  357. ASSERT(aOids);
  358. ASSERT(gpServerLock->HeldExclusive());
  359. // We only issue the rundown call if a) we're still running
  360. // and b) don't already have pending the maximum # of outstanding
  361. // rundown calls.
  362. // We check that we don't have too many calls outstanding, but this
  363. // should never actually happen since oids are not pulled from the
  364. // rundown list if this is not true.
  365. ASSERT(OkayForMoreRundownCalls());
  366. if (IsRunning() && (OkayForMoreRundownCalls()))
  367. {
  368. // Make sure we have an appropriately configured binding handle
  369. if (!_hRpcRundownHandle)
  370. {
  371. status = EnsureRundownHandle();
  372. }
  373. if (status == RPC_S_OK)
  374. {
  375. ASSERT(_hRpcRundownHandle);
  376. // Note: The server lock is released during the callback.
  377. // Since the OID hasn't rundown yet, it will keep a reference
  378. // to this OXID which in turn keeps the process object alive.
  379. // Ask our process object to issue an async call to try
  380. // to rundown the specified oids. If call was sent ok,
  381. // return. Otherwise fall thru and cleanup below.
  382. _dwRundownCallsInProgress++;
  383. status = _pProcess->RundownOids(_hRpcRundownHandle,
  384. this,
  385. cOids,
  386. aOids);
  387. ASSERT(!gpServerLock->HeldExclusive());
  388. if (status == RPC_S_OK)
  389. {
  390. return;
  391. }
  392. else
  393. {
  394. // Re-take the lock
  395. gpServerLock->LockExclusive();
  396. // The call is not outstanding anymore
  397. _dwRundownCallsInProgress--;
  398. }
  399. }
  400. }
  401. ASSERT(!IsRunning() || (status != RPC_S_OK));
  402. ASSERT(gpServerLock->HeldExclusive());
  403. BYTE aRundownStatus[MAX_OID_RUNDOWNS_PER_CALL];
  404. // If server died or apartment was unregistered, okay to run all
  405. // oids down. Otherwise we make the oids wait another timeout
  406. // period before we try again.
  407. for (ULONG i = 0; i < cOids; i++)
  408. {
  409. aRundownStatus[i] = IsRunning() ? ORS_DONTRUNDOWN : ORS_OK_TO_RUNDOWN;
  410. }
  411. // Call the notify function whih will do appropriate
  412. // cleanup on the oids
  413. ProcessRundownResultsInternal(FALSE, cOids, aOids, aRundownStatus);
  414. ASSERT(!gpServerLock->HeldExclusive());
  415. }
  416. void
  417. CServerOxid::ProcessRundownResults(ULONG cOids,
  418. CServerOid* aOids[],
  419. BYTE aRundownStatus[])
  420. /*++
  421. Routine Desciption
  422. Takes the appropriate action based on the result of trying
  423. to rundown one or more oids.
  424. Arguments:
  425. cOids -- # of oids in aOids
  426. aOids -- array of CServerOid*'s that we tried to rundown
  427. aRundownStatus -- array of status values from the
  428. OID_RUNDOWN_STATUS enumeration.
  429. Return Value:
  430. void
  431. --*/
  432. {
  433. ProcessRundownResultsInternal(TRUE,
  434. cOids,
  435. aOids,
  436. aRundownStatus);
  437. return;
  438. }
  439. void
  440. CServerOxid::ProcessRundownResultsInternal(BOOL fAsyncReturn,
  441. ULONG cOids,
  442. CServerOid* aOids[],
  443. BYTE aRundownStatus[])
  444. /*++
  445. Routine Desciption
  446. Takes the appropriate action based on the result of trying
  447. to rundown one or more oids.
  448. Caller must have gpServerLock exclusive when calling. Function
  449. returns with gpServerLock released.
  450. Arguments:
  451. fAsyncReturn -- TRUE if we are processing these results in response
  452. to a async rundown call returning, FALSE otherwise.
  453. cOids -- # of oids in aOids
  454. aOids -- array of CServerOid*'s that we tried to rundown
  455. aRundownStatus -- array of status values from the
  456. OID_RUNDOWN_STATUS enumeration.
  457. Return Value:
  458. void
  459. --*/
  460. {
  461. ULONG i;
  462. ASSERT(gpServerLock->HeldExclusive());
  463. // Ensure a reference exists for the duration of this function.
  464. // If this is an async return, then we know the async code is
  465. // keeping it warm for us.
  466. if (!fAsyncReturn)
  467. {
  468. Reference();
  469. }
  470. for(i = 0; i < cOids; i++)
  471. {
  472. CServerOid* pOid;
  473. pOid = aOids[i];
  474. ASSERT(pOid);
  475. ASSERT(this == pOid->GetOxid());
  476. if (aRundownStatus[i] == ORS_OID_PINNED)
  477. {
  478. KdPrintEx((DPFLTR_DCOMSS_ID,
  479. DPFLTR_INFO_LEVEL,
  480. "OR: Randown OID %p but the client says it's pinned\n",
  481. pOid));
  482. // Server says that particular oid is "pinned", ie cannot
  483. // be rundown until we are told otherwise. Note that we
  484. // check for this before we check # of references -- the
  485. // ORS_OID_PINNED status takes precedence.
  486. pOid->SetPinned(TRUE);
  487. pOid->SetRundown(FALSE);
  488. }
  489. else if (pOid->References() != 0)
  490. {
  491. // Added to a set while running down and still referenced.
  492. pOid->SetRundown(FALSE);
  493. }
  494. else if (aRundownStatus[i] == ORS_OK_TO_RUNDOWN)
  495. {
  496. delete pOid;
  497. }
  498. else
  499. {
  500. ASSERT(aRundownStatus[i] == ORS_DONTRUNDOWN);
  501. KdPrintEx((DPFLTR_DCOMSS_ID,
  502. DPFLTR_INFO_LEVEL,
  503. "OR: Randown OID %p but the client kept it alive\n",
  504. pOid));
  505. // Client want us to keep it alive and is still running.
  506. pOid->SetRundown(FALSE);
  507. pOid->Insert();
  508. }
  509. }
  510. ASSERT(gpServerLock->HeldExclusive());
  511. if (fAsyncReturn)
  512. {
  513. // One less async rundown call is now outstanding
  514. ASSERT(_dwRundownCallsInProgress > 0);
  515. ASSERT(_dwRundownCallsInProgress <= MAX_SIMULTANEOUS_RUNDOWNS_PER_APT);
  516. _dwRundownCallsInProgress--;
  517. // We no longer need gpServerLock after this point
  518. gpServerLock->UnlockExclusive();
  519. ASSERT(!gpServerLock->HeldExclusive());
  520. //
  521. // There may be other expired oids belonging to this
  522. // oxid; go check for that now, and try to run them down
  523. // if so.
  524. //
  525. // We used to call RundownOidsHelper directly from this point;
  526. // that function could then potentially make another async rpc
  527. // rundown call back to the server process. Unfortunately, RPC
  528. // sometimes treats async LRPC calls as synchronous, and when
  529. // that happens it can cause deadlocks (NT 749995). RPC will fix
  530. // this in Longhorn. For now we will workaround it by initiating
  531. // the new rundown call from a separate thread.
  532. //
  533. CTime now;
  534. if (gpServerOidPList->AreThereAnyEligibleOidsLyingAround(now, this))
  535. {
  536. //
  537. // Take a reference on ourself. This will be released either
  538. // at the end of CServerOxid::RundownHelperThreadProc, or below
  539. // if QueueUserWorkItem returns an error.
  540. Reference();
  541. if (!QueueUserWorkItem(CServerOxid::RundownHelperThreadProc, this, 0))
  542. {
  543. // Oops, error occurred. This is not fatal (the worker threads
  544. // will retry later anyway). Release the reference we took
  545. // above.
  546. Release();
  547. }
  548. }
  549. }
  550. // Try to clean up the rpc binding handle. In the case where
  551. // we are running down a large # of objects (hence we have a
  552. // "chained" series of rundown calls happening), the binding
  553. // handle will remain cached until the last call finishes.
  554. //
  555. // jsimmons 12/04/02 - there is a race here now. Depending on how
  556. // fast an above call to RundownHelperThreadProc is processed on a
  557. // worker thread, we may end up destroying and re-creating the rundown
  558. // handle more often than necessary. Doesn't hurt anything, it's just
  559. // wasted work. I don't want to fix this so late in the game.
  560. //
  561. ReleaseRundownHandle();
  562. if (!fAsyncReturn)
  563. {
  564. // Release the safety reference we took above. (NOTE:
  565. // this might be the last reference!)
  566. Release();
  567. // gpServerLock gets released above in the async return case, otherwise
  568. // in the non-async return case we need to do it here
  569. gpServerLock->UnlockExclusive();
  570. }
  571. return;
  572. }
  573. DWORD WINAPI CServerOxid::RundownHelperThreadProc(LPVOID pv)
  574. /*++
  575. Routine Desciption
  576. NT thread pool entry point used for doing extra rundown attempts.
  577. Arguments:
  578. pv - pointer to the CServerOxid which initiated this rundown attempt,
  579. we own one reference on this object that needs to be released.
  580. Return Value:
  581. 0 - processing completed
  582. --*/
  583. {
  584. ASSERT(pv);
  585. CServerOxid* pThis = (CServerOxid*)pv;
  586. //
  587. // Attempt to do an extra rundown attempt.
  588. //
  589. // RundownOidsHelper expects that gpServerLock is held when
  590. // called, so we better take it now:
  591. //
  592. gpServerLock->LockExclusive();
  593. // Only call RundownOidsHelper if we are okay to accept more
  594. // rundown calls. It is possible that we were okay when this
  595. // piece of work was requested, but before we could run one of
  596. // the normal worker threads picked up the soxid and issued
  597. // another rundown.
  598. if (pThis->OkayForMoreRundownCalls())
  599. {
  600. CTime now;
  601. (void)RundownOidsHelper(&now, pThis);
  602. //
  603. // However, RundownOidsHelper returns with the lock released, so
  604. // we assert that here.
  605. //
  606. ASSERT(!gpServerLock->HeldExclusive());
  607. // Retake gpServerLock for the Release call below
  608. gpServerLock->LockExclusive();
  609. }
  610. // Release the extra reference on the soxid that the caller
  611. // gave us; must hold gpServerLock across this in case this is
  612. // the last release.
  613. ASSERT(gpServerLock->HeldExclusive());
  614. pThis->Release();
  615. gpServerLock->UnlockExclusive();
  616. return 0;
  617. }
  618. RPC_STATUS
  619. CServerOxid::EnsureRundownHandle()
  620. /*++
  621. Routine Desciption
  622. Checks to see if we already have an _hRpcRundownHandle created,
  623. and if not, creates one.
  624. Arguments:
  625. void
  626. Return Value:
  627. RPC_S_OK -- _hRpcRundownHandle is present and configured correctly
  628. other -- error occurred and _hRpcRundownHandle is NULL
  629. --*/
  630. {
  631. RPC_STATUS status = RPC_S_OK;
  632. ASSERT(gpServerLock->HeldExclusive());
  633. if (!_hRpcRundownHandle)
  634. {
  635. ASSERT(_dwRundownCallsInProgress == 0);
  636. RPC_BINDING_HANDLE hRpc = NULL;
  637. status = RPC_S_OUT_OF_RESOURCES;
  638. hRpc = _pProcess->GetBindingHandle();
  639. if (hRpc)
  640. {
  641. IPID ipidUnk = GetIPID();
  642. status = RpcBindingSetObject(hRpc, &ipidUnk);
  643. if (status == RPC_S_OK)
  644. {
  645. _hRpcRundownHandle = hRpc;
  646. }
  647. else
  648. {
  649. RpcBindingFree(&hRpc);
  650. }
  651. }
  652. }
  653. return status;
  654. }
  655. void
  656. CServerOxid::ReleaseRundownHandle()
  657. /*++
  658. Routine Desciption
  659. Checks to see if we should release our cached RPC
  660. rundown binding handle.
  661. Arguments:
  662. void
  663. Return Value:
  664. void
  665. --*/
  666. {
  667. RPC_BINDING_HANDLE hRpcToFree = NULL;
  668. // May be looking at _dwRundownCallsInProgress outside
  669. // of the lock here.
  670. if ((_dwRundownCallsInProgress == 0) && _hRpcRundownHandle)
  671. {
  672. BOOL fTookLock = FALSE;
  673. if (!gpServerLock->HeldExclusive())
  674. {
  675. gpServerLock->LockExclusive();
  676. fTookLock = TRUE;
  677. }
  678. if (_dwRundownCallsInProgress == 0)
  679. {
  680. // Now it's really okay to free it, if it's
  681. // still there.
  682. hRpcToFree = _hRpcRundownHandle;
  683. _hRpcRundownHandle = NULL;
  684. }
  685. if (fTookLock)
  686. {
  687. gpServerLock->UnlockExclusive();
  688. }
  689. }
  690. if (hRpcToFree)
  691. {
  692. RPC_STATUS status = RpcBindingFree(&hRpcToFree);
  693. ASSERT(status == RPC_S_OK);
  694. }
  695. return;
  696. }
  697. ORSTATUS
  698. CServerOxid::GetRemoteInfo(
  699. OUT OXID_INFO *pInfo,
  700. IN USHORT cClientProtseqs,
  701. IN USHORT aClientProtseqs[]
  702. )
  703. // Server lock held shared.
  704. {
  705. ORSTATUS status;
  706. USHORT protseq;
  707. status = GetInfo(pInfo, FALSE);
  708. if (OR_OK == status)
  709. {
  710. protseq = FindMatchingProtseq(cClientProtseqs,
  711. aClientProtseqs,
  712. pInfo->psa->aStringArray
  713. );
  714. if (0 == protseq)
  715. {
  716. MIDL_user_free(pInfo->psa);
  717. pInfo->psa = 0;
  718. status = OR_I_NOPROTSEQ;
  719. }
  720. }
  721. return(status);
  722. }
  723. ORSTATUS
  724. CServerOxid::LazyUseProtseq(
  725. IN USHORT cClientProtseqs,
  726. IN USHORT *aClientProtseqs
  727. )
  728. // Server lock held shared, returns with the server lock exclusive.
  729. // Note: It is possible, that after this call the OXID has been deleted.
  730. {
  731. ORSTATUS status;
  732. if (IsRunning())
  733. {
  734. // Keep this OXID process alive while making the callback. If the process
  735. // crashes and this OXID has no OIDs it could be released by everybody
  736. // else. This keeps the OXID and process alive until we finish.
  737. this->Reference();
  738. gpServerLock->UnlockShared();
  739. status = _pProcess->UseProtseqIfNeeded(cClientProtseqs, aClientProtseqs);
  740. gpServerLock->LockExclusive();
  741. this->Release();
  742. }
  743. else
  744. {
  745. gpServerLock->ConvertToExclusive();
  746. status = OR_NOSERVER;
  747. }
  748. // Note: The this poiner maybe BAD now.
  749. return(status);
  750. }
  751. //
  752. // CServerSet methods.
  753. //
  754. ORSTATUS
  755. CServerSet::AddObject(OID &oid)
  756. {
  757. ORSTATUS status = OR_OK;
  758. CServerOid *pOid;
  759. ASSERT(gpServerLock->HeldExclusive());
  760. CIdKey key(oid);
  761. pOid = (CServerOid *)gpServerOidTable->Lookup(key);
  762. if (pOid)
  763. {
  764. ASSERT(_blistOids.Member(pOid) == FALSE);
  765. // Don't add duplicate IDs to the set
  766. if (_blistOids.Member(pOid) == FALSE)
  767. {
  768. status = _blistOids.Insert(pOid);
  769. if (status == OR_OK)
  770. {
  771. pOid->Reference();
  772. }
  773. }
  774. }
  775. else
  776. status = OR_BADOID;
  777. VALIDATE((status, OR_BADOID, OR_NOMEM, 0));
  778. return(status);
  779. }
  780. void
  781. CServerSet::RemoveObject(OID &oid)
  782. {
  783. CServerOid *pOid;
  784. ASSERT(gpServerLock->HeldExclusive());
  785. CIdKey key(oid);
  786. pOid = (CServerOid *)gpServerOidTable->Lookup(key);
  787. if (pOid)
  788. {
  789. CServerOid *pOidTmp = (CServerOid *)_blistOids.Remove(pOid);
  790. if (pOid == pOidTmp)
  791. {
  792. pOid->Release();
  793. }
  794. else
  795. {
  796. // Set doesn't contain the specified oid, treat this as an
  797. // add and delete by keeping the oid alive for another timeout
  798. // period.
  799. ASSERT(pOidTmp == 0);
  800. pOid->KeepAlive();
  801. }
  802. }
  803. }
  804. BOOL
  805. CServerSet::ValidateObjects(BOOL fShared)
  806. // fShared - Indicates if the server lock is held
  807. // shared (TRUE) or exclusive (FALSE).
  808. //
  809. // Return - TRUE the lock is still shared, false
  810. // the lock is held exclusive.
  811. {
  812. CServerOid *pOid;
  813. CBListIterator oids(&_blistOids);
  814. // Since we own a reference on all the Oids they must still exist.
  815. // No need to lock exclusive until we find something to delete.
  816. while(pOid = (CServerOid *)oids.Next())
  817. {
  818. if (!pOid->IsRunning())
  819. {
  820. if (fShared)
  821. {
  822. KdPrintEx((DPFLTR_DCOMSS_ID,
  823. DPFLTR_WARNING_LEVEL,
  824. "OR: Cleanup in set (%p), removing dead oids.\n",
  825. this,
  826. pOid));
  827. gpServerLock->ConvertToExclusive();
  828. fShared = FALSE;
  829. oids.Reset(&_blistOids);
  830. continue;
  831. }
  832. CServerOid *pOidTmp = (CServerOid *)_blistOids.Remove(pOid);
  833. ASSERT(pOidTmp == pOid);
  834. ASSERT(pOid->IsRunning() == FALSE);
  835. pOid->Release();
  836. }
  837. }
  838. return(fShared);
  839. }
  840. BOOL
  841. CServerSet::Rundown()
  842. // Rundown the whole set.
  843. {
  844. CServerOid *poid;
  845. CTime now;
  846. ASSERT(gpServerLock->HeldExclusive());
  847. if (_timeout > now)
  848. {
  849. // Don't rundown if we've received a late ping.
  850. return(FALSE);
  851. }
  852. if (_fLocal && _blistOids.Size() != 0)
  853. {
  854. KdPrintEx((DPFLTR_DCOMSS_ID,
  855. DPFLTR_WARNING_LEVEL,
  856. "OR: Premature rundown of local set ignored.\n"));
  857. return(FALSE);
  858. }
  859. KdPrintEx((DPFLTR_DCOMSS_ID,
  860. DPFLTR_WARNING_LEVEL,
  861. "OR: Set %p's client appears to have died\n",
  862. this));
  863. CBListIterator oids(&_blistOids);
  864. while(poid = (CServerOid *)oids.Next())
  865. {
  866. poid->Release();
  867. }
  868. return(TRUE);
  869. }
  870. //
  871. // CServerSetTable implementation
  872. //
  873. CServerSet *
  874. CServerSetTable::Allocate(
  875. IN USHORT sequence,
  876. IN PSID psid,
  877. IN BOOL fLocal,
  878. OUT ID &setid
  879. )
  880. /*++
  881. Routine Description:
  882. Allocates a new CServerSet and returns the setid for the new set.
  883. Arguments:
  884. sequence - initial sequence number for the new set.
  885. psid - pointer to an NT SID structure for the new set.
  886. fLocal - TRUE : set is for the local client,
  887. FALSE : set is for a remote client
  888. setid - the setid of the set returned. Unchanged if return value 0.
  889. Return Value:
  890. 0 - Unable to allocate a resource
  891. non-zero - A pointer to the newly created set.
  892. --*/
  893. {
  894. ASSERT(gpServerLock->HeldExclusive());
  895. UINT i;
  896. LARGE_INTEGER li;
  897. ASSERT(_cAllocated <= _cMax);
  898. if (_cAllocated == _cMax)
  899. {
  900. // Table is full, realloc
  901. // Do this first, if it succeeds great even if
  902. // a later allocation fails. If not, fail now.
  903. IndexElement *pNew = new IndexElement[_cMax * 2];
  904. if (!pNew)
  905. {
  906. return(0);
  907. }
  908. for (i = 0; i < _cMax; i++)
  909. {
  910. pNew[i] = _pElements[i];
  911. }
  912. for(i = _cMax; i < _cMax*2; i++)
  913. {
  914. if FAILED(gRNG.GenerateRandomNumber(&(pNew[i]._sequence), sizeof(pNew[i]._sequence)))
  915. {
  916. delete []pNew;
  917. return 0;
  918. }
  919. pNew[i]._pSet = 0;
  920. }
  921. delete []_pElements;
  922. _pElements = pNew;
  923. _cMax *= 2;
  924. }
  925. CServerSet *pSet = new CServerSet(sequence, psid, fLocal);
  926. if (0 == pSet)
  927. {
  928. return(0);
  929. }
  930. ASSERT(_pElements);
  931. ASSERT(_cMax > _cAllocated);
  932. for(i = _iFirstFree; i < _cMax; i++)
  933. {
  934. if (0 == _pElements[i]._pSet)
  935. {
  936. _pElements[i]._sequence++;
  937. _pElements[i]._pSet = pSet;
  938. li.HighPart = i + 1;
  939. li.LowPart = _pElements[i]._sequence;
  940. setid = li.QuadPart;
  941. _iFirstFree = i + 1;
  942. _cAllocated++;
  943. return pSet;
  944. }
  945. }
  946. ASSERT(0);
  947. return(0);
  948. }
  949. CServerSet *
  950. CServerSetTable::Lookup(
  951. IN ID setid
  952. )
  953. /*++
  954. Routine Description:
  955. Looks up an a set given the sets ID.
  956. Server lock held shared.
  957. Arguments:
  958. setid - the ID of the set to lookup
  959. Return Value:
  960. 0 - set doesn't exist
  961. non-zero - the set.
  962. --*/
  963. {
  964. LARGE_INTEGER li;
  965. li.QuadPart = setid;
  966. LONG i = li.HighPart - 1;
  967. DWORD sequence = (DWORD)(setid & ~((ID)0));
  968. if (i >= 0 && (DWORD) i < _cMax)
  969. {
  970. if (_pElements[i]._sequence == sequence)
  971. {
  972. // May still be null if it is free and has not yet be reused.
  973. return(_pElements[i]._pSet);
  974. }
  975. }
  976. return(0);
  977. }
  978. ID
  979. CServerSetTable::CheckForRundowns(
  980. )
  981. /*++
  982. Routine Description:
  983. Used by ping and worker threads to monitor for sets that should
  984. be rundown. It is called with the server lock held shared.
  985. Arguments:
  986. None
  987. Return Value:
  988. 0 - Didn't find a set to rundown
  989. non-zero - ID of a set which may need to be rundown.
  990. --*/
  991. {
  992. UINT i, end;
  993. LARGE_INTEGER id;
  994. id.QuadPart = 0;
  995. ASSERT(_iRundown < _cMax);
  996. if (_cAllocated == 0)
  997. {
  998. return(0);
  999. }
  1000. i = _iRundown;
  1001. do
  1002. {
  1003. ASSERT(_cAllocated); // loop assumes one or more allocated elements.
  1004. i = (i + 1) % _cMax;
  1005. }
  1006. while(0 == _pElements[i]._pSet);
  1007. ASSERT(_pElements[i]._pSet);
  1008. if (_pElements[i]._pSet->ShouldRundown())
  1009. {
  1010. id.HighPart = i + 1;
  1011. id.LowPart = _pElements[i]._sequence;
  1012. }
  1013. _iRundown = i;
  1014. return(id.QuadPart);
  1015. }
  1016. BOOL
  1017. CServerSetTable::RundownSetIfNeeded(
  1018. IN ID setid
  1019. )
  1020. /*++
  1021. Routine Description:
  1022. Rundowns down a set (or sets) if needed. Called by
  1023. ping and worker threads. Server lock held exclusive.
  1024. Arguments:
  1025. setid - An ID previously returned from CheckForRundowns.
  1026. Return Value:
  1027. TRUE - A set was actually rundown
  1028. FALSE - No sets actually rundown
  1029. --*/
  1030. {
  1031. ASSERT(gpServerLock->HeldExclusive());
  1032. if (gPowerState != PWR_RUNNING)
  1033. {
  1034. // Machine is or was about to be suspended. We don't want
  1035. // to rundown any sets in this state.
  1036. return(FALSE);
  1037. }
  1038. CServerSet *pSet = Lookup(setid);
  1039. if (0 == pSet || FALSE == pSet->ShouldRundown())
  1040. {
  1041. // Set already randown or has been pinged in the meantime.
  1042. return(FALSE);
  1043. }
  1044. // PERF REVIEW this function has the option of running multiple sets,
  1045. // saving the worker thread from taking and leaving the lock many times
  1046. // when a bunch of sets all rundown. This feature is not used.
  1047. LARGE_INTEGER li;
  1048. li.QuadPart = setid;
  1049. UINT i = li.HighPart - 1;
  1050. if (pSet->Rundown())
  1051. {
  1052. delete pSet;
  1053. _cAllocated--;
  1054. if (i < _iFirstFree) _iFirstFree = i;
  1055. _pElements[i]._pSet = 0;
  1056. return(TRUE);
  1057. }
  1058. return(FALSE);
  1059. }
  1060. void
  1061. CServerSetTable::PingAllSets()
  1062. /*++
  1063. Routine Description:
  1064. Performs a ping of all sets currently in the table.
  1065. Arguments:
  1066. none
  1067. Return Value:
  1068. void
  1069. --*/
  1070. {
  1071. ASSERT(gpServerLock->HeldExclusive());
  1072. ULONG i;
  1073. for(i = 0; i < _cMax; i++)
  1074. {
  1075. if (_pElements[i]._pSet)
  1076. {
  1077. _pElements[i]._pSet->Ping(FALSE);
  1078. }
  1079. }
  1080. }