Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygencritical.cGo to the documentation of this file.00001 /* 00002 * COPYRIGHT: See COPYING in the top level directory 00003 * PROJECT: ReactOS system libraries 00004 * FILE: lib/rtl/critical.c 00005 * PURPOSE: Critical sections 00006 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) 00007 * Gunnar Dalsnes 00008 */ 00009 00010 /* INCLUDES *****************************************************************/ 00011 00012 #include <rtl.h> 00013 00014 #define NDEBUG 00015 #include <debug.h> 00016 00017 #define MAX_STATIC_CS_DEBUG_OBJECTS 64 00018 00019 static RTL_CRITICAL_SECTION RtlCriticalSectionLock; 00020 static LIST_ENTRY RtlCriticalSectionList; 00021 static BOOLEAN RtlpCritSectInitialized = FALSE; 00022 static RTL_CRITICAL_SECTION_DEBUG RtlpStaticDebugInfo[MAX_STATIC_CS_DEBUG_OBJECTS]; 00023 static BOOLEAN RtlpDebugInfoFreeList[MAX_STATIC_CS_DEBUG_OBJECTS]; 00024 00025 /* FUNCTIONS *****************************************************************/ 00026 00027 /*++ 00028 * RtlpCreateCriticalSectionSem 00029 * 00030 * Checks if an Event has been created for the critical section. 00031 * 00032 * Params: 00033 * None 00034 * 00035 * Returns: 00036 * None. Raises an exception if the system call failed. 00037 * 00038 * Remarks: 00039 * None 00040 * 00041 *--*/ 00042 VOID 00043 NTAPI 00044 RtlpCreateCriticalSectionSem(PRTL_CRITICAL_SECTION CriticalSection) 00045 { 00046 HANDLE hEvent = CriticalSection->LockSemaphore; 00047 HANDLE hNewEvent; 00048 NTSTATUS Status; 00049 00050 /* Chevk if we have an event */ 00051 if (!hEvent) { 00052 00053 /* No, so create it */ 00054 if (!NT_SUCCESS(Status = NtCreateEvent(&hNewEvent, 00055 EVENT_ALL_ACCESS, 00056 NULL, 00057 SynchronizationEvent, 00058 FALSE))) { 00059 00060 /* We failed, this is bad... */ 00061 DPRINT1("Failed to Create Event!\n"); 00062 InterlockedDecrement(&CriticalSection->LockCount); 00063 RtlRaiseStatus(Status); 00064 return; 00065 } 00066 DPRINT("Created Event: %p \n", hNewEvent); 00067 00068 if (InterlockedCompareExchangePointer((PVOID*)&CriticalSection->LockSemaphore, 00069 (PVOID)hNewEvent, 00070 0)) { 00071 00072 /* Some just created an event */ 00073 DPRINT("Closing already created event: %p\n", hNewEvent); 00074 NtClose(hNewEvent); 00075 } 00076 } 00077 00078 return; 00079 } 00080 00081 /*++ 00082 * RtlpWaitForCriticalSection 00083 * 00084 * Slow path of RtlEnterCriticalSection. Waits on an Event Object. 00085 * 00086 * Params: 00087 * CriticalSection - Critical section to acquire. 00088 * 00089 * Returns: 00090 * STATUS_SUCCESS, or raises an exception if a deadlock is occuring. 00091 * 00092 * Remarks: 00093 * None 00094 * 00095 *--*/ 00096 NTSTATUS 00097 NTAPI 00098 RtlpWaitForCriticalSection(PRTL_CRITICAL_SECTION CriticalSection) 00099 { 00100 NTSTATUS Status; 00101 EXCEPTION_RECORD ExceptionRecord; 00102 BOOLEAN LastChance = FALSE; 00103 LARGE_INTEGER Timeout; 00104 00105 /* Wait 2.5 minutes */ 00106 Timeout.QuadPart = 150000L * (ULONGLONG)10000; 00107 Timeout.QuadPart = -Timeout.QuadPart; 00108 /* ^^ HACK HACK HACK. Good way: 00109 Timeout = &NtCurrentPeb()->CriticalSectionTimeout */ 00110 00111 /* Do we have an Event yet? */ 00112 if (!CriticalSection->LockSemaphore) { 00113 RtlpCreateCriticalSectionSem(CriticalSection); 00114 } 00115 00116 /* Increase the Debug Entry count */ 00117 DPRINT("Waiting on Critical Section Event: %p %p\n", 00118 CriticalSection, 00119 CriticalSection->LockSemaphore); 00120 00121 if (CriticalSection->DebugInfo) 00122 CriticalSection->DebugInfo->EntryCount++; 00123 00124 for (;;) { 00125 00126 /* Increase the number of times we've had contention */ 00127 if (CriticalSection->DebugInfo) 00128 CriticalSection->DebugInfo->ContentionCount++; 00129 00130 /* Wait on the Event */ 00131 Status = NtWaitForSingleObject(CriticalSection->LockSemaphore, 00132 FALSE, 00133 &Timeout); 00134 00135 /* We have Timed out */ 00136 if (Status == STATUS_TIMEOUT) { 00137 00138 /* Is this the 2nd time we've timed out? */ 00139 if (LastChance) { 00140 00141 DPRINT1("Deadlock: %p\n", CriticalSection); 00142 00143 /* Yes it is, we are raising an exception */ 00144 ExceptionRecord.ExceptionCode = STATUS_POSSIBLE_DEADLOCK; 00145 ExceptionRecord.ExceptionFlags = 0; 00146 ExceptionRecord.ExceptionRecord = NULL; 00147 ExceptionRecord.ExceptionAddress = RtlRaiseException; 00148 ExceptionRecord.NumberParameters = 1; 00149 ExceptionRecord.ExceptionInformation[0] = (ULONG_PTR)CriticalSection; 00150 RtlRaiseException(&ExceptionRecord); 00151 00152 } 00153 00154 /* One more try */ 00155 LastChance = TRUE; 00156 00157 } else { 00158 00159 /* If we are here, everything went fine */ 00160 return STATUS_SUCCESS; 00161 } 00162 } 00163 } 00164 00165 /*++ 00166 * RtlpUnWaitCriticalSection 00167 * 00168 * Slow path of RtlLeaveCriticalSection. Fires an Event Object. 00169 * 00170 * Params: 00171 * CriticalSection - Critical section to release. 00172 * 00173 * Returns: 00174 * None. Raises an exception if the system call failed. 00175 * 00176 * Remarks: 00177 * None 00178 * 00179 *--*/ 00180 VOID 00181 NTAPI 00182 RtlpUnWaitCriticalSection(PRTL_CRITICAL_SECTION CriticalSection) 00183 { 00184 NTSTATUS Status; 00185 00186 /* Do we have an Event yet? */ 00187 if (!CriticalSection->LockSemaphore) { 00188 RtlpCreateCriticalSectionSem(CriticalSection); 00189 } 00190 00191 /* Signal the Event */ 00192 DPRINT("Signaling Critical Section Event: %p, %p\n", 00193 CriticalSection, 00194 CriticalSection->LockSemaphore); 00195 Status = NtSetEvent(CriticalSection->LockSemaphore, NULL); 00196 00197 if (!NT_SUCCESS(Status)) { 00198 00199 /* We've failed */ 00200 DPRINT1("Signaling Failed for: %p, %p, 0x%08lx\n", 00201 CriticalSection, 00202 CriticalSection->LockSemaphore, 00203 Status); 00204 RtlRaiseStatus(Status); 00205 } 00206 } 00207 00208 /*++ 00209 * RtlpInitDeferedCriticalSection 00210 * 00211 * Initializes the Critical Section implementation. 00212 * 00213 * Params: 00214 * None 00215 * 00216 * Returns: 00217 * None. 00218 * 00219 * Remarks: 00220 * After this call, the Process Critical Section list is protected. 00221 * 00222 *--*/ 00223 VOID 00224 NTAPI 00225 RtlpInitDeferedCriticalSection(VOID) 00226 { 00227 00228 /* Initialize the Process Critical Section List */ 00229 InitializeListHead(&RtlCriticalSectionList); 00230 00231 /* Initialize the CS Protecting the List */ 00232 RtlInitializeCriticalSection(&RtlCriticalSectionLock); 00233 00234 /* It's now safe to enter it */ 00235 RtlpCritSectInitialized = TRUE; 00236 } 00237 00238 /*++ 00239 * RtlpAllocateDebugInfo 00240 * 00241 * Finds or allocates memory for a Critical Section Debug Object 00242 * 00243 * Params: 00244 * None 00245 * 00246 * Returns: 00247 * A pointer to an empty Critical Section Debug Object. 00248 * 00249 * Remarks: 00250 * For optimization purposes, the first 64 entries can be cached. From 00251 * then on, future Critical Sections will allocate memory from the heap. 00252 * 00253 *--*/ 00254 PRTL_CRITICAL_SECTION_DEBUG 00255 NTAPI 00256 RtlpAllocateDebugInfo(VOID) 00257 { 00258 ULONG i; 00259 00260 /* Try to allocate from our buffer first */ 00261 for (i = 0; i < MAX_STATIC_CS_DEBUG_OBJECTS; i++) { 00262 00263 /* Check if Entry is free */ 00264 if (!RtlpDebugInfoFreeList[i]) { 00265 00266 /* Mark entry in use */ 00267 DPRINT("Using entry: %lu. Buffer: %p\n", i, &RtlpStaticDebugInfo[i]); 00268 RtlpDebugInfoFreeList[i] = TRUE; 00269 00270 /* Use free entry found */ 00271 return &RtlpStaticDebugInfo[i]; 00272 } 00273 00274 } 00275 00276 /* We are out of static buffer, allocate dynamic */ 00277 return RtlAllocateHeap(NtCurrentPeb()->ProcessHeap, 00278 0, 00279 sizeof(RTL_CRITICAL_SECTION_DEBUG)); 00280 } 00281 00282 /*++ 00283 * RtlpFreeDebugInfo 00284 * 00285 * Frees the memory for a Critical Section Debug Object 00286 * 00287 * Params: 00288 * DebugInfo - Pointer to Critical Section Debug Object to free. 00289 * 00290 * Returns: 00291 * None. 00292 * 00293 * Remarks: 00294 * If the pointer is part of the static buffer, then the entry is made 00295 * free again. If not, the object is de-allocated from the heap. 00296 * 00297 *--*/ 00298 VOID 00299 NTAPI 00300 RtlpFreeDebugInfo(PRTL_CRITICAL_SECTION_DEBUG DebugInfo) 00301 { 00302 SIZE_T EntryId; 00303 00304 /* Is it part of our cached entries? */ 00305 if ((DebugInfo >= RtlpStaticDebugInfo) && 00306 (DebugInfo <= &RtlpStaticDebugInfo[MAX_STATIC_CS_DEBUG_OBJECTS-1])) { 00307 00308 /* Yes. zero it out */ 00309 RtlZeroMemory(DebugInfo, sizeof(RTL_CRITICAL_SECTION_DEBUG)); 00310 00311 /* Mark as free */ 00312 EntryId = (DebugInfo - RtlpStaticDebugInfo); 00313 DPRINT("Freeing from Buffer: %p. Entry: %Iu inside Process: %p\n", 00314 DebugInfo, 00315 EntryId, 00316 NtCurrentTeb()->ClientId.UniqueProcess); 00317 RtlpDebugInfoFreeList[EntryId] = FALSE; 00318 00319 } else if (!DebugInfo->Flags) { 00320 00321 /* It's a dynamic one, so free from the heap */ 00322 DPRINT("Freeing from Heap: %p inside Process: %p\n", 00323 DebugInfo, 00324 NtCurrentTeb()->ClientId.UniqueProcess); 00325 RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, DebugInfo); 00326 00327 } else { 00328 00329 /* Wine stores a section name pointer in the Flags member */ 00330 DPRINT("Assuming static: %p inside Process: %p\n", 00331 DebugInfo, 00332 NtCurrentTeb()->ClientId.UniqueProcess); 00333 00334 } 00335 } 00336 00337 /*++ 00338 * RtlDeleteCriticalSection 00339 * @implemented NT4 00340 * 00341 * Deletes a Critical Section 00342 * 00343 * Params: 00344 * CriticalSection - Critical section to delete. 00345 * 00346 * Returns: 00347 * STATUS_SUCCESS, or error value returned by NtClose. 00348 * 00349 * Remarks: 00350 * The critical section members should not be read after this call. 00351 * 00352 *--*/ 00353 NTSTATUS 00354 NTAPI 00355 RtlDeleteCriticalSection(PRTL_CRITICAL_SECTION CriticalSection) 00356 { 00357 NTSTATUS Status = STATUS_SUCCESS; 00358 00359 DPRINT("Deleting Critical Section: %p\n", CriticalSection); 00360 /* Close the Event Object Handle if it exists */ 00361 if (CriticalSection->LockSemaphore) { 00362 00363 /* In case NtClose fails, return the status */ 00364 Status = NtClose(CriticalSection->LockSemaphore); 00365 00366 } 00367 00368 /* Protect List */ 00369 RtlEnterCriticalSection(&RtlCriticalSectionLock); 00370 00371 if (CriticalSection->DebugInfo) 00372 { 00373 /* Remove it from the list */ 00374 RemoveEntryList(&CriticalSection->DebugInfo->ProcessLocksList); 00375 #if 0 /* We need to preserve Flags for RtlpFreeDebugInfo */ 00376 RtlZeroMemory(CriticalSection->DebugInfo, sizeof(RTL_CRITICAL_SECTION_DEBUG)); 00377 #endif 00378 } 00379 00380 /* Unprotect */ 00381 RtlLeaveCriticalSection(&RtlCriticalSectionLock); 00382 00383 if (CriticalSection->DebugInfo) 00384 { 00385 /* Free it */ 00386 RtlpFreeDebugInfo(CriticalSection->DebugInfo); 00387 } 00388 00389 /* Wipe it out */ 00390 RtlZeroMemory(CriticalSection, sizeof(RTL_CRITICAL_SECTION)); 00391 00392 /* Return */ 00393 return Status; 00394 } 00395 00396 /*++ 00397 * RtlSetCriticalSectionSpinCount 00398 * @implemented NT4 00399 * 00400 * Sets the spin count for a critical section. 00401 * 00402 * Params: 00403 * CriticalSection - Critical section to set the spin count for. 00404 * 00405 * SpinCount - Spin count for the critical section. 00406 * 00407 * Returns: 00408 * STATUS_SUCCESS. 00409 * 00410 * Remarks: 00411 * SpinCount is ignored on single-processor systems. 00412 * 00413 *--*/ 00414 DWORD 00415 NTAPI 00416 RtlSetCriticalSectionSpinCount(PRTL_CRITICAL_SECTION CriticalSection, 00417 ULONG SpinCount) 00418 { 00419 ULONG OldCount = (ULONG)CriticalSection->SpinCount; 00420 00421 /* Set to parameter if MP, or to 0 if this is Uniprocessor */ 00422 CriticalSection->SpinCount = (NtCurrentPeb()->NumberOfProcessors > 1) ? SpinCount : 0; 00423 return OldCount; 00424 } 00425 00426 /*++ 00427 * RtlEnterCriticalSection 00428 * @implemented NT4 00429 * 00430 * Waits to gain ownership of the critical section. 00431 * 00432 * Params: 00433 * CriticalSection - Critical section to wait for. 00434 * 00435 * Returns: 00436 * STATUS_SUCCESS. 00437 * 00438 * Remarks: 00439 * Uses a fast-path unless contention happens. 00440 * 00441 *--*/ 00442 NTSTATUS 00443 NTAPI 00444 RtlEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection) 00445 { 00446 HANDLE Thread = (HANDLE)NtCurrentTeb()->ClientId.UniqueThread; 00447 00448 /* Try to Lock it */ 00449 if (InterlockedIncrement(&CriticalSection->LockCount) != 0) { 00450 00451 /* 00452 * We've failed to lock it! Does this thread 00453 * actually own it? 00454 */ 00455 if (Thread == CriticalSection->OwningThread) { 00456 00457 /* You own it, so you'll get it when you're done with it! No need to 00458 use the interlocked functions as only the thread who already owns 00459 the lock can modify this data. */ 00460 CriticalSection->RecursionCount++; 00461 return STATUS_SUCCESS; 00462 } 00463 00464 /* NOTE - CriticalSection->OwningThread can be NULL here because changing 00465 this information is not serialized. This happens when thread a 00466 acquires the lock (LockCount == 0) and thread b tries to 00467 acquire it as well (LockCount == 1) but thread a hasn't had a 00468 chance to set the OwningThread! So it's not an error when 00469 OwningThread is NULL here! */ 00470 00471 /* We don't own it, so we must wait for it */ 00472 RtlpWaitForCriticalSection(CriticalSection); 00473 } 00474 00475 /* Lock successful. Changing this information has not to be serialized because 00476 only one thread at a time can actually change it (the one who acquired 00477 the lock)! */ 00478 CriticalSection->OwningThread = Thread; 00479 CriticalSection->RecursionCount = 1; 00480 return STATUS_SUCCESS; 00481 } 00482 00483 /*++ 00484 * RtlInitializeCriticalSection 00485 * @implemented NT4 00486 * 00487 * Initialises a new critical section. 00488 * 00489 * Params: 00490 * CriticalSection - Critical section to initialise 00491 * 00492 * Returns: 00493 * STATUS_SUCCESS. 00494 * 00495 * Remarks: 00496 * Simply calls RtlInitializeCriticalSectionAndSpinCount 00497 * 00498 *--*/ 00499 NTSTATUS 00500 NTAPI 00501 RtlInitializeCriticalSection(PRTL_CRITICAL_SECTION CriticalSection) 00502 { 00503 /* Call the Main Function */ 00504 return RtlInitializeCriticalSectionAndSpinCount(CriticalSection, 0); 00505 } 00506 00507 /*++ 00508 * RtlInitializeCriticalSectionAndSpinCount 00509 * @implemented NT4 00510 * 00511 * Initialises a new critical section. 00512 * 00513 * Params: 00514 * CriticalSection - Critical section to initialise 00515 * 00516 * SpinCount - Spin count for the critical section. 00517 * 00518 * Returns: 00519 * STATUS_SUCCESS. 00520 * 00521 * Remarks: 00522 * SpinCount is ignored on single-processor systems. 00523 * 00524 *--*/ 00525 NTSTATUS 00526 NTAPI 00527 RtlInitializeCriticalSectionAndSpinCount(PRTL_CRITICAL_SECTION CriticalSection, 00528 ULONG SpinCount) 00529 { 00530 PRTL_CRITICAL_SECTION_DEBUG CritcalSectionDebugData; 00531 00532 /* First things first, set up the Object */ 00533 DPRINT("Initializing Critical Section: %p\n", CriticalSection); 00534 CriticalSection->LockCount = -1; 00535 CriticalSection->RecursionCount = 0; 00536 CriticalSection->OwningThread = 0; 00537 CriticalSection->SpinCount = (NtCurrentPeb()->NumberOfProcessors > 1) ? SpinCount : 0; 00538 CriticalSection->LockSemaphore = 0; 00539 00540 /* Allocate the Debug Data */ 00541 CritcalSectionDebugData = RtlpAllocateDebugInfo(); 00542 DPRINT("Allocated Debug Data: %p inside Process: %p\n", 00543 CritcalSectionDebugData, 00544 NtCurrentTeb()->ClientId.UniqueProcess); 00545 00546 if (!CritcalSectionDebugData) { 00547 00548 /* This is bad! */ 00549 DPRINT1("Couldn't allocate Debug Data for: %p\n", CriticalSection); 00550 return STATUS_NO_MEMORY; 00551 } 00552 00553 /* Set it up */ 00554 CritcalSectionDebugData->Type = RTL_CRITSECT_TYPE; 00555 CritcalSectionDebugData->ContentionCount = 0; 00556 CritcalSectionDebugData->EntryCount = 0; 00557 CritcalSectionDebugData->CriticalSection = CriticalSection; 00558 CritcalSectionDebugData->Flags = 0; 00559 CriticalSection->DebugInfo = CritcalSectionDebugData; 00560 00561 /* 00562 * Add it to the List of Critical Sections owned by the process. 00563 * If we've initialized the Lock, then use it. If not, then probably 00564 * this is the lock initialization itself, so insert it directly. 00565 */ 00566 if ((CriticalSection != &RtlCriticalSectionLock) && (RtlpCritSectInitialized)) { 00567 00568 DPRINT("Securely Inserting into ProcessLocks: %p, %p, %p\n", 00569 &CritcalSectionDebugData->ProcessLocksList, 00570 CriticalSection, 00571 &RtlCriticalSectionList); 00572 00573 /* Protect List */ 00574 RtlEnterCriticalSection(&RtlCriticalSectionLock); 00575 00576 /* Add this one */ 00577 InsertTailList(&RtlCriticalSectionList, &CritcalSectionDebugData->ProcessLocksList); 00578 00579 /* Unprotect */ 00580 RtlLeaveCriticalSection(&RtlCriticalSectionLock); 00581 00582 } else { 00583 00584 DPRINT("Inserting into ProcessLocks: %p, %p, %p\n", 00585 &CritcalSectionDebugData->ProcessLocksList, 00586 CriticalSection, 00587 &RtlCriticalSectionList); 00588 00589 /* Add it directly */ 00590 InsertTailList(&RtlCriticalSectionList, &CritcalSectionDebugData->ProcessLocksList); 00591 } 00592 00593 return STATUS_SUCCESS; 00594 } 00595 00596 /*++ 00597 * RtlLeaveCriticalSection 00598 * @implemented NT4 00599 * 00600 * Releases a critical section and makes if available for new owners. 00601 * 00602 * Params: 00603 * CriticalSection - Critical section to release. 00604 * 00605 * Returns: 00606 * STATUS_SUCCESS. 00607 * 00608 * Remarks: 00609 * If another thread was waiting, the slow path is entered. 00610 * 00611 *--*/ 00612 NTSTATUS 00613 NTAPI 00614 RtlLeaveCriticalSection(PRTL_CRITICAL_SECTION CriticalSection) 00615 { 00616 #ifndef NDEBUG 00617 HANDLE Thread = (HANDLE)NtCurrentTeb()->ClientId.UniqueThread; 00618 00619 /* In win this case isn't checked. However it's a valid check so it should only 00620 be performed in debug builds! */ 00621 if (Thread != CriticalSection->OwningThread) 00622 { 00623 DPRINT1("Releasing critical section not owned!\n"); 00624 return STATUS_INVALID_PARAMETER; 00625 } 00626 #endif 00627 00628 /* Decrease the Recursion Count. No need to do this atomically because only 00629 the thread who holds the lock can call this function (unless the program 00630 is totally screwed... */ 00631 if (--CriticalSection->RecursionCount) { 00632 00633 /* Someone still owns us, but we are free. This needs to be done atomically. */ 00634 InterlockedDecrement(&CriticalSection->LockCount); 00635 00636 } else { 00637 00638 /* Nobody owns us anymore. No need to do this atomically. See comment 00639 above. */ 00640 CriticalSection->OwningThread = 0; 00641 00642 /* Was someone wanting us? This needs to be done atomically. */ 00643 if (-1 != InterlockedDecrement(&CriticalSection->LockCount)) { 00644 00645 /* Let him have us */ 00646 RtlpUnWaitCriticalSection(CriticalSection); 00647 } 00648 } 00649 00650 /* Sucessful! */ 00651 return STATUS_SUCCESS; 00652 } 00653 00654 /*++ 00655 * RtlTryEnterCriticalSection 00656 * @implemented NT4 00657 * 00658 * Attemps to gain ownership of the critical section without waiting. 00659 * 00660 * Params: 00661 * CriticalSection - Critical section to attempt acquiring. 00662 * 00663 * Returns: 00664 * TRUE if the critical section has been acquired, FALSE otherwise. 00665 * 00666 * Remarks: 00667 * None 00668 * 00669 *--*/ 00670 BOOLEAN 00671 NTAPI 00672 RtlTryEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection) 00673 { 00674 /* Try to take control */ 00675 if (InterlockedCompareExchange(&CriticalSection->LockCount, 00676 0, 00677 -1) == -1) { 00678 00679 /* It's ours */ 00680 CriticalSection->OwningThread = NtCurrentTeb()->ClientId.UniqueThread; 00681 CriticalSection->RecursionCount = 1; 00682 return TRUE; 00683 00684 } else if (CriticalSection->OwningThread == NtCurrentTeb()->ClientId.UniqueThread) { 00685 00686 /* It's already ours */ 00687 InterlockedIncrement(&CriticalSection->LockCount); 00688 CriticalSection->RecursionCount++; 00689 return TRUE; 00690 } 00691 00692 /* It's not ours */ 00693 return FALSE; 00694 } 00695 00696 /* EOF */ Generated on Tue May 15 05:00:36 2012 for ReactOS by
1.6.3
|