|
|
/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
view.cxx
Abstract:
Functions to manage VIEW 'object's
Contents: CreateView (DestroyView) FindViewByHandle ReferenceView DereferenceView DereferenceViewByHandle
Author:
Richard L Firth (rfirth) 17-Oct-1994
Environment:
Win/32 user-mode DLL
Revision History:
17-Oct-1994 rfirth Created
--*/
#include <wininetp.h>
#include "gfrapih.h"
//
// private prototypes
//
PRIVATE VOID DestroyView( IN LPVIEW_INFO ViewInfo );
//
// private data
//
DEBUG_DATA(LONG, NumberOfViews, 0);
//
// functions
//
LPVIEW_INFO CreateView( IN LPSESSION_INFO SessionInfo, IN VIEW_TYPE ViewType, IN LPSTR Request, OUT LPDWORD Error, OUT LPBOOL Cloned )
/*++
Routine Description:
Creates or clones a VIEW_INFO. There will only ever be one VIEW_INFO for a particular request to the same server. Other requests for the same data just reference the first view
Arguments:
SessionInfo - pointer to SESSION_INFO describing gopher server
ViewType - type of view - ViewTypeFile or ViewTypeFind
Request - gopher request string
Error - returned error
Cloned - returned TRUE if we cloned the view
Return Value:
LPVIEW_INFO Success - pointer to created or cloned view; check Cloned
Failure - NULL
--*/
{ LPVIEW_INFO viewInfo; DWORD error;
DEBUG_ENTER((DBG_GOPHER, Pointer, "CreateView", "%x, %x, %q, %x, %x", SessionInfo, ViewType, Request, Error, Cloned ));
//
// in both cases, we need a new VIEW_INFO
//
viewInfo = NEW(VIEW_INFO); if (viewInfo != NULL) { error = AllocateHandle((LPVOID)viewInfo, &viewInfo->Handle); if (error == ERROR_SUCCESS) { viewInfo->Request = NEW_STRING(Request); viewInfo->RequestLength = strlen(Request); if (viewInfo->Request != NULL) { InitializeListHead(&viewInfo->List); viewInfo->ViewType = ViewType; } else { error = ERROR_NOT_ENOUGH_MEMORY; } } } else { error = ERROR_NOT_ENOUGH_MEMORY; }
//
// now search for an already existing view
//
if (error == ERROR_SUCCESS) {
PLIST_ENTRY listPtr; PLIST_ENTRY list; PLIST_ENTRY listAddress; BOOL found; LPBUFFER_INFO bufferInfo;
AcquireViewLock(SessionInfo, ViewType);
//
// new scheme: we now only buffer Find views: we always create a new
// file request because file requests go straight to the user buffer
// (keeping the connection alive until the caller finishes reading)
//
found = FALSE; if (ViewType == ViewTypeFind) { // list = SessionInfo->FindList.List.Flink;
listAddress = &SessionInfo->FindList.List; // for (listPtr = list; listPtr != listAddress; listPtr = listPtr->Flink) {
// if (!STRICMP(((LPVIEW_INFO)listPtr)->Request, Request)) {
// found = TRUE;
// break;
// }
// }
} else { listAddress = &SessionInfo->FileList.List; }
//
// create a buffer info, or get a pointer to the current buffer info
// depending on whether we located a view of the same request at the
// same server
//
/* N.B. found is never true because the above code is commented out. */ if (found) { // bufferInfo = ((LPVIEW_INFO)listPtr)->BufferInfo;
//
// this is a clone. If there is a thread which is concurrently
// requesting the data - right now - then we must wait on the
// RequestEvent until gives us the green light. If there is no
// RequestEvent then the data has already been received and
// some kind soul has seen fit to close the request handle. I.e.
// we don't have to wait
//
// if (bufferInfo->RequestEvent != NULL) {
// ++bufferInfo->RequestWaiters;
// }
*Cloned = TRUE; } else { bufferInfo = CreateBuffer(&error); if (bufferInfo != NULL) { *Cloned = FALSE; } }
if (error == ERROR_SUCCESS) {
//
// whilst holding the view lock, we add the view to the list, set
// the view's reference count to 1, increment the session's
// reference count, and point the view at the session
//
viewInfo->ReferenceCount = 1; viewInfo->BufferInfo = bufferInfo; viewInfo->SessionInfo = SessionInfo;
//
// also whilst holding the view lock, we increment the reference
// count on the buffer info
//
ReferenceBuffer(bufferInfo); InsertHeadList(listAddress, &viewInfo->List);
//
// N.B. this will acquire the session list lock (then release it)
//
ReferenceSession(SessionInfo);
VIEW_CREATED();
}
ReleaseViewLock(SessionInfo, ViewType); }
if (error != ERROR_SUCCESS) { if (viewInfo != NULL) { viewInfo = DereferenceView(viewInfo);
INET_ASSERT(viewInfo == NULL);
} }
DEBUG_PRINT(VIEW, INFO, ("ViewInfo %x is %scloned\n", viewInfo, (*Cloned == TRUE) ? "" : "NOT " ));
DEBUG_ERROR(SESSION, error);
*Error = error;
DEBUG_LEAVE(viewInfo);
return viewInfo; }
PRIVATE VOID DestroyView( IN LPVIEW_INFO ViewInfo )
/*++
Routine Description:
Destroys a VIEW_INFO after freeing all resources that it owns. ViewInfo must have been removed from the relevant session view list by the time this function is called
Arguments:
ViewInfo - pointer to VIEW_INFO to destroy
Return Value:
None.
--*/
{ DEBUG_ENTER((DBG_GOPHER, None, "DestroyView", "%x", ViewInfo ));
INET_ASSERT(ViewInfo != NULL); INET_ASSERT(ViewInfo->ReferenceCount == 0);
if (ViewInfo->Handle) {
DWORD error;
error = FreeHandle(ViewInfo->Handle);
INET_ASSERT(error == ERROR_SUCCESS);
}
if (ViewInfo->Request) { DEL_STRING(ViewInfo->Request); }
DEL(ViewInfo);
VIEW_DESTROYED();
DEBUG_LEAVE(0); }
LPVIEW_INFO FindViewByHandle( IN HANDLE Handle, IN VIEW_TYPE ViewType )
/*++
Routine Description:
Finds a VIEW_INFO given a handle. If found, the VIEW_INFO reference count is incremented
Arguments:
Handle - handle associated with VIEW_INFO
ViewType - identifies which list to look on
Return Value:
LPVIEW_INFO Success - pointer to VIEW_INFO; Session points at SESSION_INFO
Failure - NULL
--*/
{ LPVIEW_INFO viewInfo; LPSESSION_INFO sessionInfo; BOOL found = FALSE;
DEBUG_ENTER((DBG_GOPHER, Pointer, "FindViewByHandle", "%x, %x", Handle, ViewType ));
AcquireSessionLock();
sessionInfo = (LPSESSION_INFO)SessionList.List.Flink; while (sessionInfo != (LPSESSION_INFO)&SessionList.List) {
PLIST_ENTRY endOfList; PLIST_ENTRY list;
AcquireViewLock(sessionInfo, ViewType);
INET_ASSERT((ViewType == ViewTypeFile) || (ViewType == ViewTypeFind));
if (ViewType == ViewTypeFile) { list = sessionInfo->FileList.List.Flink; endOfList = &sessionInfo->FileList.List; } else { list = sessionInfo->FindList.List.Flink; endOfList = &sessionInfo->FindList.List; }
for (viewInfo = (LPVIEW_INFO)list; viewInfo != (LPVIEW_INFO)endOfList; viewInfo = (LPVIEW_INFO)(viewInfo->List.Flink)) {
if (viewInfo->Handle == Handle) {
//
// we found the one we were looking for. Make sure that we
// increase the reference count before we release the lock
//
INET_ASSERT(viewInfo->ViewType == ViewType);
ReferenceView(viewInfo); found = TRUE; break; // out of for()
} }
ReleaseViewLock(sessionInfo, ViewType);
if (found) { break; // out of while()
} else { sessionInfo = (LPSESSION_INFO)sessionInfo->List.Flink; } } if (!found) { viewInfo = NULL; }
ReleaseSessionLock();
DEBUG_LEAVE(viewInfo);
return viewInfo; }
VOID ReferenceView( IN LPVIEW_INFO ViewInfo )
/*++
Routine Description:
Increments reference count on VIEW_INFO
Arguments:
ViewInfo - pointer to VIEW_INFO to reference
Return Value:
None.
--*/
{ INET_ASSERT(ViewInfo != NULL);
InterlockedIncrement(&ViewInfo->ReferenceCount);
DEBUG_PRINT(REFCOUNT, INFO, ("ReferenceView(): ViewInfo{%x}->ReferenceCount = %d\n", ViewInfo, ViewInfo->ReferenceCount )); }
LPVIEW_INFO DereferenceView( IN LPVIEW_INFO ViewInfo )
/*++
Routine Description:
Reduces the reference count of a VIEW_INFO by 1. If it goes to zero, the VIEW_INFO is removed from its owner's list and is deallocated
Arguments:
ViewInfo - pointer to VIEW_INFO to dereference
Return Value:
LPVIEW_INFO NULL - ViewInfo was deleted
!NULL - Reference count still >0
--*/
{ BOOL deleteViewInfo;
DEBUG_ENTER((DBG_GOPHER, Pointer, "DereferenceView", "%x", ViewInfo ));
INET_ASSERT(ViewInfo != NULL); INET_ASSERT(ViewInfo->ReferenceCount >= 1);
deleteViewInfo = FALSE; if (InterlockedDecrement(&ViewInfo->ReferenceCount) == 0) {
//
// perform any reference count manipulation within the view lock
//
AcquireViewLock(ViewInfo->SessionInfo, ViewInfo->ViewType);
//
// if the reference count is still zero, then it is safe to remove it
// from the SESSION_INFO, and delete
//
if (ViewInfo->ReferenceCount == 0) {
RemoveEntryList(&ViewInfo->List);
INET_ASSERT(ViewInfo->BufferInfo != NULL);
//
// perform buffer info reference count manipulation within the view
// lock
//
DereferenceBuffer(ViewInfo->BufferInfo); deleteViewInfo = TRUE; }
ReleaseViewLock(ViewInfo->SessionInfo, ViewInfo->ViewType); } else {
DEBUG_PRINT(REFCOUNT, INFO, ("DereferenceView(): ViewInfo{%x}->ReferenceCount = %d\n", ViewInfo, ViewInfo->ReferenceCount ));
}
//
// safe to delete this view info outside of the view lock
//
if (deleteViewInfo) {
LPSESSION_INFO sessionInfo;
sessionInfo = ViewInfo->SessionInfo;
DestroyView(ViewInfo); ViewInfo = NULL; DereferenceSession(sessionInfo); }
DEBUG_LEAVE(ViewInfo);
return ViewInfo; }
DWORD DereferenceViewByHandle( IN HINTERNET Handle, IN VIEW_TYPE ViewType )
/*++
Routine Description:
Atomically searches for the VIEW_INFO identified by Handle and if found dereferences it. This may cause the VIEW_INFO to be deleted
Arguments:
Handle - identifying VIEW_INFO to dereference
ViewType - identifies which list to look on
Return Value:
DWORD Success - ERROR_SUCCESS Handle was dereferenced, but not necessarily deleted
Failure - ERROR_INVALID_HANDLE Couldn't find Handle
--*/
{ LPVIEW_INFO viewInfo; DWORD error;
DEBUG_ENTER((DBG_GOPHER, Dword, "DereferenceViewByHandle", "%x", Handle ));
//
// we have to perform this operation whilst holding the session lock. We
// find the VIEW_INFO then dereference it twice - once to counteract the
// reference added by FindViewByHandle(), and once for the reference we
// were asked to remove by the caller.
//
// Since we are holding the Session lock, no other thread can dereference
// the VIEW_INFO or SESSION_INFO. The second derereference may cause the
// SESSION_INFO to be destroyed
//
AcquireSessionLock();
viewInfo = FindViewByHandle(Handle, ViewType); if (viewInfo != NULL) { DereferenceView(viewInfo); DereferenceView(viewInfo); error = ERROR_SUCCESS; } else { error = ERROR_INVALID_HANDLE; }
ReleaseSessionLock();
DEBUG_LEAVE(error);
return error; }
|