/* $Id: resource.c,v 1.21 2002/09/08 10:23:19 chorns Exp $
 *
 * COPYRIGHT:       See COPYING in the top level directory
 * PROJECT:         ReactOS kernel
 * FILE:            ntoskrnl/ex/resource.c
 * PURPOSE:         Resource synchronization construct
 * PROGRAMMER:      Unknown 
 * UPDATE HISTORY:
 *                  Created 22/05/98
 */


/*
 * Usage of ERESOURCE members is not documented.
 * From names of members and functionnalities, we can assume :
 *
 * OwnerTable = list of threads who have shared access(if more than one)
 * ActiveCount = number of threads who have access to the resource
 * Flag = bits : ResourceOwnedExclusive=0x80
 *               ResourceNeverExclusive=0x10
 *               ResourceReleaseByOtherThread=0x20
 *               ResourceDisableBoost=0x08
 * SharedWaiters = semaphore, used to manage wait list of shared waiters.
 * ExclusiveWaiters = event, used to manage wait list of exclusive waiters.
 * OwnerThreads[0]= thread who have exclusive access
 * OwnerThreads[1]= if only one thread own the resource
 *                     thread who have shared access
 *                  else
 *                     OwnerThread=0
 *                     and TableSize = number of entries in the owner table
 * NumberOfExclusiveWaiters = number of threads waiting for exclusive access.
 * NumberOfSharedWaiters = number of threads waiting for exclusive access.
 *
 */

#define ResourceOwnedExclusive 0x80
#define ResourceDisableBoost   0x08

/* INCLUDES *****************************************************************/

#include <ddk/ntddk.h>
#include <internal/ke.h>
#include <internal/pool.h>

#define NDEBUG
#include <internal/debug.h>

/* GLOBALS *******************************************************************/

#define TAG_OWNER_TABLE     TAG('R', 'O', 'W', 'N')
#define TAG_EXCLUSIVE_LOCK  TAG('E', 'R', 'E', 'L')
#define TAG_SHARED_SEM      TAG('E', 'R', 'S', 'S')

/* FUNCTIONS *****************************************************************/


BOOLEAN
STDCALL
ExTryToAcquireResourceExclusiveLite (
	PERESOURCE	Resource
	)
/*
 * FUNCTION: Attempts to require the resource for exclusive access
 * ARGUMENTS:
 *         Resource = Points to the resource of be acquired
 * RETURNS: TRUE if the resource was acquired for the caller
 * NOTES: Must be acquired at IRQL < DISPATCH_LEVEL
 */
{
  return(ExAcquireResourceExclusiveLite(Resource,FALSE));
}

BOOLEAN
STDCALL
ExAcquireResourceExclusive (
	PERESOURCE	Resource,
	BOOLEAN		Wait
	)
{
   return(ExAcquireResourceExclusiveLite(Resource,Wait));
}

BOOLEAN
STDCALL
ExAcquireResourceExclusiveLite (
	PERESOURCE	Resource,
	BOOLEAN		Wait
	)
/*
 * FUNCTION: Acquires a resource exclusively for the calling thread
 * ARGUMENTS:
 *       Resource = Points to the resource to acquire
 *       Wait = Is set to TRUE if the caller should wait to acquire the
 *              resource if it can't be acquired immediately
 * RETURNS: TRUE if the resource was acquired,
 *          FALSE otherwise
 * NOTES: Must be called at IRQL < DISPATCH_LEVEL
 */
{
   KIRQL oldIrql;
   
   DPRINT("ExAcquireResourceExclusiveLite(Resource %x, Wait %d)\n",
	  Resource, Wait);
   
   KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);

   /* resource already locked */
   if((Resource->Flag & ResourceOwnedExclusive)
      && Resource->OwnerThreads[0].OwnerThread == ExGetCurrentResourceThread())
     {
	/* it's ok : same lock for same thread */
	Resource->OwnerThreads[0].a.OwnerCount++;
	KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
	DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
	return(TRUE);
     }

   if (Resource->ActiveCount && !Wait)
     {
	KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
	DPRINT("ExAcquireResourceExclusiveLite() = FALSE\n");
	return(FALSE);
     }
   
   /* 
    * This is slightly better than it looks because other exclusive
    * threads who are waiting won't be woken up but there is a race
    * with new threads trying to grab the resource so we must have
    * the spinlock, still normally this loop will only be executed
    * once
    * NOTE: We might want to set a timeout to detect deadlock 
    * (10 minutes?)
    */
   while (Resource->ActiveCount) 
     {
	Resource->NumberOfExclusiveWaiters++;
	KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
	KeWaitForSingleObject(Resource->ExclusiveWaiters,
			      Executive,
			      KernelMode,
			      FALSE,
			      NULL);
	KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
	Resource->NumberOfExclusiveWaiters--;
     }
   Resource->Flag |= ResourceOwnedExclusive;
   Resource->ActiveCount = 1;
   Resource->OwnerThreads[0].OwnerThread = ExGetCurrentResourceThread();
   Resource->OwnerThreads[0].a.OwnerCount = 1;
   KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
   DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
   return(TRUE);
}

