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.

525 lines
16 KiB

  1. /*************************************************************************
  2. * Microsoft Windows NT *
  3. * *
  4. * Copyright(c) Microsoft Corp., 1994-1997. *
  5. * *
  6. * Revision History: *
  7. * *
  8. * Jan. 24,94 Koti Created *
  9. * 03-May-97 MohsinA Performance Thread Pooling *
  10. * Description: *
  11. * *
  12. * This file contains functions that process requests from LPR clients *
  13. * *
  14. *************************************************************************/
  15. #include "lpd.h"
  16. VOID CleanupConn( PSOCKCONN pscConn);
  17. // ========================================================================
  18. //
  19. // SYNOPSIS: Thread Pooling Performance Fix.
  20. // AUTHOR: MohsinA, 25-Apr-97.
  21. // HISTORY: Boeing needs scalable lpd servers.
  22. //
  23. // Notes:
  24. // This is a worker thread
  25. // that pulls pscConn from the global queue and services them.
  26. // It is created from LoopOnAccept when there are many jobs and
  27. // too few WorkerThread(s).
  28. // WorkerThread dies when there are too many idle threads or
  29. // when shutting down.
  30. //
  31. DWORD WorkerThread( PSOCKCONN pscConn )
  32. {
  33. DWORD threadid = GetCurrentThreadId();
  34. int stayalive = 1; // bool, loop break in cs.
  35. int fIamLastThread= 0;
  36. int SLEEP_TIME = 4000; // in ms, constant per thread.
  37. int time_slept = 0; // in ms, sum.
  38. int num_jobs = 0; // ordinal sum.
  39. COMMON_LPD local_common;
  40. #ifdef PROFILING
  41. time_t time_start = time(NULL);
  42. time_t time_done = 0;
  43. #endif
  44. // We randomize the sleep time, as we don't want all the
  45. // threads to wake up together. srand must be seeded for each thread.
  46. #ifdef PROFILING
  47. srand( time_start );
  48. #endif
  49. SLEEP_TIME = 2000 + (rand() & 0x7ff); // 2000 to 4000.
  50. // We can't use this pscConn as another thread could have pulled it out.
  51. // Instead we go and pull another pscConn from the queue.
  52. pscConn = NULL;
  53. while( stayalive ){
  54. //
  55. // Shutdown after emptying the queue below.
  56. // fShuttingDownGLB will clean the job in ServiceTheClient.
  57. //
  58. EnterCriticalSection( &csConnSemGLB );
  59. {
  60. if( scConnHeadGLB.pNext ){
  61. // == Remove one from the head.
  62. pscConn = scConnHeadGLB.pNext;
  63. scConnHeadGLB.pNext = pscConn->pNext;
  64. pscConn->pNext = NULL;
  65. Common.QueueLength--;
  66. // == Remove one from the tail.
  67. // PSOCKCONN x = &scConnHeadGLB;
  68. // int count = Common.QueueLength;
  69. //
  70. // while( x->pNext->pNext ){
  71. // x = x->pNext;
  72. // --count;
  73. // assert( 0 < count );
  74. // }
  75. // pscConn = x->pNext;
  76. // Common.QueueLength--;
  77. // x->pNext = NULL;
  78. }else{
  79. //
  80. // One thread dies after 16 idle SLEEP_TIME.
  81. //
  82. if( fShuttingDownGLB || ( Common.IdleCounter > 32 ) ){
  83. Common.IdleCounter /= 2;
  84. stayalive = 0;
  85. }else{
  86. Common.IdleCounter++;
  87. }
  88. pscConn = NULL;
  89. }
  90. assert( Common.AliveThreads >= 0 );
  91. assert( Common.QueueLength >= 0 );
  92. local_common = Common; // struct copy, for readonly.
  93. }
  94. LeaveCriticalSection( &csConnSemGLB );
  95. if( pscConn )
  96. {
  97. num_jobs++;
  98. ServiceTheClient( pscConn );
  99. }
  100. else if( stayalive )
  101. {
  102. // LOGIT(( "PROFILING: thread %3d sleeping %d, IdleCounter=%d\n",
  103. // threadid, SLEEP_TIME, local_common.IdleCounter ));
  104. Sleep( SLEEP_TIME );
  105. time_slept += SLEEP_TIME;
  106. }
  107. } // while stayalive.
  108. // ====================================================================
  109. #ifdef PROFILING
  110. time_done = time(NULL);
  111. LOGIT(("PROFILING: thread %3d ends, jobs=%d, life=%d, slept=%d,\n"
  112. " AliveThreads=%d -1, MaxThreads=%d,\n"
  113. " TotalAccepts=%d, TotalErrors=%d, IdleCounter=%d\n"
  114. " Time now is %s"
  115. ,
  116. threadid, num_jobs, time_done - time_start, time_slept/1000,
  117. local_common.AliveThreads, local_common.MaxThreads,
  118. local_common.TotalAccepts, local_common.TotalErrors,
  119. local_common.IdleCounter,
  120. ctime(&time_done)
  121. ));
  122. #else
  123. LOGIT(("WorkerThread: thread %3d ends.\n", threadid ));
  124. #endif
  125. EnterCriticalSection( &csConnSemGLB );
  126. {
  127. Common.AliveThreads--;
  128. fIamLastThread = (Common.AliveThreads < 1 );
  129. }
  130. LeaveCriticalSection( &csConnSemGLB );
  131. if( fIamLastThread && fShuttingDownGLB ){
  132. LOGIT(("WorkerThread: Last worker thread exiting\n"));
  133. SetEvent( hEventLastThreadGLB );
  134. }
  135. return NO_ERROR; // Thread Ends.
  136. }
  137. /*
  138. ****************************************************************************
  139. * *
  140. * ServiceTheClient(): *
  141. * This function reads and interprets the request from the LPR client and *
  142. * takes appropriate action. In that sense, this routine is the heart of *
  143. * LPD service. *
  144. * *
  145. * Returns: *
  146. * NO_ERROR (always) *
  147. * *
  148. * Parameters: *
  149. * pscConn (IN-OUT): pointer to SOCKCONN structure for this connection *
  150. * *
  151. * History: *
  152. * Jan.24, 94 Koti Created *
  153. * *
  154. ****************************************************************************
  155. */
  156. DWORD ServiceTheClient( PSOCKCONN pscConn )
  157. {
  158. DWORD dwErrcode;
  159. DWORD dwResponse;
  160. CHAR chCmdCode;
  161. DWORD threadid = GetCurrentThreadId();
  162. pscConn->fLogGenericEvent = TRUE;
  163. pscConn->dwThread = threadid;
  164. pscConn->hPrinter = (HANDLE)INVALID_HANDLE_VALUE;
  165. #ifdef PROFILING
  166. pscConn->time_start = time(NULL);
  167. #endif
  168. if ( fShuttingDownGLB ){
  169. LOGIT(("ServiceTheClient: Thread %3d shutting down.\n", threadid ));
  170. goto ServiceTheClient_BAIL;
  171. }
  172. // who are we connected to?
  173. GetClientInfo( pscConn );
  174. //
  175. // get server ip address, since print clustering allows one
  176. // node to have multiple ip addresses. Depending on the ip
  177. // address, we'll go to different sets of print queues on the node.
  178. //
  179. // Albert Ting cluster change, MohsinA, 07-Mar-97.
  180. //
  181. GetServerInfo( pscConn );
  182. // get command from the client
  183. // ----------------- command 02 => "Receive Job"
  184. // | 02 | Queue LF | Queue => Queue or Printer to print on
  185. // -----------------
  186. if ( GetCmdFromClient( pscConn ) != NO_ERROR )
  187. {
  188. // didn't get a command from client: it's bad news!
  189. LPD_DEBUG( "GetCmdFromClient() failed in ServiceTheClient()!\n" );
  190. goto ServiceTheClient_BAIL;
  191. }
  192. // get name of the queue (printer) from the command. If it's not
  193. // formatted properly, quit!
  194. if ( !ParseQueueName( pscConn ) )
  195. {
  196. PCHAR aszStrings[2]={ pscConn->szIPAddr, NULL };
  197. LpdReportEvent( LPDLOG_BAD_FORMAT, 1, aszStrings, 0 );
  198. pscConn->fLogGenericEvent = FALSE;
  199. LPD_DEBUG( "ParseQueueName() failed in ServiceTheClient()!\n" );
  200. goto ServiceTheClient_BAIL;
  201. }
  202. // ====================================================================
  203. chCmdCode = pscConn->pchCommand[0];
  204. switch( chCmdCode )
  205. {
  206. case LPDC_RECEIVE_JOB:
  207. pscConn->wState = LPDS_RECEIVE_JOB;
  208. ProcessJob( pscConn );
  209. CleanupConn( pscConn );
  210. if ( pscConn->wState != LPDS_ALL_WENT_WELL )
  211. {
  212. AbortThisJob( pscConn );
  213. if ( pscConn->fLogGenericEvent )
  214. {
  215. PCHAR aszStrings[2]={ pscConn->szIPAddr, NULL };
  216. LpdReportEvent( LPDLOG_DIDNT_WORK, 1, aszStrings, 0 );
  217. }
  218. }
  219. if (pscConn->fMustFreeLicense)
  220. {
  221. NtLSFreeHandle(pscConn->LicenseHandle);
  222. }
  223. break;
  224. case LPDC_RESUME_PRINTING:
  225. pscConn->wState = LPDS_RESUME_PRINTING;
  226. if ( fAllowPrintResumeGLB )
  227. {
  228. dwResponse = ( ResumePrinting( pscConn ) == NO_ERROR ) ?
  229. LPD_ACK : LPD_NAK;
  230. }
  231. else
  232. {
  233. dwResponse = LPD_NAK;
  234. }
  235. dwErrcode = ReplyToClient( pscConn, (WORD)dwResponse );
  236. break;
  237. case LPDC_SEND_SHORTQ:
  238. case LPDC_SEND_LONGQ:
  239. pscConn->wState = LPDS_SEND_LONGQ;
  240. if ( ParseQueueRequest( pscConn, FALSE ) != NO_ERROR )
  241. {
  242. LPD_DEBUG( "ServiceTheClient(): ParseQueueRequest() failed\n" );
  243. break;
  244. }
  245. SendQueueStatus( pscConn, LPD_LONG );
  246. break;
  247. case LPDC_REMOVE_JOBS:
  248. if ( !fJobRemovalEnabledGLB )
  249. {
  250. break;
  251. }
  252. pscConn->wState = LPDS_REMOVE_JOBS;
  253. if ( ParseQueueRequest( pscConn, TRUE ) != NO_ERROR )
  254. {
  255. LPD_DEBUG( "ServiceTheClient(): ParseQueueRequest() failed\n" );
  256. break;
  257. }
  258. if ( RemoveJobs( pscConn ) == NO_ERROR )
  259. {
  260. ReplyToClient( pscConn, LPD_ACK );
  261. }
  262. break;
  263. default:
  264. break;
  265. }
  266. // ====================================================================
  267. if ( pscConn->wState != LPDS_ALL_WENT_WELL ){
  268. goto ServiceTheClient_BAIL;
  269. }
  270. #ifdef PROFILING
  271. pscConn->time_done = time(NULL);
  272. LOGIT(("PROFILING: ok, thread %3d, time_queued %s"
  273. " wait=%d, work=%d\n",
  274. threadid,
  275. ctime(&pscConn->time_queued),
  276. pscConn->time_start - pscConn->time_queued,
  277. pscConn->time_done - pscConn->time_start
  278. ));
  279. #endif
  280. // close the connection down and terminate the thread
  281. TerminateConnection( pscConn );
  282. pscConn = NULL;
  283. return NO_ERROR;
  284. // ====================================================================
  285. // if we reached here, then a non-recoverable error occured somewhere:
  286. // try to inform the client (by sending a NAK) and terminate the thread
  287. ServiceTheClient_BAIL:
  288. #ifdef PROFILING
  289. pscConn->time_done = time(NULL);
  290. LOGIT(("PROFILING: bail, thread %3d, job times %8d, wait=%d work=%d\n",
  291. threadid,
  292. pscConn->time_queued,
  293. pscConn->time_start - pscConn->time_queued,
  294. pscConn->time_done - pscConn->time_start
  295. ));
  296. #endif
  297. LPD_DEBUG( "Reached ServiceTheClient_BAIL.\n" );
  298. ReplyToClient( pscConn, LPD_NAK );
  299. TerminateConnection( pscConn );
  300. pscConn = NULL;
  301. return NO_ERROR;
  302. } // end ServiceTheClient()
  303. /*****************************************************************************
  304. * *
  305. * TerminateConnection(): *
  306. * This function releases all the memory that was allocated while *
  307. * processing the client's requests, closes the printer, closes the *
  308. * socket connection, removes its structure (pscConn) from the global *
  309. * linked list and frees the memory allocated for pscConn itself. *
  310. * Also, if the main thread is waiting on this thread for shutdown then *
  311. * this function sets hEventLastThreadGLB event to tell the main thread *
  312. * that this thread is done. *
  313. * *
  314. * Returns: *
  315. * Nothing *
  316. * *
  317. * Parameters: *
  318. * pscConn (IN-OUT): pointer to SOCKCONN structure for this connection *
  319. * *
  320. * History: *
  321. * Jan.24, 94 Koti Created *
  322. * *
  323. *****************************************************************************/
  324. VOID TerminateConnection( PSOCKCONN pscConn )
  325. {
  326. // PSOCKCONN pscCurrent;
  327. // BOOL fIamLastThread=FALSE;
  328. // it should never be NULL at this point! But check it anyway!
  329. if ( pscConn == (PSOCKCONN) NULL )
  330. {
  331. LPD_DEBUG( "TerminateConnection(): pscConn NULL at entry\n" );
  332. return;
  333. }
  334. ShutdownPrinter( pscConn );
  335. if ( pscConn->hPrinter != (HANDLE)INVALID_HANDLE_VALUE )
  336. {
  337. LPD_DEBUG( "TerminateConnection: hPrinter not closed\n" );
  338. }
  339. // close the socket
  340. if ( pscConn->sSock != INVALID_SOCKET )
  341. {
  342. SureCloseSocket( pscConn->sSock );
  343. }
  344. //
  345. // release memory in every field of the structure
  346. //
  347. if ( pscConn->pchCommand != NULL )
  348. LocalFree( pscConn->pchCommand );
  349. if ( pscConn->pchPrinterName != NULL )
  350. LocalFree( pscConn->pchPrinterName );
  351. //
  352. // no memory was allocated for ppchUsers[] and adwJobIds[]. They just
  353. // pointed to parts of what's freed by ( pscConn->pchCommand ) above.
  354. //
  355. if ( pscConn->pqStatus != NULL )
  356. LocalFree( pscConn->pqStatus );
  357. // EnterCriticalSection( &csConnSemGLB );
  358. // {
  359. //
  360. // if( Common.AliveThreads <= 1 ){
  361. // fIamLastThread = TRUE;
  362. // }
  363. //
  364. // //
  365. // // // remove this structure from the link
  366. // //
  367. // // pscCurrent = &scConnHeadGLB;
  368. // //
  369. // // while( pscCurrent ){
  370. // // if (pscConn == pscCurrent->pNext)
  371. // // break;
  372. // // pscCurrent = pscCurrent->pNext;
  373. // //
  374. // // // what if we can't find our pscConn in the list at all?
  375. // // // this should NEVER ever happen, but good to check!
  376. // //
  377. // // if( pscCurrent == NULL)
  378. // // {
  379. // // LocalFree( pscConn );
  380. // // LPD_DEBUG( "TerminateConnection(): "
  381. // // "couldn't find pscConn "
  382. // // " in the list!\n" );
  383. // // LeaveCriticalSection( &csConnSemGLB );
  384. // // return;
  385. // // }
  386. // // }
  387. // // pscCurrent->pNext = pscConn->pNext;
  388. // }
  389. // LeaveCriticalSection( &csConnSemGLB );
  390. memset( pscConn, 0, sizeof( SOCKCONN ) );
  391. LocalFree( pscConn );
  392. // //
  393. // // if shutdown is in progress and we are the last active thread, tell
  394. // // the poor main thread (blocked for us to finish) that we're done!
  395. // //
  396. //
  397. // if( fIamLastThread && fShuttingDownGLB ){
  398. // LOGIT(("TerminateConnection: Last worker thread exiting\n"));
  399. // SetEvent( hEventLastThreadGLB );
  400. // }
  401. return;
  402. }