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.

690 lines
16 KiB

  1. /*++
  2. Copyright (c) 1990-1994 Microsoft Corporation
  3. All rights reserved
  4. Module Name:
  5. threads.c (thread manager)
  6. Abstract:
  7. Handles the threads used to for notifications (WPC, FFPCN)
  8. Author:
  9. Albert Ting (AlbertT) 25-Jan-94
  10. Environment:
  11. User Mode -Win32
  12. Revision History:
  13. --*/
  14. #include "precomp.h"
  15. #pragma hdrstop
  16. #include "threadm.h"
  17. #include "ntfytab.h"
  18. #define ENTER_THREAD_LIST() EnterCriticalSection(tmStateStatic.pCritSec)
  19. #define EXIT_THREAD_LIST() LeaveCriticalSection(tmStateStatic.pCritSec)
  20. extern CRITICAL_SECTION RouterNotifySection;
  21. DWORD
  22. ThreadNotifyProcessJob(
  23. PTMSTATEVAR pTMStateVar,
  24. PJOB pJob);
  25. PJOB
  26. ThreadNotifyNextJob(
  27. PTMSTATEVAR ptmStateVar);
  28. TMSTATESTATIC tmStateStatic = {
  29. 10,
  30. 2500,
  31. (PFNPROCESSJOB)ThreadNotifyProcessJob,
  32. (PFNNEXTJOB)ThreadNotifyNextJob,
  33. NULL,
  34. NULL,
  35. &RouterNotifySection
  36. };
  37. TMSTATEVAR tmStateVar;
  38. PCHANGE pChangeList;
  39. WCHAR szThreadMax[] = L"ThreadNotifyMax";
  40. WCHAR szThreadIdleLife[] = L"ThreadNotifyIdleLife";
  41. WCHAR szThreadNotifySleep[] = L"ThreadNotifySleep";
  42. DWORD dwThreadNotifySleep = 1000;
  43. BOOL
  44. ThreadInit()
  45. {
  46. HKEY hKey;
  47. DWORD dwType = REG_DWORD;
  48. DWORD cbData;
  49. if (!TMCreateStatic(&tmStateStatic))
  50. return FALSE;
  51. if (!TMCreate(&tmStateStatic, &tmStateVar))
  52. return FALSE;
  53. if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  54. szPrintKey,
  55. 0,
  56. KEY_READ,
  57. &hKey)) {
  58. cbData = sizeof(tmStateStatic.uMaxThreads);
  59. //
  60. // Ignore failure case since we default to 10.
  61. //
  62. RegQueryValueEx(hKey,
  63. szThreadMax,
  64. NULL,
  65. &dwType,
  66. (LPBYTE)&tmStateStatic.uMaxThreads,
  67. &cbData);
  68. cbData = sizeof(tmStateStatic.uIdleLife);
  69. //
  70. // Ignore failure case since we default to 1000 (1 sec).
  71. //
  72. RegQueryValueEx(hKey,
  73. szThreadIdleLife,
  74. NULL,
  75. &dwType,
  76. (LPBYTE)&tmStateStatic.uIdleLife,
  77. &cbData);
  78. cbData = sizeof(dwThreadNotifySleep);
  79. //
  80. // Ignore failure case since we default to 2500 (2.5 sec).
  81. //
  82. RegQueryValueEx(hKey,
  83. szThreadNotifySleep,
  84. NULL,
  85. &dwType,
  86. (LPBYTE)&dwThreadNotifySleep,
  87. &cbData);
  88. RegCloseKey(hKey);
  89. }
  90. return TRUE;
  91. }
  92. VOID
  93. ThreadDestroy()
  94. {
  95. TMDestroy(&tmStateVar);
  96. TMDestroyStatic(&tmStateStatic);
  97. }
  98. BOOL
  99. LinkChange(
  100. PCHANGE pChange)
  101. /*++
  102. Routine Description:
  103. Link up the change to the list of jobs that need to be processed.
  104. If the call succeeded but there was an overflow at the client,
  105. then we won't add this to the list until it gets refreshed.
  106. Arguments:
  107. Return Value:
  108. --*/
  109. {
  110. if (pChange->eStatus & STATUS_CHANGE_DISCARDNOTED) {
  111. return FALSE;
  112. }
  113. pChange->cRef++;
  114. pChange->eStatus |= STATUS_CHANGE_ACTIVE;
  115. LinkAdd(&pChange->Link, (PLINK*)&pChangeList);
  116. return TRUE;
  117. }
  118. BOOL
  119. ThreadNotify(
  120. LPPRINTHANDLE pPrintHandle)
  121. /*++
  122. Routine Description:
  123. Handles notifying the remote clients of changes.
  124. Arguments:
  125. pPrintHandle - printer that requires notification
  126. Return Value:
  127. TRUE = success, GetLastError() valid on FALSE.
  128. NOTE: Currenly only supports grouping
  129. --*/
  130. {
  131. PCHANGE pChange = pPrintHandle->pChange;
  132. ENTER_THREAD_LIST();
  133. //
  134. // Only add if we're not on the linked list already.
  135. //
  136. if (!(pChange->eStatus & STATUS_CHANGE_ACTIVE)) {
  137. DBGMSG(DBG_NOTIFY, ("TMN: link added 0x%x cRef++ %d\n",
  138. pChange,
  139. pChange->cRef));
  140. //
  141. // Only add ourseleves to the linked list and
  142. // Notify via TMAddJob if we are not on the list.
  143. //
  144. if (LinkChange(pChange))
  145. TMAddJob(&tmStateVar);
  146. } else {
  147. pChange->eStatus |= STATUS_CHANGE_ACTIVE_REQ;
  148. DBGMSG(DBG_NOTIFY, ("TMN: In LL already 0x%x cRef %d\n",
  149. pChange,
  150. pChange->cRef));
  151. }
  152. EXIT_THREAD_LIST();
  153. return TRUE;
  154. }
  155. PJOB
  156. ThreadNotifyNextJob(
  157. PTMSTATEVAR ptmStateVar)
  158. /*++
  159. Routine Description:
  160. Callback to get the next job.
  161. Arguments:
  162. ptmStateVar - ignored.
  163. Return Value:
  164. pJob (pChange)
  165. --*/
  166. {
  167. PCHANGE pChange;
  168. ENTER_THREAD_LIST();
  169. //
  170. // If there are no jobs left, quit.
  171. //
  172. pChange = (PCHANGE)pChangeList;
  173. DBGMSG(DBG_NOTIFY, ("ThreadNotifyNextJob: Removing pChange 0x%x\n",
  174. pChange));
  175. if (pChange) {
  176. LinkDelete(&pChange->Link, (PLINK*)&pChangeList);
  177. }
  178. EXIT_THREAD_LIST();
  179. return (PJOB)pChange;
  180. }
  181. DWORD
  182. ThreadNotifyProcessJob(
  183. PTMSTATEVAR pTMStateVar,
  184. PJOB pJob)
  185. /*++
  186. Routine Description:
  187. Does the actual RPC call to notify the client.
  188. Arguments:
  189. pJob = pChange structure
  190. Return Value:
  191. --*/
  192. {
  193. PCHANGE pChange = (PCHANGE)pJob;
  194. DWORD fdwChangeFlags;
  195. HANDLE hNotifyRemote;
  196. PPRINTER_NOTIFY_INFO pPrinterNotifyInfo;
  197. PPRINTER_NOTIFY_INFO pPrinterNotifyInfoNew;
  198. RPC_V2_UREPLY_PRINTER Reply;
  199. DWORD dwReturn;
  200. DWORD dwResult = 0;
  201. DWORD dwColor;
  202. ENTER_THREAD_LIST();
  203. if (pChange->eStatus & STATUS_CHANGE_CLOSING) {
  204. //
  205. // Abort this job
  206. //
  207. dwReturn = ERROR_INVALID_PARAMETER;
  208. goto Done;
  209. }
  210. fdwChangeFlags = pChange->fdwChangeFlags;
  211. pChange->fdwChangeFlags = 0;
  212. //
  213. // We must save out this copy in case we have some more info
  214. // while we are RPCing. Turn off LL_REQ because we are about
  215. // to process this batch.
  216. //
  217. pPrinterNotifyInfo = pChange->ChangeInfo.pPrinterNotifyInfo;
  218. pChange->ChangeInfo.pPrinterNotifyInfo = NULL;
  219. pChange->eStatus &= ~STATUS_CHANGE_ACTIVE_REQ;
  220. dwColor = pChange->dwColor;
  221. //
  222. // We were already marked in use when the job was added.
  223. // If another thread wants to delete it, they should OR in
  224. // STATUS_CHANGE_CLOSING, which we will pickup.
  225. //
  226. EXIT_THREAD_LIST();
  227. if (pChange->hNotifyRemote) {
  228. DBGMSG(DBG_NOTIFY, (">> Remoting pChange 0x%x hNotifyRemote 0x%x\n",
  229. pChange,
  230. pChange->hNotifyRemote));
  231. RpcTryExcept {
  232. //
  233. // Note:
  234. //
  235. // We should not be impersonating at this stage since
  236. // we will get a separate session id.
  237. //
  238. if (pPrinterNotifyInfo) {
  239. Reply.pInfo = (PRPC_V2_NOTIFY_INFO)pPrinterNotifyInfo;
  240. //
  241. // Remote case; bind and call the remote router.
  242. //
  243. dwReturn = RpcRouterReplyPrinterEx(
  244. pChange->hNotifyRemote,
  245. dwColor,
  246. fdwChangeFlags,
  247. &dwResult,
  248. REPLY_PRINTER_CHANGE,
  249. Reply);
  250. } else {
  251. dwReturn = RpcRouterReplyPrinter(
  252. pChange->hNotifyRemote,
  253. fdwChangeFlags,
  254. 1,
  255. (PBYTE)&pPrinterNotifyInfo);
  256. }
  257. } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
  258. dwReturn = RpcExceptionCode();
  259. } RpcEndExcept
  260. } else {
  261. dwReturn = ERROR_INVALID_HANDLE;
  262. DBGMSG(DBG_WARNING, ("ThreadNotifyProcessJob: no hNotifyRemote\n"));
  263. }
  264. if (dwReturn) {
  265. DBGMSG(DBG_WARNING, ("ThreadNotifyProcessJob: RPC error %d\npChange 0x%x, hNotifyRemote 0x%x, hPrinterRemote 0x%x\n",
  266. dwReturn,
  267. pChange,
  268. pChange->hNotifyRemote,
  269. pChange->dwPrinterRemote));
  270. //
  271. // On error, close and retry
  272. //
  273. CloseReplyRemote(pChange->hNotifyRemote);
  274. if (OpenReplyRemote(pChange->pszLocalMachine,
  275. &pChange->hNotifyRemote,
  276. pChange->dwPrinterRemote,
  277. REPLY_TYPE_NOTIFICATION,
  278. 0,
  279. NULL)) {
  280. pChange->hNotifyRemote = NULL;
  281. }
  282. }
  283. Sleep(dwThreadNotifySleep);
  284. ENTER_THREAD_LIST();
  285. pPrinterNotifyInfoNew = pChange->ChangeInfo.pPrinterNotifyInfo;
  286. //
  287. // Keep dwResult only if the color is current.
  288. //
  289. // The avoids the problem when the server sends a discard (RPC1),
  290. // then the client refreshes (RPC2). RPC2 returns first, clearing
  291. // the client's discard bit. The server overflows the new
  292. // buffer. RPC1 completes, and returns discardnoted, which is
  293. // incorrect since it is stale. If the color isn't checked, then
  294. // the server thinks that the discard has been noted when it really
  295. // has not.
  296. //
  297. if (dwColor != pChange->dwColor) {
  298. dwResult = 0;
  299. }
  300. if (pPrinterNotifyInfo) {
  301. //
  302. // Handle different error states from RPC. Each case/default
  303. // block must either update or pChange->ChangeInfo.pPrinterNotifyInfo
  304. // and free the old or the new if both exist.
  305. //
  306. switch (dwReturn) {
  307. case ERROR_SUCCESS:
  308. //
  309. // On success, see if the client saw the info but couldn't
  310. // store it since they overflowed. In this case, we note
  311. // this and never RPC to them again until they refresh.
  312. //
  313. if (dwResult & PRINTER_NOTIFY_INFO_DISCARDNOTED) {
  314. pChange->eStatus |= (STATUS_CHANGE_DISCARDED |
  315. STATUS_CHANGE_DISCARDNOTED);
  316. }
  317. //
  318. // If new buffer allocated, free our old one, else reuse buffer.
  319. //
  320. if (!pPrinterNotifyInfoNew) {
  321. //
  322. // Clear it since we are reusing it.
  323. //
  324. ClearPrinterNotifyInfo(pPrinterNotifyInfo, pChange);
  325. pChange->ChangeInfo.pPrinterNotifyInfo = pPrinterNotifyInfo;
  326. } else {
  327. //
  328. // Free the old one since we are using the new one now.
  329. //
  330. RouterFreePrinterNotifyInfo(pPrinterNotifyInfo);
  331. }
  332. pChange->ChangeInfo.pPrinterNotifyInfo->Flags |= dwResult;
  333. break;
  334. case RPC_S_CALL_FAILED_DNE:
  335. //
  336. // On DNE, keep the notification info. We are guarenteed by
  337. // rpc that this means no part of the call executed.
  338. //
  339. if (pPrinterNotifyInfoNew) {
  340. //
  341. // We already have some info. Merge it in
  342. // with the exiting data.
  343. //
  344. pChange->ChangeInfo.pPrinterNotifyInfo = pPrinterNotifyInfo;
  345. if (pChange->ChangeInfo.pPrintHandle) {
  346. AppendPrinterNotifyInfo(pChange->ChangeInfo.pPrintHandle,
  347. dwColor,
  348. pPrinterNotifyInfoNew);
  349. }
  350. RouterFreePrinterNotifyInfo(pPrinterNotifyInfoNew);
  351. }
  352. break;
  353. default:
  354. //
  355. // Did it succeed? Maybe, maybe not. Fail it by freeing
  356. // the current one if it exists, then clear the current one
  357. // and set the discard bit.
  358. //
  359. pChange->ChangeInfo.pPrinterNotifyInfo = pPrinterNotifyInfo;
  360. ClearPrinterNotifyInfo(pPrinterNotifyInfo, pChange);
  361. SetDiscardPrinterNotifyInfo(pPrinterNotifyInfo, pChange);
  362. //
  363. // Free the new buffer since we are reusing the old one.
  364. //
  365. if (pPrinterNotifyInfoNew) {
  366. RouterFreePrinterNotifyInfo(pPrinterNotifyInfoNew);
  367. }
  368. }
  369. }
  370. pChange->eStatus &= ~STATUS_CHANGE_ACTIVE;
  371. //
  372. // STATUS_CHANGE_ACTIVE_REQ set, then some notifications came in
  373. // while we were out. Check that we actually do have information
  374. // (it might have been sent with the last RPC) and we aren't closing.
  375. //
  376. if ((pChange->eStatus & STATUS_CHANGE_ACTIVE_REQ) &&
  377. NotifyNeeded(pChange) &&
  378. !(pChange->eStatus & STATUS_CHANGE_CLOSING)) {
  379. DBGMSG(DBG_NOTIFY, ("ThreadNotifyProcessJob: delayed link added 0x%x cRef++ %d\n",
  380. pChange,
  381. pChange->cRef));
  382. pChange->eStatus &= ~STATUS_CHANGE_ACTIVE_REQ;
  383. LinkChange(pChange);
  384. //
  385. // No need to call TMAddJob(&tmStateVar) since this
  386. // thread will pickup the job. If there is a job already waiting,
  387. // then that means it already has a thread spawning to pick it up.
  388. //
  389. }
  390. Done:
  391. //
  392. // Mark ourselves no longer in use. If we were in use when someone
  393. // tried to delete the notify, we need to delete it once we're done.
  394. //
  395. pChange->cRef--;
  396. DBGMSG(DBG_NOTIFY, ("ThreadNotifyProcessJob: Done 0x%x cRef-- %d\n",
  397. pChange,
  398. pChange->cRef));
  399. if (pChange->eStatus & STATUS_CHANGE_CLOSING) {
  400. hNotifyRemote = pChange->hNotifyRemote;
  401. pChange->hNotifyRemote = NULL;
  402. //
  403. // Free the Change struct and close the hNotifyRemote
  404. //
  405. FreeChange(pChange);
  406. EXIT_THREAD_LIST();
  407. CloseReplyRemote(hNotifyRemote);
  408. } else {
  409. EXIT_THREAD_LIST();
  410. }
  411. return 0;
  412. }
  413. //
  414. // Usually a macro
  415. //
  416. #ifndef LINKADDFAST
  417. VOID
  418. LinkAdd(
  419. PLINK pLink,
  420. PLINK* ppLinkHead)
  421. /*++
  422. Routine Description:
  423. Adds the item to linked list.
  424. Arguments:
  425. pLink - item to add
  426. ppLinkHead - linked list head pointer
  427. Return Value:
  428. VOID
  429. NOTE: This appends to the tail of the list; the macro must be changed also.
  430. --*/
  431. {
  432. //
  433. // First check if its in the list
  434. //
  435. PLINK pLinkT;
  436. PLINK pLinkLast = NULL;
  437. for(pLinkT=*ppLinkHead; pLinkT; pLinkT=pLinkT->pNext) {
  438. if (pLinkT == pLink) {
  439. DBGMSG(DBG_ERROR, ("LinkAdd: Duplicate link adding!\n"));
  440. }
  441. pLinkLast = pLinkT;
  442. }
  443. if (pLinkLast) {
  444. pLinkLast->pNext = pLink;
  445. } else {
  446. pLink->pNext = *ppLinkHead;
  447. *ppLinkHead = pLink;
  448. }
  449. }
  450. #endif
  451. VOID
  452. LinkDelete(
  453. PLINK pLink,
  454. PLINK* ppLinkHead)
  455. /*++
  456. Routine Description:
  457. Removes item from list
  458. Arguments:
  459. pLink - Item to delete
  460. ppLinkHead - pointer to link head
  461. Return Value:
  462. VOID
  463. --*/
  464. {
  465. PLINK pLink2 = *ppLinkHead;
  466. if (!pLink)
  467. return;
  468. //
  469. // Check head case first
  470. //
  471. if (pLink2 == pLink) {
  472. *ppLinkHead = pLink->pNext;
  473. } else {
  474. //
  475. // Scan list to delete
  476. //
  477. for(;
  478. pLink2;
  479. pLink2=pLink2->pNext) {
  480. if (pLink == pLink2->pNext) {
  481. pLink2->pNext = pLink->pNext;
  482. break;
  483. }
  484. }
  485. }
  486. pLink->pNext = NULL;
  487. return;
  488. }