static BOOLEAN EiRemoveSharedOwner(PERESOURCE Resource,
				   ERESOURCE_THREAD ResourceThreadId)
/*
 * FUNCTION: Removes the current thread from the shared owners of the resource
 * ARGUMENTS:
 *      Resource = Pointer to the resource for which the thread is to be
 *                 added
 * NOTE: Must be called with the resource spinlock held
 */
{
   ULONG i;
   
   if (Resource->OwnerThreads[1].OwnerThread == ResourceThreadId)
     {
	Resource->OwnerThreads[1].a.OwnerCount--;
	if (Resource->OwnerThreads[1].a.OwnerCount == 0)
	  {
             Resource->ActiveCount--;
	     Resource->OwnerThreads[1].OwnerThread = 0;
	  }
	return(TRUE);
     }
   
   if (Resource->OwnerThreads[1].OwnerThread) 
     {
	/* Oh dear, the caller didn't own the resource after all */
	return(FALSE);;
     }
   
   for (i=0; i<Resource->OwnerThreads[1].a.TableSize; i++)
     {
	if (Resource->OwnerTable[i].OwnerThread == ResourceThreadId)
	  {
	     Resource->OwnerTable[i].a.OwnerCount--;
	     if (Resource->OwnerTable[i].a.OwnerCount == 0)
	       {
	          Resource->ActiveCount--;
		  Resource->OwnerTable[i].OwnerThread = 0;
	       }
	     return TRUE;
	  }
     }
   return(FALSE);
}

