762 lines
25 KiB
C
762 lines
25 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1990 - 1996 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
schedule.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module provides all the scheduling services for the Local Spooler
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Dave Snipp (DaveSn) 15-Mar-1991
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
Krishna Ganugapati (KrishnaG) 07-Dec-1993 - rewrote the scheduler thread to
|
||
|
gracefully kill off port threads if there are no jobs assigned to ports and
|
||
|
to recreate the port thread if the port receives a job and is without a thread.
|
||
|
|
||
|
Matthew A Felton (MattFe) June 1994 RapidPrint implemented
|
||
|
|
||
|
MattFe April 96 Chained Jobs
|
||
|
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include <precomp.h>
|
||
|
|
||
|
#define MIDNIGHT (60 * 60 * 24)
|
||
|
|
||
|
#if DBG
|
||
|
/* For the debug message:
|
||
|
*/
|
||
|
#define HOUR_FROM_SECONDS(Time) (((Time) / 60) / 60)
|
||
|
#define MINUTE_FROM_SECONDS(Time) (((Time) / 60) % 60)
|
||
|
#define SECOND_FROM_SECONDS(Time) (((Time) % 60) % 60)
|
||
|
|
||
|
/* Format for %02d:%02d:%02d replaceable string:
|
||
|
*/
|
||
|
#define FORMAT_HOUR_MIN_SEC(Time) HOUR_FROM_SECONDS(Time), \
|
||
|
MINUTE_FROM_SECONDS(Time), \
|
||
|
SECOND_FROM_SECONDS(Time)
|
||
|
|
||
|
/* Format for %02d:%02d replaceable string:
|
||
|
*/
|
||
|
#define FORMAT_HOUR_MIN(Time) HOUR_FROM_SECONDS(Time), \
|
||
|
MINUTE_FROM_SECONDS(Time)
|
||
|
#endif
|
||
|
|
||
|
|
||
|
HANDLE SchedulerSignal = NULL;
|
||
|
|
||
|
|
||
|
VOID
|
||
|
DbgPrintTime(
|
||
|
);
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
GetTimeToWait(
|
||
|
DWORD CurrentTime,
|
||
|
PINIPRINTER pIniPrinter,
|
||
|
PINIJOB pIniJob
|
||
|
);
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
GetCurrentTimeInSeconds(
|
||
|
);
|
||
|
|
||
|
|
||
|
PINIJOB
|
||
|
AssignFreeJobToFreePort(
|
||
|
PINIPORT pIniJob,
|
||
|
DWORD *pSecsToWait
|
||
|
);
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
SchedulerThread(
|
||
|
PINISPOOLER pIniSpooler
|
||
|
)
|
||
|
{
|
||
|
PINIPORT pIniPort;
|
||
|
PINIJOB pIniJob;
|
||
|
DWORD SchedulerTimeout = INFINITE; // In seconds
|
||
|
DWORD ThisPortTimeToWait; // In seconds
|
||
|
PINIPORT pIniNextPort = NULL;
|
||
|
|
||
|
for( ; ; ) {
|
||
|
|
||
|
if (SchedulerTimeout == INFINITE) {
|
||
|
|
||
|
DBGMSG(DBG_TRACE, ("Scheduler thread waiting indefinitely\n"));
|
||
|
|
||
|
} else {
|
||
|
|
||
|
DBGMSG(DBG_TRACE, ("Scheduler thread waiting for %02d:%02d:%02d\n",
|
||
|
FORMAT_HOUR_MIN_SEC(SchedulerTimeout)));
|
||
|
|
||
|
//
|
||
|
// The SchedulerTimeout is in seconds, so we need to multiply
|
||
|
// by 1000.
|
||
|
//
|
||
|
|
||
|
SchedulerTimeout *= 1000;
|
||
|
|
||
|
}
|
||
|
|
||
|
if (WaitForSingleObject(SchedulerSignal,
|
||
|
SchedulerTimeout) == WAIT_FAILED) {
|
||
|
|
||
|
DBGMSG(DBG_WARNING, ("SchedulerThread:WaitforSingleObject failed: Error %d\n",
|
||
|
GetLastError()));
|
||
|
}
|
||
|
|
||
|
/* The timeout will be reset if there are jobs to be printed
|
||
|
* at a later time. This will result in WaitForSingleObject
|
||
|
* timing out when the first one is due to be printed.
|
||
|
*/
|
||
|
|
||
|
SchedulerTimeout = INFINITE;
|
||
|
|
||
|
EnterSplSem();
|
||
|
|
||
|
pIniPort = pIniSpooler->pIniPort;
|
||
|
|
||
|
while (pIniPort) {
|
||
|
|
||
|
DBGMSG(DBG_TRACE, ("Now Processing Port %ws\n", pIniPort->pName));
|
||
|
|
||
|
SPLASSERT( pIniPort->signature == IPO_SIGNATURE );
|
||
|
|
||
|
// Check conditions based on which we can assign this
|
||
|
// port a job.
|
||
|
|
||
|
|
||
|
//
|
||
|
// Rule 1 - if there is a job being processed by this
|
||
|
// port, then leave this port alone.
|
||
|
//
|
||
|
|
||
|
if ( (pIniPort->pIniJob) &&
|
||
|
!(pIniPort->Status & PP_WAITING )){
|
||
|
|
||
|
SPLASSERT( pIniPort->pIniJob->signature == IJ_SIGNATURE );
|
||
|
|
||
|
//
|
||
|
// If this port has a job which has timed out AND
|
||
|
// there is another job waiting on this port then
|
||
|
// push the timed out job out by setting JOB_ABANDON
|
||
|
// see spooler.c LocalReadPrinter
|
||
|
|
||
|
if (( pIniPort->pIniJob->Status & JOB_TIMEOUT ) &&
|
||
|
( pIniPort->pIniJob->WaitForWrite != INVALID_HANDLE_VALUE ) &&
|
||
|
( NULL != AssignFreeJobToFreePort( pIniPort, &ThisPortTimeToWait ) )) {
|
||
|
|
||
|
pIniPort->pIniJob->Status |= JOB_ABANDON;
|
||
|
ReallocSplStr(&pIniPort->pIniJob->pStatus, szFastPrintTimeout);
|
||
|
|
||
|
LogJobInfo( pIniSpooler,
|
||
|
MSG_DOCUMENT_TIMEOUT,
|
||
|
pIniPort->pIniJob->JobId,
|
||
|
pIniPort->pIniJob->pDocument,
|
||
|
pIniPort->pIniJob->pUser,
|
||
|
pIniPort->pIniJob->pIniPrinter->pName,
|
||
|
dwFastPrintWaitTimeout );
|
||
|
|
||
|
|
||
|
SetEvent( pIniPort->pIniJob->WaitForWrite );
|
||
|
|
||
|
SetPrinterChange(pIniPort->pIniJob->pIniPrinter,
|
||
|
pIniPort->pIniJob,
|
||
|
NVJobStatusAndString,
|
||
|
PRINTER_CHANGE_SET_JOB,
|
||
|
pIniPort->pIniSpooler);
|
||
|
}
|
||
|
|
||
|
pIniPort = pIniPort->pNext;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Is there any job that can be scheduled to this port
|
||
|
//
|
||
|
|
||
|
pIniJob = AssignFreeJobToFreePort(pIniPort, &ThisPortTimeToWait);
|
||
|
|
||
|
SchedulerTimeout = min(ThisPortTimeToWait, SchedulerTimeout);
|
||
|
|
||
|
if (pIniPort->Status & PP_THREADRUNNING) {
|
||
|
if (pIniPort->Status & PP_WAITING) {
|
||
|
|
||
|
// If we are working on a Chained Job then the job has already
|
||
|
// been assigned by the port thread from the last job on this port
|
||
|
// so ignore any other job found for us
|
||
|
|
||
|
if ( pIniPort->pIniJob ) {
|
||
|
|
||
|
pIniJob = pIniPort->pIniJob;
|
||
|
|
||
|
DBGMSG( DBG_TRACE, ("ScheduleThread NextJob pIniPort %x JoId %d pIniJob %x\n",
|
||
|
pIniPort, pIniJob->JobId, pIniJob ));
|
||
|
}
|
||
|
|
||
|
if ( pIniJob ) {
|
||
|
|
||
|
DBGMSG(DBG_TRACE, ("ScheduleThread pIniJob %x Size %d pDocument %ws\n",
|
||
|
pIniJob, pIniJob->Size, pIniJob->pDocument));
|
||
|
|
||
|
|
||
|
if (pIniPort != pIniJob->pIniPort) {
|
||
|
|
||
|
++pIniPort->cJobs;
|
||
|
pIniJob->pIniPort = pIniPort;
|
||
|
}
|
||
|
|
||
|
pIniPort->pIniJob = pIniJob;
|
||
|
|
||
|
// Chained Jobs
|
||
|
// Point the Master Jobs Current Pointer to the first
|
||
|
// in the chain.
|
||
|
|
||
|
if ( pIniJob->pCurrentIniJob == NULL &&
|
||
|
pIniJob->NextJobId != 0 ) {
|
||
|
|
||
|
pIniJob->pCurrentIniJob = pIniJob;
|
||
|
}
|
||
|
|
||
|
|
||
|
pIniPort->Status &= ~PP_WAITING;
|
||
|
|
||
|
// If the job is still spooling then we will need
|
||
|
// to create an event to synchronize the port thread
|
||
|
|
||
|
if ( !( pIniJob->Status & JOB_DIRECT ) ) {
|
||
|
|
||
|
pIniJob->WaitForWrite = INVALID_HANDLE_VALUE;
|
||
|
|
||
|
if ( pIniJob->Status & JOB_SPOOLING ) {
|
||
|
|
||
|
pIniJob->WaitForWrite = CreateEvent( NULL,
|
||
|
EVENT_RESET_MANUAL,
|
||
|
EVENT_INITIAL_STATE_NOT_SIGNALED,
|
||
|
NULL );
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Update cRef so that nobody can delete this job
|
||
|
// before the Port Thread Starts up
|
||
|
|
||
|
SplInSem();
|
||
|
INCJOBREF(pIniJob);
|
||
|
|
||
|
SetEvent(pIniPort->Semaphore);
|
||
|
pIniJob->Status |= JOB_DESPOOLING;
|
||
|
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// If the port thread is running and it is waiting
|
||
|
// for a job and there is no job to assign, then
|
||
|
// kill the port thread
|
||
|
//
|
||
|
DBGMSG(DBG_TRACE, ("Now destroying the new port thread %.8x\n", pIniPort));
|
||
|
DestroyPortThread(pIniPort, FALSE);
|
||
|
|
||
|
pIniPort->Status &= ~PP_WAITING;
|
||
|
|
||
|
if (pIniPort->Status & PP_FILE) {
|
||
|
//
|
||
|
// We should destroy the Pseudo-File Port at this
|
||
|
// point. There are no jobs assigned to this Port
|
||
|
// and we are in Critical Section
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// Now deleting the pIniPort entry for the Pseudo-Port
|
||
|
//
|
||
|
|
||
|
DBGMSG(DBG_TRACE, ("Now deleting the Pseudo-Port %ws\n", pIniPort->pName));
|
||
|
|
||
|
pIniNextPort = pIniPort->pNext;
|
||
|
DBGMSG(DBG_TRACE, ("The Next Port that will be processed is %ws\n", pIniNextPort->pName));
|
||
|
|
||
|
if ( !pIniPort->cJobs )
|
||
|
DeletePortEntry(pIniPort);
|
||
|
pIniPort = pIniNextPort;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else if (!(pIniPort->Status & PP_THREADRUNNING) && pIniJob) {
|
||
|
|
||
|
//
|
||
|
// If the port thread is not running, and there is a job to
|
||
|
// assign, then create a port thread. REMEMBER do not assign
|
||
|
// the job to the port because we are in a Spooler Section and
|
||
|
// if we release the Spooler Section, the first thing the port
|
||
|
// thread does is reinitialize its pIniPort->pIniJob to NULL
|
||
|
// Wait the next time around we execute the for loop to assign
|
||
|
// the job to this port. Should we set SchedulerTimeOut to zero??
|
||
|
//
|
||
|
DBGMSG( DBG_TRACE, ("ScheduleThread Now creating the new port thread pIniPot %x\n", pIniPort));
|
||
|
CreatePortThread( pIniPort );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now go to the next port
|
||
|
//
|
||
|
|
||
|
pIniPort = pIniPort->pNext;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Leave Spooler Semaphore
|
||
|
//
|
||
|
|
||
|
LeaveSplSem();
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
PINIJOB
|
||
|
AssignFreeJobToFreePort(
|
||
|
PINIPORT pIniPort,
|
||
|
DWORD *pSecsToWait
|
||
|
)
|
||
|
/*++
|
||
|
Note: You must ensure that the port is free. This function will not
|
||
|
assign a job to this port, but if there exists one, it will return a
|
||
|
pointer to the INIJOB. Irrespective of whether it finds a job or not,
|
||
|
it will return the minimum timeout value that the scheduler thread
|
||
|
should sleep for.
|
||
|
--*/
|
||
|
{
|
||
|
DWORD CurrentTime; // Time in seconds
|
||
|
DWORD Timeout = INFINITE; // Time in seconds
|
||
|
DWORD SecsToWait; // Time in seconds
|
||
|
PINIPRINTER pTopIniPrinter, pIniPrinter;
|
||
|
PINIJOB pTopIniJob, pIniJob;
|
||
|
PINIJOB pTopIniJobOnThisPrinter, pTopIniJobSpooling;
|
||
|
DWORD i;
|
||
|
|
||
|
SplInSem();
|
||
|
|
||
|
if ( pIniPort->Status & PP_ERROR ) {
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
pTopIniPrinter = NULL;
|
||
|
pTopIniJob = NULL;
|
||
|
|
||
|
for (i = 0; i < pIniPort->cPrinters ; i++) {
|
||
|
pIniPrinter = pIniPort->ppIniPrinter[i];
|
||
|
|
||
|
//
|
||
|
// if this printer is in a state not to print skip it
|
||
|
//
|
||
|
|
||
|
if ( PrinterStatusBad(pIniPrinter->Status) ) {
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// if we haven't found a top-priority printer yet,
|
||
|
// or this printer is higher priority than the top-priority
|
||
|
// printer, see if it has jobs to go. If we find any, the
|
||
|
// highest priority one will become the top priority job and
|
||
|
// this printer will become the top-priority printer.
|
||
|
//
|
||
|
|
||
|
if (!pTopIniPrinter ||
|
||
|
(pIniPrinter->Priority > pTopIniPrinter->Priority)) {
|
||
|
|
||
|
pTopIniJobOnThisPrinter = NULL;
|
||
|
pTopIniJobSpooling = NULL;
|
||
|
pIniJob = pIniPrinter->pIniFirstJob;
|
||
|
while (pIniJob) {
|
||
|
|
||
|
if (!(pIniPort->Status & PP_FILE) &&
|
||
|
(pIniJob->Status & JOB_PRINT_TO_FILE)) {
|
||
|
pIniJob = pIniJob->pIniNextJob;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ((pIniPort->Status & PP_FILE) &&
|
||
|
!(pIniJob->Status & JOB_PRINT_TO_FILE)) {
|
||
|
pIniJob = pIniJob->pIniNextJob;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Find a job which is not PAUSED, PRINTING etc.
|
||
|
// For RapidPrint also allow SPOOLING jobs to print
|
||
|
|
||
|
if ( ( !( pIniJob->pIniPrinter->Attributes & PRINTER_ATTRIBUTE_DIRECT ) &&
|
||
|
!( pIniJob->pIniPrinter->Attributes & PRINTER_ATTRIBUTE_QUEUED ) &&
|
||
|
( pIniJob->Status & JOB_SPOOLING ) &&
|
||
|
!( pIniJob->Status & ( JOB_PAUSED | JOB_PRINTING |
|
||
|
JOB_PRINTED | JOB_TIMEOUT |
|
||
|
JOB_DESPOOLING | JOB_PENDING_DELETION |
|
||
|
JOB_BLOCKED_DEVQ | JOB_COMPOUND ))) ||
|
||
|
// OR //
|
||
|
|
||
|
!(pIniJob->Status & ( JOB_SPOOLING |
|
||
|
JOB_PAUSED | JOB_PRINTING |
|
||
|
JOB_PRINTED | JOB_TIMEOUT |
|
||
|
JOB_DESPOOLING | JOB_PENDING_DELETION |
|
||
|
JOB_BLOCKED_DEVQ | JOB_COMPOUND ))) {
|
||
|
|
||
|
//
|
||
|
// if we find such a job, then determine how much
|
||
|
// time, we need to wait before this job can actually
|
||
|
// print.
|
||
|
//
|
||
|
|
||
|
CurrentTime = GetCurrentTimeInSeconds();
|
||
|
#if DBG
|
||
|
if (MODULE_DEBUG & DBG_TIME)
|
||
|
DbgPrintTime();
|
||
|
#endif
|
||
|
SecsToWait = GetTimeToWait(CurrentTime, pIniPrinter, pIniJob);
|
||
|
|
||
|
|
||
|
if (SecsToWait == 0) {
|
||
|
|
||
|
//
|
||
|
// if we needn't wait at all, then we make this job the
|
||
|
// TopIniJob if either there is no TopIniJob or this job
|
||
|
// has a higher priority than an existing TopIniJob on this
|
||
|
// printer.
|
||
|
//
|
||
|
|
||
|
// Keep both the Highest Priority Spooling and Non
|
||
|
// spooling job in case we want to favour non spooling
|
||
|
// jobs over spooling jobs
|
||
|
|
||
|
if ( pIniJob->Status & JOB_SPOOLING ) {
|
||
|
|
||
|
if ( pTopIniJobSpooling == NULL ) {
|
||
|
|
||
|
pTopIniJobSpooling = pIniJob;
|
||
|
|
||
|
} else if ( pIniJob->pIniPrinter->Attributes & PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST ) {
|
||
|
|
||
|
//
|
||
|
// For DO_COMPLETE_FIRST we'll take larger jobs
|
||
|
// first over pure priority based
|
||
|
//
|
||
|
|
||
|
if (( pIniJob->Size > pTopIniJobSpooling->Size ) ||
|
||
|
|
||
|
(( pIniJob->Size == pTopIniJobSpooling->Size ) &&
|
||
|
( pIniJob->Priority > pTopIniJobSpooling->Priority ))) {
|
||
|
|
||
|
pTopIniJobSpooling = pIniJob;
|
||
|
|
||
|
}
|
||
|
|
||
|
// For Priority Based, pick a higher priority job if it has some
|
||
|
// at least our minimum requirement
|
||
|
|
||
|
} else if (( pIniJob->Priority > pTopIniJobSpooling->Priority ) &&
|
||
|
( pIniJob->Size >= dwFastPrintSlowDownThreshold )) {
|
||
|
|
||
|
pTopIniJobSpooling = pIniJob;
|
||
|
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
if (!pTopIniJobOnThisPrinter ||
|
||
|
(pIniJob->Priority > pTopIniJobOnThisPrinter->Priority)) {
|
||
|
|
||
|
pTopIniJobOnThisPrinter = pIniJob;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// if we have to wait then keep track of how long we
|
||
|
// can doze off before the next job that is to be
|
||
|
// scheduled later.
|
||
|
//
|
||
|
|
||
|
Timeout = min(Timeout, SecsToWait);
|
||
|
}
|
||
|
}
|
||
|
//
|
||
|
// loop thru all jobs on this printer.
|
||
|
//
|
||
|
|
||
|
pIniJob = pIniJob->pIniNextJob;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We've already established that this printer has a
|
||
|
// higher priority than any previous TopIniPrinter or
|
||
|
// that there is no TopIniPrinter yet.
|
||
|
|
||
|
// if we did find a TopIniJobOnThisPrinter for this pIniPrinter
|
||
|
// update the TopIniPrinter and TopIniJob pointers
|
||
|
//
|
||
|
|
||
|
// We don't want to schedule Spooling Jobs whose size doesn't meet
|
||
|
// our minimum size requirement
|
||
|
|
||
|
if (( pTopIniJobSpooling != NULL ) &&
|
||
|
( dwFastPrintSlowDownThreshold > pTopIniJobSpooling->Size )) {
|
||
|
|
||
|
pTopIniJobSpooling = NULL ;
|
||
|
}
|
||
|
|
||
|
if ( pTopIniJobOnThisPrinter == NULL ) {
|
||
|
|
||
|
pTopIniJobOnThisPrinter = pTopIniJobSpooling;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
// For FastPrint we can choose to favour Completed jobs over
|
||
|
// Spooling jobs
|
||
|
|
||
|
if ( !( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST ) &&
|
||
|
( pTopIniJobSpooling ) &&
|
||
|
( pTopIniJobSpooling->Priority > pTopIniJobOnThisPrinter->Priority )) {
|
||
|
|
||
|
pTopIniJobOnThisPrinter = pTopIniJobSpooling;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pTopIniJobOnThisPrinter) {
|
||
|
pTopIniPrinter = pIniPrinter;
|
||
|
pTopIniJob = pTopIniJobOnThisPrinter;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
//
|
||
|
// This ends the if clause for finding a printer with higher priority
|
||
|
// than the current TopIniPrinter. Loop back and process all printers
|
||
|
}
|
||
|
//
|
||
|
// End of For Loop for all Printers
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// if we have a TopIniJob at this stage, it means we have a job that can be
|
||
|
// assigned to the IniPort. We will return a pointer to this job back
|
||
|
|
||
|
// We will also copy the Timeout value that has been computed for this
|
||
|
// IniPort back to the SchedulerThread.
|
||
|
|
||
|
*pSecsToWait = Timeout;
|
||
|
|
||
|
return(pTopIniJob);
|
||
|
|
||
|
}
|
||
|
|
||
|
DWORD
|
||
|
GetCurrentTimeInSeconds(
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Note: This function returns a value representing the time in seconds
|
||
|
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
SYSTEMTIME st;
|
||
|
|
||
|
GetSystemTime(&st);
|
||
|
|
||
|
return ((((st.wHour * 60) + st.wMinute) * 60) + st.wSecond);
|
||
|
}
|
||
|
|
||
|
/* GetTimeToWait
|
||
|
*
|
||
|
* Determines how long it is in seconds from the current time
|
||
|
* before the specified job should be printed on the specified printer.
|
||
|
*
|
||
|
* Parameters:
|
||
|
*
|
||
|
* CurrentTime - Current system time in seconds
|
||
|
*
|
||
|
* pIniPrinter - Pointer to INIPRINTER structure for the printer.
|
||
|
* This contains the StartTime and UntilTime fields.
|
||
|
*
|
||
|
* pIniJob - Pointer to INIJOB structure for the job.
|
||
|
* This contains the StartTime and UntilTime fields.
|
||
|
*
|
||
|
* Return value:
|
||
|
*
|
||
|
* The number of seconds till the job should be printed.
|
||
|
* If the job can be printed immediately, this will be 0.
|
||
|
* We don't support specifying the day the job should be printed,
|
||
|
* so the return value should always be in the following range:
|
||
|
*
|
||
|
* 0 <= return value < 86400 (60 * 60 * 24)
|
||
|
*
|
||
|
* Remarks:
|
||
|
*
|
||
|
* The user can specify hours on both the printer and the job.
|
||
|
* Thus a printer may be configured to print only at night,
|
||
|
* say between the hours 20:00 and 06:00.
|
||
|
* Any job submitted to the printer outside those hours
|
||
|
* will not print until 20:00.
|
||
|
* If, in addition, the user specifies the hours when the job
|
||
|
* may print (e.g. through Printer Properties -> Details
|
||
|
* in Print Manager), the job will print when the two periods
|
||
|
* overlap.
|
||
|
*
|
||
|
* This routine finds the two wait periods determined by the
|
||
|
* printer hours and the job hours respectively.
|
||
|
* The actual time to wait is the longer of the two.
|
||
|
* It therefore assumes that the two periods overlap.
|
||
|
* This doesn't matter if the routine is called again
|
||
|
* when the scheduler thread wakes up again.
|
||
|
*
|
||
|
* CHANGED: 14 June 1993
|
||
|
*
|
||
|
* The printer times are now ignored.
|
||
|
* When a job is submitted it inherits the printer's hours.
|
||
|
* These are all we need to check. Now if the printer's hours
|
||
|
* are changed, any already existing jobs on that printer
|
||
|
* will still print within the originally assigned times.
|
||
|
*
|
||
|
*
|
||
|
*/
|
||
|
DWORD
|
||
|
GetTimeToWait(
|
||
|
DWORD CurrentTime,
|
||
|
PINIPRINTER pIniPrinter,
|
||
|
PINIJOB pIniJob
|
||
|
)
|
||
|
{
|
||
|
/* Printer and job start and until times are in minutes.
|
||
|
* Convert them to seconds, so that we can start printing
|
||
|
* bang on the minute.
|
||
|
*/
|
||
|
DWORD PrinterStartTime = (pIniPrinter->StartTime * 60);
|
||
|
DWORD PrinterUntilTime = (pIniPrinter->UntilTime * 60);
|
||
|
DWORD JobStartTime = (pIniJob->StartTime * 60);
|
||
|
DWORD JobUntilTime = (pIniJob->UntilTime * 60);
|
||
|
DWORD PrinterTimeToWait = 0;
|
||
|
DWORD JobTimeToWait = 0;
|
||
|
DWORD TimeToWait = 0;
|
||
|
|
||
|
/* Current time must be within the window between StartTime and UntilTime
|
||
|
* of both the printer and the job.
|
||
|
* But if StartTime and UntilTime are identical, any time is valid.
|
||
|
*/
|
||
|
|
||
|
#ifdef IGNORE_PRINTER_TIMES
|
||
|
|
||
|
if (PrinterStartTime > PrinterUntilTime) {
|
||
|
|
||
|
/* E.g. StartTime = 20:00
|
||
|
* UntilTime = 06:00
|
||
|
*
|
||
|
* This spans midnight, so check we're not in the period
|
||
|
* between UntilTime and StartTime:
|
||
|
*/
|
||
|
if ((CurrentTime < PrinterStartTime)
|
||
|
&&(CurrentTime >= PrinterUntilTime)) {
|
||
|
|
||
|
/* It's after 06:00, but before 20:00:
|
||
|
*/
|
||
|
PrinterTimeToWait = (PrinterStartTime - CurrentTime);
|
||
|
}
|
||
|
|
||
|
} else if (PrinterStartTime < PrinterUntilTime) {
|
||
|
|
||
|
/* E.g. StartTime = 08:00
|
||
|
* UntilTime = 18:00
|
||
|
*/
|
||
|
if (CurrentTime < PrinterStartTime) {
|
||
|
|
||
|
/* It's after midnight, but before printing hours:
|
||
|
*/
|
||
|
PrinterTimeToWait = (PrinterStartTime - CurrentTime);
|
||
|
|
||
|
} else if (CurrentTime >= PrinterUntilTime) {
|
||
|
|
||
|
/* It's before midnight, and after printing hours.
|
||
|
* In this case, time to wait is the period until
|
||
|
* midnight plus the start time:
|
||
|
*/
|
||
|
PrinterTimeToWait = ((MIDNIGHT - CurrentTime) + PrinterStartTime);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif /* IGNORE_PRINTER_TIMES
|
||
|
|
||
|
/* Do the same for the job time constraints:
|
||
|
*/
|
||
|
if (JobStartTime > JobUntilTime) {
|
||
|
|
||
|
if ((CurrentTime < JobStartTime)
|
||
|
&&(CurrentTime >= JobUntilTime)) {
|
||
|
|
||
|
JobTimeToWait = (JobStartTime - CurrentTime);
|
||
|
}
|
||
|
|
||
|
} else if (JobStartTime < JobUntilTime) {
|
||
|
|
||
|
if (CurrentTime < JobStartTime) {
|
||
|
|
||
|
JobTimeToWait = (JobStartTime - CurrentTime);
|
||
|
|
||
|
} else if (CurrentTime >= JobUntilTime) {
|
||
|
|
||
|
JobTimeToWait = ((MIDNIGHT - CurrentTime) + JobStartTime);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
TimeToWait = max(PrinterTimeToWait, JobTimeToWait);
|
||
|
|
||
|
DBGMSG(DBG_TRACE, ("Checking time to print %ws\n"
|
||
|
"\tCurrent time: %02d:%02d:%02d\n"
|
||
|
"\tPrinter hours: %02d:%02d to %02d:%02d\n"
|
||
|
"\tJob hours: %02d:%02d to %02d:%02d\n"
|
||
|
"\tTime to wait: %02d:%02d:%02d\n\n",
|
||
|
pIniJob->pDocument ?
|
||
|
pIniJob->pDocument :
|
||
|
L"(NULL)",
|
||
|
FORMAT_HOUR_MIN_SEC(CurrentTime),
|
||
|
FORMAT_HOUR_MIN(PrinterStartTime),
|
||
|
FORMAT_HOUR_MIN(PrinterUntilTime),
|
||
|
FORMAT_HOUR_MIN(JobStartTime),
|
||
|
FORMAT_HOUR_MIN(JobUntilTime),
|
||
|
FORMAT_HOUR_MIN_SEC(TimeToWait)));
|
||
|
|
||
|
return TimeToWait;
|
||
|
}
|
||
|
|
||
|
|
||
|
#if DBG
|
||
|
VOID DbgPrintTime(
|
||
|
)
|
||
|
{
|
||
|
SYSTEMTIME st;
|
||
|
|
||
|
GetLocalTime(&st);
|
||
|
|
||
|
DBGMSG( DBG_TIME,
|
||
|
( "Time: %02d:%02d:%02d\n", st.wHour, st.wMinute, st.wSecond ));
|
||
|
}
|
||
|
#endif
|