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.
 
 
 
 
 
 

716 lines
23 KiB

/*++
Copyright (c) 1996-2001 Microsoft Corporation
Module Name:
QfsTrans.cpp
Abstract:
This is a private interface for communications between
Qfs redirection layer and MNS resource.
Author:
GorN 8-March-2002
Revision History:
--*/
extern "C" {
#include "clusrtlp.h"
#include "QfsTrans.h"
};
WCHAR SharedMem_MappedFileName[] = L"\\MajorityNodeSet_SharedMemTransport_ver_1.0";
void SharedMem_Cleanup(PSHARED_MEM_CONTEXT Ctxt)
{
if (Ctxt->Mem) {
UnmapViewOfFile(Ctxt->Mem);
Ctxt->Mem = NULL;
}
if (Ctxt->FileMappingHandle) {
CloseHandle(Ctxt->FileMappingHandle);
Ctxt->FileMappingHandle = 0;
}
if (Ctxt->FileHandle) {
CloseHandle(Ctxt->FileHandle);
Ctxt->FileHandle = 0;
}
}
#define USE_FILE_MAPPING 1
DWORD SharedMem_Create(PSHARED_MEM_CONTEXT Ctxt,
IN DWORD CreateMode,
IN DWORD RetrySeconds,
IN DWORD RequiredSize)
/*++
Routine Description:
Opens/Create shared file and mapped it into memory
Arguments:
CreateMode - OPEN_EXISTING for a client, CREATE_ALWAYS for a server side
RetrySeconds - for how many seconds to retry the CreateFile if it fails
RequiredSize - how big the file should be (if CreateMode == CREATE_ALWAYS)
Return Value:
Win32 Status of request
--*/
{
WCHAR fname[MAX_PATH];
DWORD Status=ERROR_SUCCESS;
ZeroMemory(Ctxt, sizeof(*Ctxt));
#if !USE_FILE_MAPPING
Status = ClRtlGetClusterDirectory( fname, MAX_PATH - sizeof(SharedMem_MappedFileName) );
if ( Status != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[Qfs] SharedMem_Create: Error %1!d! in getting cluster dir !!!\n", Status);
goto exit_gracefully;
}
wcscat(fname, SharedMem_MappedFileName);
// Try a few times to open a file
// (Say resmon was able to restart before Qfs layer closed the file)
for(;;) {
Ctxt->FileHandle = CreateFile(
fname, // file name
GENERIC_READ | GENERIC_WRITE, // access mode
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, // share
NULL, // SD
CreateMode,
FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_DELETE_ON_CLOSE,
NULL // handle to template file
);
if (Ctxt->FileHandle != INVALID_HANDLE_VALUE) {
break;
}
Status = GetLastError();
ClRtlLogPrint(LOG_UNUSUAL,
"[Qfs] SharedMem_Create: failed to open the file, %1!d!\n", Status);
if (RetrySeconds-- == 0) {
// no more retries. Return failure
goto exit_gracefully;
}
ClRtlLogPrint(LOG_UNUSUAL, "[Qfs] Retrying in 1 second\n");
Sleep(1000);
}
ClRtlLogPrint(LOG_NOISE, "[Qfs] SharedMem_Create: Created %1!ws!\n", fname);
if (CreateMode == OPEN_EXISTING) {
// Figure out the length of the file
Ctxt->MappingSize = GetFileSize(Ctxt->FileHandle, NULL);
if (Ctxt->MappingSize == INVALID_FILE_SIZE)
{
Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[Qfs] SharedMem_Create: failed to get the file size, %1!d!\n", Status);
goto exit_gracefully;
}
} else {
Ctxt->MappingSize = RequiredSize;
if (!SetFilePointer(Ctxt->FileHandle, Ctxt->MappingSize, 0, FILE_BEGIN)) {
Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[Qfs] SharedMem_Create: failed to move file pointer, %1!d!\n", Status);
goto exit_gracefully;
}
if (!SetEndOfFile(Ctxt->FileHandle)) {
Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[Qfs] SharedMem_Create: failed to set end of file, %1!d!\n", Status);
goto exit_gracefully;
}
}
Ctxt->FileMappingHandle = CreateFileMapping(
Ctxt->FileHandle, // handle to file
NULL, // security
PAGE_READWRITE, 0,0,NULL); // 0 offset, no name
if (Ctxt->FileMappingHandle == NULL) {
Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[Qfs] SharedMem_Create: failed to create file mapping, %1!d!\n", GetLastError());
goto exit_gracefully;
}
#else
wsprintf(fname, L"Global%ws", SharedMem_MappedFileName);
if (CreateMode == OPEN_EXISTING) {
Ctxt->FileMappingHandle = OpenFileMappingW(FILE_MAP_WRITE, FALSE, fname);
if (Ctxt->FileMappingHandle == NULL) {
Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[Qfs] SharedMem_Create: failed to open file mapping, %1!d!\n", Status);
goto exit_gracefully;
}
}
else {
Ctxt->FileMappingHandle = CreateFileMappingW(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
RequiredSize,
fname);
if (Ctxt->FileMappingHandle == NULL) {
Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[Qfs] SharedMem_Create: failed to open file mapping, %1!d!\n", Status);
goto exit_gracefully;
}
else if((Status = GetLastError()) == ERROR_ALREADY_EXISTS) {
ClRtlLogPrint(LOG_CRITICAL,
"[Qfs] SharedMem_Create: file mapping already exists, exiting...\n");
goto exit_gracefully;
}
Status = ERROR_SUCCESS;
}
if (CreateMode == CREATE_ALWAYS) {
// Acl the object.
Status = ClRtlSetObjSecurityInfo(Ctxt->FileMappingHandle,
SE_KERNEL_OBJECT,
GENERIC_ALL,
GENERIC_ALL,
0
);
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint(LOG_CRITICAL,
"[Qfs] SharedMem_Create: failed to ACL the file map, %1!d!\n", Status);
goto exit_gracefully;
}
}
#endif
Ctxt->Mem = MapViewOfFile(
Ctxt->FileMappingHandle, // handle to file-mapping object
FILE_MAP_WRITE, // access mode
0,0,0); // offset 0, map entire file
if (Ctxt->Mem == NULL) {
Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[Qfs] SharedMem_Create: failed to map view of the file, %1!d!\n", GetLastError());
goto exit_gracefully;
}
ClRtlLogPrint(LOG_NOISE, "[Qfs] SharedMem_Create: Created %1!ws!\n", fname);
exit_gracefully:
if (Status != ERROR_SUCCESS) {
SharedMem_Cleanup(Ctxt);
}
return Status;
}
//////////////////////////////////////////////////////////////////////
////////// Thread Counter Code ////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
DWORD ThreadCounter_Init(PMTHREAD_COUNTER tc)
{
tc->LastThreadLeft = CreateEvent(NULL, TRUE, FALSE, NULL); // manual. non-signalled
tc->Count = 1;
if (tc->LastThreadLeft == NULL) {
DWORD Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[Qfs] ThreadCounter: Failed to allocate the event, error %1!d!\n", Status);
return Status;
}
return ERROR_SUCCESS;
}
void ThreadCounter_Inc(PMTHREAD_COUNTER tc)
{
DWORD result = InterlockedIncrement(&tc->Count);
}
void ThreadCounter_Dec(PMTHREAD_COUNTER tc)
{
DWORD result = InterlockedDecrement(&tc->Count);
if(result == 0) {
SetEvent(tc->LastThreadLeft);
}
}
void ThreadCounter_Rundown(PMTHREAD_COUNTER tc)
{
if (tc->Count == 0) {
ClRtlLogPrint(LOG_UNUSUAL, "[Qfs] No running threads. No need to rundown\n");
} else {
ThreadCounter_Dec(tc);
WaitForSingleObject(tc->LastThreadLeft, INFINITE);
}
}
void ThreadCounter_Cleanup(PMTHREAD_COUNTER tc)
{
if (tc->LastThreadLeft) {
CloseHandle(tc->LastThreadLeft);
tc->LastThreadLeft = NULL;
}
}
enum {SHARED_MEM_HEADER_VERSION = 1};
typedef struct _SHARED_MEM_HEADER {
DWORD Version;
DWORD JobBufferCount;
DWORD ServerPid;
DWORD State;
LONG FilledBuffersMask;
HANDLE EventHandles[MAX_JOB_BUFFERS + 2];
JOB_BUF JobBuffers[1];
} SHARED_MEM_HEADER, *PSHARED_MEM_HEADER;
//////////////////////////////////////////////////////////////////////
////////// S E R V E R C O D E //////////////////////////////////
//////////////////////////////////////////////////////////////////////
VOID MemServer_Cleanup(PSHARED_MEM_SERVER Server)
{
SharedMem_Cleanup(&Server->ShMem);
ThreadCounter_Cleanup(&Server->ThreadCounter);
for(DWORD i = 0; i < Server->nBuffers + 2; ++i) {
if (Server->EventHandles[i]) {
CloseHandle(Server->EventHandles[i]);
Server->EventHandles[i] = 0;
}
}
}
DWORD WINAPI DoWork(
LPVOID lpParameter)
{
JOB_BUF volatile* JobBuf = (JOB_BUF volatile*)lpParameter;
PSHARED_MEM_SERVER Server = (PSHARED_MEM_SERVER)JobBuf->ServerCookie;
int BufferNo = (int)(JobBuf - Server->JobBuffers);
Server->DoRealWork((PJOB_BUF)JobBuf, Server->DoRealWorkContext);
ThreadCounter_Dec(&Server->ThreadCounter);
SetEvent(Server->BufferReady[BufferNo]);
return ERROR_SUCCESS;
}
VOID CALLBACK ServerAttentionCallback(
PVOID lpParameter, // thread data
BOOLEAN TimerOrWaitFired)
{
PSHARED_MEM_SERVER Server = (PSHARED_MEM_SERVER)lpParameter;
LONG FilledBuffers = 0;
LONG ValueRead, OldValue;
TimerOrWaitFired; // unused
ClRtlLogPrint(LOG_NOISE,"[Qfs] ServerAttentionCallback fired\n");
do {
ValueRead = *Server->FilledBuffersMask;
OldValue = InterlockedExchangeAdd(Server->FilledBuffersMask, -ValueRead);
FilledBuffers += ValueRead;
} while (ValueRead != OldValue);
// Queue work items for all the filled buffers //
for (DWORD i = 0; i < Server->nBuffers; ++i)
{
if ( (1 << i) & FilledBuffers ) {
ThreadCounter_Inc(&Server->ThreadCounter);
Server->JobBuffers[i].ServerCookie = Server;
if (!QueueUserWorkItem(DoWork,&Server->JobBuffers[i],WT_EXECUTELONGFUNCTION)) {
DWORD Status = GetLastError();
Server->JobBuffers[i].hdr.Status = Status;
ClRtlLogPrint(LOG_CRITICAL,"[Qfs] Failed to queue a work item, %1!d!\n", Status);
ThreadCounter_Dec(&Server->ThreadCounter);
SetEvent(Server->BufferReady[i]);
}
}
}
}
DWORD MemServer_Online(
PSHARED_MEM_SERVER Server,
int nBuffers,
DoRealWorkCallback DoRealWork,
PVOID DoRealWorkContext)
{
DWORD Status = ERROR_SUCCESS;
DWORD RequiredSize = sizeof(SHARED_MEM_HEADER) + (nBuffers-1) * sizeof(JOB_BUF);
ZeroMemory(Server, sizeof(*Server));
Server->DoRealWork = DoRealWork;
Server->DoRealWorkContext = DoRealWorkContext;
if (nBuffers > MAX_JOB_BUFFERS) {
return ERROR_TOO_MANY_SESS;
}
Status = ThreadCounter_Init(&Server->ThreadCounter);
if (Status != ERROR_SUCCESS) {
goto exit_gracefully;
}
for (int i = 0; i < nBuffers + 2; ++i)
{
Server->EventHandles[i] = CreateEvent(NULL,FALSE,FALSE,NULL);// auto,non-signaled
if (Server->EventHandles[i] == NULL) {
Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,"Failed to create an event, %1!d!\n", Status);
goto exit_gracefully;
}
}
Server->Attention = Server->EventHandles[0];
Server->GoingOffline = Server->EventHandles[1];
Server->BufferReady = Server->EventHandles + 2;
Server->nBuffers = nBuffers;
Status = SharedMem_Create(&Server->ShMem, CREATE_ALWAYS, 6, RequiredSize);
if (Status != ERROR_SUCCESS) {
goto exit_gracefully;
}
PSHARED_MEM_HEADER hdr = (PSHARED_MEM_HEADER)Server->ShMem.Mem;
hdr->Version = SHARED_MEM_HEADER_VERSION;
hdr->JobBufferCount = nBuffers;
Server->JobBuffers = hdr->JobBuffers;
Server->FilledBuffersMask = &hdr->FilledBuffersMask;
memcpy(hdr->EventHandles, Server->EventHandles, sizeof(HANDLE)*(nBuffers+2));
// store the current process id last, since it is the first thing client looks at to see
// whether the server is ready
InterlockedExchange((volatile LONG*)&hdr->ServerPid, GetCurrentProcessId());
if (!RegisterWaitForSingleObject(
&Server->AttentionWaitRegistration, // wait handle
Server->Attention, // handle to object
ServerAttentionCallback, // timer callback function
Server, // callback function parameter
INFINITE, // time-out interval
WT_EXECUTEINWAITTHREAD))
{
Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[QfsServer] Failed to register a wait handler, status %1!d!\n", Status);
}
exit_gracefully:
if (Status != ERROR_SUCCESS) {
MemServer_Cleanup(Server);
}
return Status;
}
void MemServer_Offline(PSHARED_MEM_SERVER Server)
{
UnregisterWaitEx(Server->AttentionWaitRegistration, INVALID_HANDLE_VALUE);
// wait for all threads launched to complete
ThreadCounter_Rundown(&Server->ThreadCounter);
SetEvent(Server->GoingOffline);
MemServer_Cleanup(Server);
}
//////////////////////////////////////////////////////////////////////
////////// C L I E N T C O D E //////////////////////////////////
//////////////////////////////////////////////////////////////////////
enum { Disconnected, Connected, Draining};
DWORD MemClient_Init(PSHARED_MEM_SERVER Client)
{
DWORD Status = ERROR_SUCCESS;
ZeroMemory(Client, sizeof(*Client));
InitializeCriticalSection(&Client->Lock);
Client->State = Disconnected;
Client->FreeBufferCountSemaphore = NULL; // initialized during connect
return Status;
}
void MemClient_Disconnect(PSHARED_MEM_SERVER Client) // Called with Client->Lock held
{
ClRtlLogPrint(LOG_NOISE, "[Qfs] Disconnecting ...\n");
SharedMem_Cleanup(&Client->ShMem);
if (Client->GoingOfflineWaitRegistration) {
UnregisterWaitEx(Client->GoingOfflineWaitRegistration, NULL);
Client->GoingOfflineWaitRegistration = NULL;
}
if (Client->ServerProcessWaitRegistration) {
UnregisterWaitEx(Client->ServerProcessWaitRegistration, NULL);
Client->ServerProcessWaitRegistration = NULL;
}
Client->Attention = NULL;
Client->GoingOffline = NULL;
Client->BufferReady = NULL;
Client->State = Disconnected;
for(DWORD i = 0; i < Client->nBuffers + 2; ++i) {
if (Client->EventHandles[i]) {
CloseHandle(Client->EventHandles[i]);
Client->EventHandles[i] = 0;
}
}
if (Client->ServerProcess) {
CloseHandle(Client->ServerProcess);
Client->ServerProcess = 0;
}
if (Client->FreeBufferCountSemaphore) {
CloseHandle(Client->FreeBufferCountSemaphore);
Client->FreeBufferCountSemaphore = NULL;
}
}
void MemClient_Cleanup(PSHARED_MEM_SERVER Client)
{
MemClient_Disconnect(Client); // don't need to acquire Client lock for cleanup
DeleteCriticalSection(&Client->Lock);
}
VOID CALLBACK MemClient_DisconnectCallback(
PVOID lpParameter, // thread data
BOOLEAN TimerOrWaitFired)
{
PSHARED_MEM_SERVER Client = (PSHARED_MEM_SERVER)lpParameter;
TimerOrWaitFired; // unused
ClRtlLogPrint(LOG_NOISE, "[Qfs] DisconnectCallback fired\n");
EnterCriticalSection(&Client->Lock);
if (Client->ConnectionRefcount > 0) {
ClRtlLogPrint(LOG_NOISE, "[Qfs] I/O in progress. Last thread to exit will cleanup\n");
} else if (Client->State == Disconnected) {
ClRtlLogPrint(LOG_NOISE, "[Qfs] Already disconnected. Nothing to do\n");
} else {
MemClient_Disconnect(Client);
}
LeaveCriticalSection(&Client->Lock);
}
DWORD MemClient_Connect(PSHARED_MEM_SERVER Client) // Called with Client->Lock held
{
DWORD Status = ERROR_SUCCESS;
DWORD Retry = 3;
Status = SharedMem_Create(&Client->ShMem, OPEN_EXISTING, 0, 0);
if (Status != ERROR_SUCCESS) {
goto exit_gracefully;
}
PSHARED_MEM_HEADER volatile hdr = (PSHARED_MEM_HEADER)Client->ShMem.Mem;
// There is a chance that we connected to the shared memory immediately after
// server process created it, but before it initialized it properly
while (hdr->ServerPid == 0) {
Sleep(1000);
if (Retry -- == 0) {
Status = ERROR_PIPE_NOT_CONNECTED;
ClRtlLogPrint(LOG_CRITICAL,
"[Qfs] Connect, Server is not up, error %2!d!\n", hdr->ServerPid, Status);
goto exit_gracefully;
}
ClRtlLogPrint(LOG_UNUSUAL, "[Qfs] Connect, server is not up yet, retrying ...\n");
}
Client->nBuffers = hdr->JobBufferCount;
Client->FilledBuffersMask = &hdr->FilledBuffersMask;
Client->JobBuffers = hdr->JobBuffers;
Client->FreeBufferCountSemaphore = CreateSemaphore(0, Client->nBuffers, Client->nBuffers, 0);
if (Client->FreeBufferCountSemaphore == NULL) {
Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[Qfs] Connect, Failed to create semaphore %1!d!, error %2!d!\n", Client->nBuffers, Status);
goto exit_gracefully;
}
Client->ServerProcess = OpenProcess(
PROCESS_DUP_HANDLE|SYNCHRONIZE, FALSE, hdr->ServerPid);
if (Client->ServerProcess == NULL) {
Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[Qfs] Connect, Failed to open process %1!d!, error %2!d!\n", hdr->ServerPid, Status);
goto exit_gracefully;
}
for (DWORD i = 0; i < Client->nBuffers + 2; ++i)
{
if (!DuplicateHandle(
Client->ServerProcess, hdr->EventHandles[i],
GetCurrentProcess(), &Client->EventHandles[i],
0, FALSE, DUPLICATE_SAME_ACCESS))
{
Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[Qfs] Connect, Failed to dup the handle %1!p!, error %2!d!\n",
hdr->EventHandles[i], Status);
}
}
Client->Attention = Client->EventHandles[0];
Client->GoingOffline = Client->EventHandles[1];
Client->BufferReady = Client->EventHandles + 2;
Client->State = Connected;
if (!RegisterWaitForSingleObject(
&Client->ServerProcessWaitRegistration, // wait handle
Client->ServerProcess, // handle to object
MemClient_DisconnectCallback, // timer callback function
Client, // callback function parameter
INFINITE, // time-out interval
WT_EXECUTEINWAITTHREAD))
{
Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[QfsServer] Failed to register a SP wait handler, status %1!d!\n", Status);
goto exit_gracefully;
}
if (!RegisterWaitForSingleObject(
&Client->GoingOfflineWaitRegistration, // wait handle
Client->GoingOffline, // handle to object
MemClient_DisconnectCallback, // timer callback function
Client, // callback function parameter
INFINITE, // time-out interval
WT_EXECUTEINWAITTHREAD))
{
Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[QfsServer] Failed to register a GO wait handler, status %1!d!\n", Status);
goto exit_gracefully;
}
ClRtlLogPrint(LOG_NOISE, "[Qfs] Connected\n");
exit_gracefully:
if (Status != ERROR_SUCCESS) {
MemClient_Disconnect(Client);
}
return Status;
}
DWORD MemClient_ReserveBuffer(PSHARED_MEM_SERVER Client, PJOB_BUF *j)
{
DWORD Status = ERROR_SUCCESS;
EnterCriticalSection(&Client->Lock);
// Figure out whether there is a live connection
if (Client->State == Draining) {
Status = ERROR_BROKEN_PIPE;
} else if (Client->State == Disconnected) {
Status = MemClient_Connect(Client);
} else if ( // server died or went offline
(WaitForSingleObject(Client->GoingOffline,0) == WAIT_OBJECT_0)
||(WaitForSingleObject(Client->ServerProcess,0) == WAIT_OBJECT_0))
{
if (Client->ConnectionRefcount == 0) { // no in-flight i/o
MemClient_Disconnect(Client);
Status = MemClient_Connect(Client);
} else {
Status = ERROR_BROKEN_PIPE;
}
}
if (Status != ERROR_SUCCESS) {
goto fnExit;
}
// Refcount the connection and drop the lock, so that we can wait for a free buffer
ClRtlLogPrint(LOG_NOISE,
"[Qfs] Reserve RefCount++ %1!d! => %2!d!\n", Client->ConnectionRefcount, Client->ConnectionRefcount+1);
Client->ConnectionRefcount += 1;
LeaveCriticalSection(&Client->Lock);
WaitForSingleObject(Client->FreeBufferCountSemaphore, INFINITE);
EnterCriticalSection(&Client->Lock);
for(DWORD i = 0; i < Client->nBuffers; ++i) {
if ( ((1 << i) & Client->BusyBuffers) == 0 ) {
*j = &Client->JobBuffers[i];
Client->BusyBuffers |= (1 << i);
Client->JobBuffers[i].ClientCookie = Client;
break;
}
}
fnExit:
LeaveCriticalSection(&Client->Lock);
return Status;
}
DWORD MemClient_DeliverBuffer(PJOB_BUF j)
{
PSHARED_MEM_SERVER Client = (PSHARED_MEM_SERVER)j->ClientCookie;
int n = (int)(j - Client->JobBuffers);
HANDLE WaitHandles[3] =
{Client->BufferReady[n], Client->ServerProcess, Client->GoingOffline};
DWORD wait;
InterlockedExchangeAdd(Client->FilledBuffersMask, 1 << n);
SetEvent(Client->Attention);
wait = WaitForMultipleObjects(3, WaitHandles, FALSE, INFINITE);
if (wait == WAIT_FAILED) {
DWORD Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[Qfs] DeliverBuffer. Wait failed, error %1!d!\n", Status);
return Status;
}
if (wait == WAIT_OBJECT_0) {
return ERROR_SUCCESS;
}
Client->State = Draining; // safe (State will be changed to (Dis)Connected when all I/O drained)
return ERROR_BROKEN_PIPE;
}
VOID MemClient_Release(PJOB_BUF j)
{
PSHARED_MEM_SERVER Client = (PSHARED_MEM_SERVER)j->ClientCookie;
int n = (int)(j - Client->JobBuffers);
EnterCriticalSection(&Client->Lock);
Client->BusyBuffers &= ~(1 << n); // mark the buffer as free
ReleaseSemaphore(Client->FreeBufferCountSemaphore, 1, NULL);
ClRtlLogPrint(LOG_NOISE,
"[Qfs] Release RefCount-- %1!d! => %2!d!\n", Client->ConnectionRefcount, Client->ConnectionRefcount-1);
if (--Client->ConnectionRefcount == 0) {
// Do we need to disconnect?
if ( (WaitForSingleObject(Client->GoingOffline,0) == WAIT_OBJECT_0)
||(WaitForSingleObject(Client->ServerProcess,0) == WAIT_OBJECT_0))
{
MemClient_Disconnect(Client);
}
}
LeaveCriticalSection(&Client->Lock);
}