static BOOLEAN EiAddSharedOwner(PERESOURCE Resource)
/*
 * FUNCTION: Adds the current thread to the shared owners of the resource
 * ARGUMENTS:
 *         Resource = Pointer to the resource for which the thread is to be
 *                    added
 * NOTE: Must be called with the resource spinlock held
 */
{
   ERESOURCE_THREAD CurrentThread = ExGetCurrentResourceThread();
   POWNER_ENTRY freeEntry;
   ULONG i = 0;
   
   DPRINT("EiAddSharedOwner(Resource %x)\n", Resource);
   
   if (Resource->ActiveCount == 0) 
     {
	/* no owner, it's easy */
	Resource->OwnerThreads[1].OwnerThread = ExGetCurrentResourceThread();
	Resource->OwnerThreads[1].a.OwnerCount = 1;
	if (Resource->OwnerTable != NULL)
	  {
	     ExFreePool(Resource->OwnerTable);
	  }
	Resource->OwnerTable = NULL;
	Resource->ActiveCount = 1;
	DPRINT("EiAddSharedOwner() = TRUE\n");
	return(TRUE);
     }
   
   /* 
    * now, we must search if this thread has already acquired this resource 
    * then increase ownercount if found, else create new entry or reuse free 
    * entry
    */
   if (Resource->OwnerTable == NULL)
     {
	DPRINT("Creating owner table\n");
	
	/* allocate ownertable,memset to 0, initialize first entry */
	Resource->OwnerTable = 
	  ExAllocatePoolWithTag(NonPagedPool, sizeof(OWNER_ENTRY)*3, 
				TAG_OWNER_TABLE);
	if (Resource->OwnerTable == NULL)
	  {
	     KeBugCheck(0);
	     return(FALSE);
	  }
	memset(Resource->OwnerTable,0,sizeof(OWNER_ENTRY)*3);
	memcpy(&Resource->OwnerTable[0], &Resource->OwnerThreads[1],
	       sizeof(OWNER_ENTRY));
	
	Resource->OwnerThreads[1].OwnerThread = 0;
	Resource->OwnerThreads[1].a.TableSize = 3;
	
	Resource->OwnerTable[1].OwnerThread = CurrentThread;
	Resource->OwnerTable[1].a.OwnerCount = 1;
        Resource->ActiveCount++;
	
	return(TRUE);
     }
   
   DPRINT("Search free entries\n");
   
   DPRINT("Number of entries %d\n", 
	  Resource->OwnerThreads[1].a.TableSize);
   
   freeEntry = NULL;
   for (i=0; i<Resource->OwnerThreads[1].a.TableSize; i++)
     {
	if (Resource->OwnerTable[i].OwnerThread == CurrentThread)
	  {
	     DPRINT("Thread already owns resource\n");
	     Resource->OwnerTable[i].a.OwnerCount++;
	     return(TRUE);
	  }
	if (Resource->OwnerTable[i].OwnerThread == 0)
	  {
	     freeEntry = &Resource->OwnerTable[i];
	     break;
	  }
     }
   
   DPRINT("Found free entry %x\n", freeEntry);
   
   if (!freeEntry)
     {
	DPRINT("Allocating new entry\n");
	
	/* reallocate ownertable with one more entry */
	freeEntry = 
	  ExAllocatePoolWithTag(NonPagedPool,
				sizeof(OWNER_ENTRY)*
				(Resource->OwnerThreads[1].a.TableSize+1),
				TAG_OWNER_TABLE);
	if (freeEntry == NULL)
	  {
	     KeBugCheck(0);
	     return(FALSE);
	  }
	memcpy(freeEntry,Resource->OwnerTable,
	       sizeof(OWNER_ENTRY)*(Resource->OwnerThreads[1].a.TableSize));
	ExFreePool(Resource->OwnerTable);
	Resource->OwnerTable=freeEntry;
	freeEntry=&Resource->OwnerTable[Resource->OwnerThreads[1].a.TableSize];
	Resource->OwnerThreads[1].a.TableSize++;
     }
   DPRINT("Creating entry\n");
   freeEntry->OwnerThread=ExGetCurrentResourceThread();
   freeEntry->a.OwnerCount=1;
   Resource->ActiveCount++;
   return(TRUE);
}

BOOLEAN
STDCALL
ExAcquireResourceSharedLite (
	PERESOURCE	Resource,
	BOOLEAN		Wait
	)
/*
 * FUNCTION: Acquires the given resource for shared access by the calling
 *           thread
 * ARGUMENTS:
 *       Resource = Points to the resource to acquire
 *       Wait = Is set to TRUE if the caller should be put into wait state
 *              until the resource can be acquired if it cannot be acquired
 *              immediately
 * RETURNS: TRUE, if the resource is acquire
 *          FALSE otherwise
 */
{
   KIRQL oldIrql;
   
   DPRINT("ExAcquireResourceSharedLite(Resource %x, Wait %d)\n",
	  Resource, Wait);
   
   KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
   
   /* first, resolve trivial cases */
   if (Resource->ActiveCount == 0) 
     {
	EiAddSharedOwner(Resource);
	KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
	DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
	return(TRUE);
     }
   
   if ((Resource->Flag & ResourceOwnedExclusive)
       && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread())
     {
	/* exclusive, but by same thread : it's ok */
	/* 
	 * NOTE: Is this correct? Seems the same as ExConvertExclusiveToShared 
	 */
	Resource->OwnerThreads[0].a.OwnerCount++;
	KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
	DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
	return(TRUE);
     }
   
   if ((Resource->Flag & ResourceOwnedExclusive)
       || Resource->NumberOfExclusiveWaiters)
     { 
	/* exclusive by another thread , or thread waiting for exclusive */
	if (!Wait) 
	  {
	     KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
	     DPRINT("ExAcquireResourceSharedLite() = FALSE\n");
	     return(FALSE);
	  }
	else
	  {
	    Resource->NumberOfSharedWaiters++;
	    do
	    {
	       /* wait for the semaphore */
	       KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
	       KeWaitForSingleObject(Resource->SharedWaiters,0, KernelMode, FALSE, NULL);
	       KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
	       /* the spin lock was released we must check again */
	    }
	    while ((Resource->Flag & ResourceOwnedExclusive)
		|| Resource->NumberOfExclusiveWaiters);
	    Resource->NumberOfSharedWaiters--;
	  }
     }
   
   EiAddSharedOwner(Resource);
   KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
   DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
   return(TRUE);
}

