Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

639 lines
17 KiB

/*
Microsoft Mosaic
Copyright 1995 Microsoft, Inc.
All Rights Reserved
Create 1/30/95 Chris Franklin CMF
*/
#include "all.h"
#include "safestrm.h"
#include "decoder.h"
#include "stdio.h"
#ifdef FEATURE_IMG_THREADS
// after this much time (2 minutes) w/o request thread asks to be terminated
#define BORED_TIMEOUT (120000)
// used to pass back WVM_DCSTATUS message to main thread.
// defined by DC_Init() and must stay defined until DC_Deinit()
static HWND hwndDCNotify = NULL;
// list of reserved decoders (used to control how many concurrent connections)
static PDECODER ReservedList = NULL;
// list of dynamically created decoders (needed when URL is followed and produces
// a mime type that must be decoded) . this differs from inline IMG, where we know
// before fetch and are controlling how many connections we concurrently launched
static PDECODER ForcedList = NULL;
// uniqueID used to indentify decoder between main thread and win32 decoder threads
// incremented for every request
static unsigned long cbRequestID = 0;
// Initializes data structures needed for managing decoder threads. doesn't actually
// launch thread for a given decoder until first DC_Start.
enum GuitErrs cbDC_Init(int cbMaxDecodeThreads,HWND hwndNotify)
{
enum GuitErrs result = errNoError;
PDECODER plast = NULL;
PDECODER pdecoder;
int i;
hwndDCNotify = hwndNotify;
for (i = 0;i < cbMaxDecodeThreads;i++)
{
pdecoder = GTR_MALLOC(sizeof(DECODER));
if (pdecoder == NULL)
{
result = errOUTOFMEM;
goto exitPoint;
}
memset(pdecoder,0,sizeof(DECODER));
if (plast) plast->pnext = pdecoder;
else ReservedList = pdecoder;
if (bSS_New(&pdecoder->ssInput))
{
result = errResourceUnavailable;
goto exitPoint;
}
plast = pdecoder;
}
exitPoint:
if (result != errNoError) DC_Deinit();
return result;
}
// Deintializes and frees list of DECODER's
static void deinitList(PDECODER plist)
{
PDECODER pnext;
PDECODER pdecoder;
XX_DMsg(DBG_IMAGE, ("deinitList in Decoder.c\n"));
pdecoder = plist;
while(pdecoder)
{
pnext = pdecoder->pnext;
if (pdecoder->hThread != NULL)
{
HANDLE hTmpThread = pdecoder->hThread;
pdecoder->hThread = NULL;
SetEvent(pdecoder->evRequest);
CloseHandle(hTmpThread);
XX_DMsg(DBG_IMAGE, ("deinitList closed hThread (0x%x)\n", hTmpThread));
// TRACE_OUT(("TerminateThread( 0x%lx, 0 ) in deinitList\n",pdecoder->hThread));
// TerminateThread(pdecoder->hThread,0);
}
if (pdecoder->evRequest != NULL) CloseHandle(pdecoder->evRequest);
SS_Free(&pdecoder->ssInput);
GTR_FREE(pdecoder);
pdecoder = pnext;
}
}
// FilterProc for the unblock conditionally on decoder being available
static boolean ReserveFilter(ThreadID theThread,void *context)
{
struct Mwin *tw = Async_GetWindowFromThread(theThread);
if (tw && TW_GETBLOCKED(tw,TW_DECODERBLOCKED))
{
TW_CLEARBLOCKED(tw,TW_DECODERBLOCKED);
return TRUE;
}
return FALSE;
}
// marks a decoder as available
void makeAvailable(PDECODER self)
{
self->pAbortProc = NULL;
self->pCompletionProc = NULL;
self->pRequestProc = NULL;
self->pUpdateProc = NULL;
self->pStretchProc = NULL;
self->pImgUpdateProc = NULL;
self->status = DC_Waiting;
self->tw = NULL;
self->pOutput = NULL;
self->cbRequestID = 0;
self->bCoversImg = FALSE;
Async_UnblockConditionally(ReserveFilter,NULL);
}
// Deintializes and frees structures needed to manage decoder threads
void DC_Deinit()
{
deinitList(ReservedList);
ReservedList = NULL;
deinitList(ForcedList);
ForcedList = NULL;
}
static PDECODER pGetFromList(PDECODER plist,struct Mwin *tw,BOOL bConservative)
{
PDECODER pdecoder = plist;
int cbCount = 0;
int cbWaiting = 0;
PDECODER pfree = NULL;
while (pdecoder)
{
if (pdecoder->status == DC_Waiting)
{
pfree = pdecoder;
if (!bConservative) goto exitPoint;
cbWaiting++;
}
cbCount++;
pdecoder = pdecoder->pnext;
}
if (cbCount != cbWaiting) pfree = NULL;
exitPoint:
if (pfree)
{
pfree->status = DC_Reserved;
pfree->cbRequestID = ++cbRequestID;
if(pfree->cbRequestID == 0) pfree->cbRequestID = ++cbRequestID;
pfree->tw = tw;
}
return pfree;
}
// Tries to allocate a DECODER first from ReservedList, then from ForcedList.
// If that fails,allocates a new decoder and places it on the forcedlist. this never
// blocks and should be used only for out-of-line images for which we
// already have a connection.
PDECODER pDC_ForceDecoder(struct Mwin *tw)
{
PDECODER plast = NULL;
PDECODER pdecoder;
pdecoder = pGetFromList(ReservedList,tw,FALSE);
if (pdecoder) goto exitPoint;
pdecoder = pGetFromList(ForcedList,tw,FALSE);
if (pdecoder) goto exitPoint;
pdecoder = GTR_MALLOC(sizeof(DECODER));
if (pdecoder == NULL) goto exitPoint;
memset(pdecoder,0,sizeof(DECODER));
if (bSS_New(&pdecoder->ssInput))
{
GTR_FREE(pdecoder);
pdecoder = NULL;
goto exitPoint;
}
if (ForcedList) ForcedList->pnext = pdecoder;
else ForcedList = pdecoder;
exitPoint:
return pdecoder;
}
// Attempts to reserve a DECODER from the ReservedList. If none are available
// blocks the current thread and sets tw->bDecoderBlocked. The async thread will
// be unblocked when a reserved decoder becomes available. NOTE: if there are
// more than two async threads blocking on a decoder, they are all unblocked. the
// first to run will get the decoder, and the others thus tolerate repeated blocks
// if bConservative is TRUE, only reserves on DECODER at a time.
PDECODER pDC_Reserve(struct Mwin *tw, BOOL bConservative)
{
PDECODER pdecoder = pGetFromList(ReservedList,tw,bConservative);
// Do available decoders - block the async thread
if (pdecoder == NULL)
{
Async_BlockThread(Async_GetCurrentThread());
TW_SETBLOCKED(tw,TW_DECODERBLOCKED);
}
return pdecoder;
}
// Called by main thread when it must block until the next completion message from
// the win32 thread (currently one message when Width and Height are known and
// one on completion). the thread will reblock if the status message
// is not a completion, but has an opportunity to react to progressive
// status messages (eg W & H known, later progressive draw)
// If status is DC_Complete, calls the completion routine that has been installed to
// complete those steps of decoding that should be done in the main thread.
// If status is not DC_Complete, the current thread is blocked.
// Returns: current status
DECODERSTATUS cbDC_BlockOnCompletion(PDECODER self,enum GuitErrs *pErrorCode)
{
DECODERSTATUS status = self->status;
// on completion we call the completion procedure. we clear the completion and
// abort proc so they won't be inadvertantly called. we set self->status to DC_Waiting
// so it can be reused and unblock any async threads blocked on reserving a decoder.
if (status == DC_Complete)
{
*pErrorCode = self->pCompletionProc ? (*self->pCompletionProc)(self):errNoError;
makeAvailable(self);
}
else if (status == DC_Aborting)
{
// It is an internal error to call DC_BlockOnCompletion while aborting
#ifdef XX_DEBUG
XX_Assert((0), ("Block on Complete called while aborting\n"));
#endif
}
else
{
ThreadID theThread = Async_GetCurrentThread();
struct Mwin *tw = Async_GetWindowFromThread(theThread);
#ifdef XX_DEBUG
if (self->tw == NULL)
{
XX_Assert((0), ("self->tw NULL in cbDC_BlockOnCompletion\n"));
}
#endif
tw->blockedOn = self;
Async_BlockThread(theThread);
}
return status;
}
// FilterProc for the unblock conditionally on decoder completing
static boolean CompleteFilter(ThreadID theThread,void *context)
{
struct Mwin *tw = Async_GetWindowFromThread(theThread);
if (tw && tw->blockedOn == context)
{
tw->blockedOn = NULL;
return TRUE;
}
return FALSE;
}
// Called by main thread to signal the win32 thread that the request is to be aborted.
// No other main thread should reference decoder after this point.
void DC_Abort(PDECODER self)
{
if (self->status < DC_Active) makeAvailable(self);
else if (self->status == DC_Complete)
{
if (self->pAbortProc) (*self->pAbortProc)(self);
makeAvailable(self);
}
else self->status = DC_Aborting;
self->tw = NULL;
self->pCompletionProc = NULL;
if (self->status == DC_Waiting)
Async_UnblockConditionally(CompleteFilter,self);
}
// Called by win32 thread to post a status message to main thread
void DC_PostStatus(PDECODER self,DECODERSTATUS status)
{
if (hwndDCNotify)
{
while (!PostMessage(hwndDCNotify,WVM_DCSTATUS,status,self->cbRequestID))
{
#ifdef XX_DEBUG
XX_DMsg(DBG_IMAGE, ("Cannot PostMessage, sleeping...\n"));
#endif
Sleep(100);
}
}
}
// finds a DECODER based on cbRequestID on the given list
// returns PDECODER or NULL, if none found
static PDECODER pFindByRequestID(PDECODER plist,unsigned long cbRequestID)
{
while (plist)
{
if (plist->cbRequestID == cbRequestID) return plist;
plist = plist->pnext;
}
return NULL;
}
// finds a DECODER that is DC_Waiting and has thread
static PDECODER pFindBored(PDECODER plist,unsigned long cbRequestID)
{
while (plist)
{
if (plist->status == DC_Waiting && plist->hThread != NULL) return plist;
plist = plist->pnext;
}
return NULL;
}
// Called by the main thread in response to a WVM_DCSTATUS message with the
// LPARAM and WPARAM fields of the message. unblocks any async thread waiting
// on a completion message. the thread will reblock if the status message
// is not a completion, but will has an opportunity to react to progressive
// status messages (eg W & H known, later progressive draw)
void DC_DoStatusMessage(WPARAM wParam, LPARAM lParam)
{
DECODERSTATUS status = (DECODERSTATUS) wParam;
PDECODER pdecoder;
// First, we call ReplyMessage to let the thread run.
// We call pAbortProc here, since async_thread may be long gone.
ReplyMessage(0);
#ifdef XX_DEBUG
XX_DMsg(DBG_IMAGE, ("status %ld for request %ld...\n",(unsigned long) wParam,(unsigned long) lParam));
#endif
if (status != DC_Bored)
{
pdecoder = pFindByRequestID(ReservedList,(unsigned long) lParam);
if (pdecoder == NULL)
pdecoder = pFindByRequestID(ForcedList,(unsigned long) lParam);
}
else
{
pdecoder = pFindBored(ReservedList,(unsigned long) lParam);
if (pdecoder == NULL)
pdecoder = pFindBored(ForcedList,(unsigned long) lParam);
}
if (pdecoder != NULL)
{
if (status == DC_Bored)
{
HANDLE hTmpThread = pdecoder->hThread;
pdecoder->hThread = NULL;
SetEvent(pdecoder->evRequest);
CloseHandle(hTmpThread);
XX_DMsg(DBG_IMAGE, ("closed thread (0x%x) in DC_DoStatusMessage\n", hTmpThread));
// TRACE_OUT(("TerminateThread( 0x%lx, 0 ) in DC_DoStatusMessage\n",pdecoder->hThread));
// TerminateThread(pdecoder->hThread,0);
// pdecoder->hThread = NULL;
}
else
{
if (pdecoder->status != DC_Aborting) pdecoder->status = status;
Async_UnblockConditionally(CompleteFilter,pdecoder);
if (pdecoder->status == DC_Aborting && status == DC_Complete)
{
if (pdecoder->pAbortProc) (*pdecoder->pAbortProc)(pdecoder);
makeAvailable(pdecoder);
}
}
}
}
// This is the body of the thread. waits for a new pRequestProc, then
// executes it and loops back. must not be run until self->evRequest is
// created in main thread.
DWORD WINAPI ThreadProc(PDECODER self)
{
DWORD dwResult;
while(TRUE)
{
dwResult = WaitForSingleObject(self->evRequest,BORED_TIMEOUT);
if (!self->hThread)
{
XX_DMsg(DBG_IMAGE, ("suicide thread (0x%x) in ThreadProc\n", self->hThread));
return(1);
}
if (dwResult == WAIT_ABANDONED)
{
XX_DMsg(DBG_IMAGE, ("abandoned thread (0x%x) in ThreadProc\n", self->hThread));
return(2);
}
if ((int)self->hThread < 1)
{
XX_DMsg(DBG_IMAGE, ("BOGUS thread (0x%x) in ThreadProc\n", self->hThread));
return(69);
}
XX_DMsg(DBG_IMAGE, ("ThreadProc(0x%x), dwResult=0x%x \n", self->hThread, dwResult));
if (dwResult == WAIT_TIMEOUT)
{
DC_PostStatus(self,DC_Bored);
}
else
{
if (self->pRequestProc) self->result = (*self->pRequestProc)(self);
DC_PostStatus(self,DC_Complete);
}
}
return 0;
}
// Dispatches a win32 thread to execute a decoder procedure. if this
// decoder already has a thread, it is reused, if not, we fire one up.
// creation of evRequest is also deferred to first use.
enum GuitErrs cbDC_Start(PDECODER self,PDECODERPROC pRequestProc)
{
enum GuitErrs result = errNoError;
DWORD idThread;
if (self->status != DC_Reserved)
{
result = errDCInUse;
goto exitPoint;
}
if (self->evRequest == NULL)
{
self->evRequest = CreateEvent(NULL, // Default security
FALSE,// Auto reset
FALSE,// Initially not signaled
NULL);// No name
if (self->evRequest == NULL)
{
result = errResourceUnavailable;
goto exitPoint;
}
}
if (self->hThread == NULL)
{
self->hThread = CreateThread(NULL, // Default security
0x1000, // Initial Stack size
ThreadProc, // Thread routine
self, // Thread variable
0, // Creation flags
&idThread); // Thread ID
if (self->hThread == NULL)
{
result = errResourceUnavailable;
goto exitPoint;
}
#if 0
SetThreadPriority(self->hThread,THREAD_PRIORITY_BELOW_NORMAL);
#endif
#ifdef XX_DEBUG
XX_DMsg(DBG_IMAGE, ("launching decoder thread %ld (%lx)...\n",(unsigned long) self->hThread,idThread));
#endif
}
self->status = DC_Active;
self->pRequestProc = pRequestProc;
if (!SetEvent(self->evRequest))
{
result = errInternalError;
goto exitPoint;
}
exitPoint:
if (result != errNoError)
{
self->status = DC_Complete;
self->result = result;
}
return result;
}
// Sets completion proc address
void DC_SetCompletion(PDECODER self, PDECODERPROC pCompletionProc)
{
self->pCompletionProc = pCompletionProc;
}
// Sets abort proc address
void DC_SetAbort(PDECODER self, PDECODERPROC pAbortProc)
{
self->pAbortProc = pAbortProc;
}
// Sets output structure pointer. called by win32 thread to create.
// and main thread via completion and abort proc's to reset after
// free.
void DC_SetOutput(PDECODER self, void *pOutput)
{
self->pOutput = pOutput;
}
// Gets the output structure pointer. reader should only access W & H
// fields after DC_WHKnown. should only access decoded data after
// DC_Complete
void *pDC_GetOutput(PDECODER self)
{
return self->pOutput;
}
// Gets the SAFESTREAM bound to the decoder
PSAFESTREAM pDC_GetStream(PDECODER self)
{
return &(self->ssInput);
}
// Sets the result of the request
void DC_SetResult(PDECODER self, enum GuitErrs result)
{
self->result = result;
}
// Gets the result of the operation
enum GuitErrs cbDC_GetResult(PDECODER self)
{
return self->result;
}
// Sets the request pointer
// Gets the status of the operation
DECODERSTATUS cbDC_GetStatus(PDECODER self)
{
return self->status;
}
// Gets the requestid of the operation
unsigned long cbDC_GetRequestID(PDECODER self)
{
return self ? self->cbRequestID:0;
}
// Sets updaterect proc address
void DC_SetUpdate(PDECODER self, PDCUPDATEPROC pUpdateProc)
{
self->pUpdateProc = pUpdateProc;
}
// Sets StretchDIBits proc address
void DC_SetStretch(PDECODER self, PDCSTRETCHPROC pStretchProc)
{
self->pStretchProc = pStretchProc;
}
// Sets updaterect proc address
void DC_SetImgUpdate(PDECODER self, PDCIMGUPDATEPROC pImgUpdateProc)
{
self->pImgUpdateProc = pImgUpdateProc;
}
// Gets updaterect proc address
PDCIMGUPDATEPROC pDC_GetImgUpdate(PDECODER self)
{
return self->pImgUpdateProc;
}
// Performs a StretchDIBits for progressive draw (deals with
// only some of the data being available etc
int cbDC_StretchDIBits(
PDECODER self,
HDC hdc, // handle of device context
int XDest, // x-coordinate of upper-left corner of dest. rect.
int YDest, // y-coordinate of upper-left corner of dest. rect.
int nDestWidth, // width of destination rectangle
int nDestHeight, // height of destination rectangle
int XSrc, // x-coordinate of upper-left corner of source rect.
int YSrc, // y-coordinate of upper-left corner of source rect.
int nSrcWidth, // width of source rectangle
int nSrcHeight, // height of source rectangle
UINT iUsage, // usage
DWORD dwRop, // raster operation code
PDIBENV pdibenv // DIBENV for draw
)
{
#ifdef XX_DEBUG
if (!((self->status==DC_ProgDraw || self->status==DC_Complete) && self->pStretchProc))
{
XX_Assert((0), ("unexpected state in cbDC_StretchDIBits\n"));
}
#endif
return (*self->pStretchProc)(self,hdc,XDest,YDest,nDestWidth,nDestHeight,XSrc,YSrc,nSrcWidth,nSrcHeight,iUsage,dwRop,pdibenv);
}
// Performs an invalidate of rectangles changed between logicalRow0
// and logicalRowN
void DC_UpdateRect(PDECODER self,struct Mwin *tw, RECT *r,int logicalRow0,int logicalRowN)
{
#ifdef XX_DEBUG
if (!((self->status==DC_ProgDraw || self->status==DC_Complete) && self->pUpdateProc))
{
XX_Assert((0), ("unexpected state in DC_UpdateRect\n"));
}
#endif
(*self->pUpdateProc)(self,tw,r,logicalRow0,logicalRowN);
}
// Returns PROGDRAWSTATUS for self w/re progressive cbDC_StretchDIBits
PROGDRAWSTATUS cbDC_ProgDrawValid(PDECODER self)
{
if (self->pStretchProc && (self->status == DC_ProgDraw || self->status == DC_Complete))
{
return (self->bCoversImg ? DC_ProgTotal:DC_ProgPartial);
}
else
{
return DC_ProgNot;
}
}
// Declares data now allows drawing to cover whole image w/re progressive cbDC_StretchDIBits
void DC_SetCoversImg(PDECODER self)
{
self->bCoversImg = TRUE;
}
#endif