VOID
STDCALL
ExConvertExclusiveToSharedLite (
	PERESOURCE	Resource
	)
/*
 * FUNCTION: Converts a given resource from acquired for exclusive access
 *           to acquire for shared access
 * ARGUMENTS:
 *      Resource = Points to the resource for which the access should be
 *                 converted
 * NOTES: Caller must be running at IRQL < DISPATCH_LEVEL
 */
{
   ULONG oldWaiters;
   KIRQL oldIrql;
   
   DPRINT("ExConvertExclusiveToSharedLite(Resource %x)\n", Resource);
   
   KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
   
   oldWaiters = Resource->NumberOfSharedWaiters;
   
   if (!(Resource->Flag & ResourceOwnedExclusive))
     {
	/* Might not be what the caller expects, better bug check */
	KeBugCheck(0);
	KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
	return;
     }
   
   //transfer infos from entry 0 to entry 1 and erase entry 0
   Resource->OwnerThreads[1].OwnerThread=Resource->OwnerThreads[0].OwnerThread;
   Resource->OwnerThreads[1].a.OwnerCount=Resource->OwnerThreads[0].a.OwnerCount;
   Resource->OwnerThreads[0].OwnerThread=0;
   Resource->OwnerThreads[0].a.OwnerCount=0;
   /* erase exclusive flag */
   Resource->Flag &= (~ResourceOwnedExclusive);
   /* if no shared waiters, that's all */
   if (!oldWaiters) 
     {
	KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
	return;
     }
   /* else, awake the waiters */
   KeReleaseSemaphore(Resource->SharedWaiters,0,oldWaiters,0);
   KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
   DPRINT("ExConvertExclusiveToSharedLite() finished\n");
}

VOID
STDCALL
ExDisableResourceBoostLite (
	PERESOURCE	Resource
	)
{
   Resource->Flag |= ResourceDisableBoost;
}

ULONG
STDCALL
ExGetExclusiveWaiterCount (
	PERESOURCE	Resource
	)
{
  return(Resource->NumberOfExclusiveWaiters);
}

BOOLEAN
STDCALL
ExAcquireSharedStarveExclusive (
	PERESOURCE	Resource,
	BOOLEAN		Wait
	)
/*
 * FUNCTION: Acquires a given resource for shared access without waiting
 *           for any pending attempts to acquire exclusive access to the
 *           same resource
 * ARGUMENTS:
 *       Resource = Points to the resource to be acquired for shared access
 *       Wait = Is set to TRUE if the caller will wait until the resource
 *              becomes available when access can't be granted immediately
 * RETURNS: TRUE if the requested access is granted. The routine returns
 *          FALSE if the input Wait is FALSE and shared access can't be
 *          granted immediately
 */
{
   KIRQL oldIrql;
   
   DPRINT("ExAcquireSharedStarveExclusive(Resource %x, Wait %d)\n",
	  Resource, Wait);
   
   KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
   
   /* no owner, it's easy */
   if (Resource->ActiveCount == 0) 
     {
	Resource->OwnerThreads[1].OwnerThread=ExGetCurrentResourceThread();
	Resource->OwnerThreads[1].a.OwnerCount=1;
	Resource->ActiveCount=1;
	KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
	DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
	return(TRUE);
     }
   
   if ((Resource->Flag & ResourceOwnedExclusive)
       &&  Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread())
     { 
	/* exclusive, but by same thread : it's ok */
	Resource->OwnerThreads[0].a.OwnerCount++;
	KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
	DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
	return(TRUE);
     }
   
   if (Resource->Flag & ResourceOwnedExclusive)
     { 
	/* exclusive by another thread */
	if (!Wait) 
	  {
	     KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
	     DPRINT("ExAcquireSharedStarveExclusive() = FALSE\n");
	     return(FALSE);
	  }
	else
	  {
	     Resource->NumberOfSharedWaiters++;
	     /* wait for the semaphore */
	     KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
	     KeWaitForSingleObject(Resource->SharedWaiters,0,0,0,0);
	     KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
	     Resource->NumberOfSharedWaiters--;
	  }
     }
   EiAddSharedOwner(Resource);
   KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
   DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
   return(TRUE);
}

BOOLEAN
STDCALL
ExAcquireSharedWaitForExclusive (
	PERESOURCE	Resource,
	BOOLEAN		Wait
	)
{
  return(ExAcquireResourceSharedLite(Resource,Wait));
}

NTSTATUS
STDCALL
ExDeleteResource (
	PERESOURCE	Resource
	)
{
   return(ExDeleteResourceLite(Resource));
}

NTSTATUS
STDCALL
ExDeleteResourceLite (
	PERESOURCE	Resource
	)
{
   DPRINT("ExDeleteResourceLite(Resource %x)\n", Resource);
   if (Resource->OwnerTable) ExFreePool(Resource->OwnerTable);
   if (Resource->SharedWaiters) ExFreePool(Resource->SharedWaiters);
   if (Resource->ExclusiveWaiters) ExFreePool(Resource->ExclusiveWaiters);
   return(STATUS_SUCCESS);;
}

ULONG
STDCALL
ExGetSharedWaiterCount (
	PERESOURCE	Resource
	)
{
   return(Resource->NumberOfSharedWaiters);
}

NTSTATUS
STDCALL
ExInitializeResource (
	PERESOURCE	Resource
	)
{
   return(ExInitializeResourceLite(Resource));
}

NTSTATUS STDCALL
ExInitializeResourceLite (PERESOURCE	Resource)
{
   DPRINT("ExInitializeResourceLite(Resource %x)\n", Resource);
   memset(Resource,0,sizeof(ERESOURCE));
   Resource->NumberOfSharedWaiters = 0;
   Resource->NumberOfExclusiveWaiters = 0;
   KeInitializeSpinLock(&Resource->SpinLock);
   Resource->Flag = 0;
   Resource->ExclusiveWaiters = 
     ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_EXCLUSIVE_LOCK);
   KeInitializeEvent(Resource->ExclusiveWaiters,
		     SynchronizationEvent,
		     FALSE);
   Resource->SharedWaiters = 
     ExAllocatePoolWithTag(NonPagedPool ,sizeof(KSEMAPHORE), TAG_SHARED_SEM);
   KeInitializeSemaphore(Resource->SharedWaiters,0,0x7fffffff);
   Resource->ActiveCount = 0;
   return(0);
}

BOOLEAN
STDCALL
ExIsResourceAcquiredExclusiveLite (
	PERESOURCE	Resource
	)
/*
 * FUNCTION: Returns whether the current thread has exclusive access to
 * a given resource
 * ARGUMENTS:
 *        Resource = Points to the resource to be queried
 * RETURNS: TRUE if the caller has exclusive access to the resource,
 *          FALSE otherwise
 */
{
   return((Resource->Flag & ResourceOwnedExclusive)
	  && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread());
}

ULONG
STDCALL
ExIsResourceAcquiredSharedLite (
	PERESOURCE	Resource
	)
/*
 * FUNCTION: Returns whether the current thread has shared access to a given
 *           resource
 * ARGUMENTS:
 *      Resource = Points to the resource to be queried
 * RETURNS: The number of times the caller has acquired shared access to the
 *          given resource
 */ 
{
   ULONG i;
   if (Resource->OwnerThreads[0].OwnerThread == ExGetCurrentResourceThread())
     {
	return(Resource->OwnerThreads[0].a.OwnerCount);
     }
   if (Resource->OwnerThreads[1].OwnerThread == ExGetCurrentResourceThread())
     {
	return(Resource->OwnerThreads[1].a.OwnerCount);
     }
   if (!Resource->OwnerThreads[1].a.TableSize) 
     {
	return(0);
     }
   for (i=0; i<Resource->OwnerThreads[1].a.TableSize; i++)
     {
	if (Resource->OwnerTable[i].OwnerThread==ExGetCurrentResourceThread())
	  {
	     return Resource->OwnerTable[i].a.OwnerCount;
	  }
     }
   return(0);
}

VOID
STDCALL
ExReinitializeResourceLite (
	PERESOURCE	Resource
	)
{
   Resource->NumberOfSharedWaiters = 0;
   Resource->NumberOfExclusiveWaiters = 0;
   KeInitializeSpinLock(&Resource->SpinLock);
   Resource->Flag=0;
   KeInitializeEvent(Resource->ExclusiveWaiters,SynchronizationEvent,
		     FALSE);
   KeInitializeSemaphore(Resource->SharedWaiters,0,0x7fffffff);
   Resource->ActiveCount = 0;
   if (Resource->OwnerTable) 
     {
	ExFreePool(Resource->OwnerTable);
     }
   Resource->OwnerThreads[0].OwnerThread=0;
   Resource->OwnerThreads[0].a.OwnerCount=0;
   Resource->OwnerThreads[1].OwnerThread=0;
   Resource->OwnerThreads[1].a.OwnerCount=0;
}

VOID
FASTCALL
ExReleaseResourceLite (
	PERESOURCE	Resource
	)
{
  return(ExReleaseResourceForThreadLite(Resource,
					ExGetCurrentResourceThread()));
}

VOID
STDCALL
ExReleaseResourceForThread (
	PERESOURCE		Resource,
	ERESOURCE_THREAD	ResourceThreadId
	)
{
  return(ExReleaseResourceForThreadLite(Resource,ResourceThreadId));
}

VOID
STDCALL
ExReleaseResourceForThreadLite (
	PERESOURCE		Resource,
	ERESOURCE_THREAD	ResourceThreadId
	)
/*
 * FUNCTION: Releases a resource for the given thread
 * ARGUMENTS:
 *         Resource = Points to the release to release
 *         ResourceThreadId = Identifies the thread that originally acquired
 *                            the resource
 * NOTES: Must be running at IRQL < DISPATCH_LEVEL
 * BUG: We don't support starving exclusive waiters
 */
{
   KIRQL oldIrql;
   
   DPRINT("ExReleaseResourceForThreadLite(Resource %x, ResourceThreadId %x)\n",
	  Resource, ResourceThreadId);
   
   KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
   
   if (Resource->Flag & ResourceOwnedExclusive)
     {
	DPRINT("Releasing from exclusive access\n");
	
	Resource->OwnerThreads[0].a.OwnerCount--;
	if (Resource->OwnerThreads[0].a.OwnerCount > 0)
	  {
	     KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
	     DPRINT("ExReleaseResourceForThreadLite() finished\n");
	     return;
	  }
	
	Resource->OwnerThreads[0].OwnerThread = 0;
	Resource->ActiveCount--;
	Resource->Flag &=(~ResourceOwnedExclusive);
	assert(Resource->ActiveCount == 0);
	DPRINT("Resource->NumberOfExclusiveWaiters %d\n",
	       Resource->NumberOfExclusiveWaiters);
	if (Resource->NumberOfExclusiveWaiters)
	  {
	     /* get resource to first exclusive waiter */
	     KeSetEvent(Resource->ExclusiveWaiters, 
			IO_NO_INCREMENT, 
			FALSE);
	     KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
	     DPRINT("ExReleaseResourceForThreadLite() finished\n");
	     return;
	  }
	DPRINT("Resource->NumberOfSharedWaiters %d\n",
	       Resource->NumberOfSharedWaiters);
	if (Resource->NumberOfSharedWaiters)
	  {
	     DPRINT("Releasing semaphore\n");
	     KeReleaseSemaphore(Resource->SharedWaiters,
				IO_NO_INCREMENT,
				Resource->NumberOfSharedWaiters,
				FALSE);
	  }
	KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
	DPRINT("ExReleaseResourceForThreadLite() finished\n");
	return;
     }
  
   EiRemoveSharedOwner(Resource, ResourceThreadId);
   
   if (Resource->ActiveCount == 0)
     {
	if (Resource->NumberOfExclusiveWaiters)
	  { 
	     /* get resource to first exclusive waiter */
	     KeSetEvent(Resource->ExclusiveWaiters,
			IO_NO_INCREMENT,
			FALSE);
	  }
     }
     
   KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
   DPRINT("ExReleaseResourceForThreadLite() finished\n");
}


VOID
STDCALL
ExSetResourceOwnerPointer (
	IN	PERESOURCE	Resource,
	IN	PVOID		OwnerPointer
	)
{

}

/* EOF */
