from system view - this is correct, safe, ok call CloseHandle
at any time. this independed from are exist some I/O request in progress on file to which this handle pointed. even more call CloseHandle
the best way (begin from vista, on xp - this is driver depended, usual I/O canceled in this case too) cancel all pending I/O requests
What happens if one thread closes the handle while another currently
has an I/O in progress
if this is not last handle to file - nothing hapen. otherwise (this is usual case and think you mean exactly this case - you have single handle open on file) the IRP_MJ_CLEANUP
handler will be called in driver (which create device objec, on which your file created). then already is no general and common answer - driver specific. however most windows built-in drivers at this point just complete with some error (usual with STATUS_CANCELLED
but not always, say npfs.sys (pipe driver) use STATUS_PIPE_BROKEN
error here) all pending requests and return control. on xp/2003 - this is all. so this is driver duty complete all pending requests at this point. however some 3-th party drivers can and not do this - so some I/O can still be pending (undefined time) after handle closed. but begin from vista here was major change. the FILE_OBJECT
was extended (on xp/2003 the CompletionContext
was last field) - added IrpList
. now inside FILE_OBJECT
system mantain list of some active I/O request on file. when we send asynchronous IRP request it queued to Thread (always before vista) or to FILE_OBJECT
- if IOCP port accosiated with this file object, ApcContext not 0 (from win32 view - pointer to OVERLAPPED
not 0) and no user event in IRP (from win32 view OVERLAPPED.hEvent == 0). this affect when system cancel IRP - when thread exit or when last file handle closed. in vista, after IRP_MJ_CLEANUP
handler return - system check IrpList
and if it not empty - call IoCancelIrp
for every Irp from this list. of course than again driver depended, but all well design drivers (including all microsoft drivers) when return pending for request (Irp) mandatory register CancelRoutine to be called if the specified IRP is canceled. this routine called when system call IoCancelIrp
(if not removed before) and inside this routine driver complete request with STATUS_CANCELLED
. this mean that if driver not complete Irp inside IRP_MJ_CLEANUP
- system by self cancel this irp just after IRP_MJ_CLEANUP
return and this cause that driver anyway complete this Irp inside it CancelRoutine
so conclusion - close last handle on file (last call CloseHandle
) is effective way for cancell all I/O requests on this file (with some exceptions on xp and only for 3-rd party drivers).this is legal, safe and effective (if we want cancell al io and close file)
Does CloseHandle block until the write finishes?
because inside this call called driver supplied IRP_MJ_CLEANUP
handler - can be all. even block. but if say about built-in windows drivers - this never happens. and even for 3-rd party drivers - i never view this behaviour. so in practic - CloseHandle
not block and not wait when all I/O requests on file finished
so call CloseHandle
is safe at any time, but here exist another problem, not related to system design but related to your code design. in your code (and usual) handle is shared resourse - several different threads will use it. and this is usual problem, when object (handle in your case) used by several threads - who and when must close handle. what happens if one your thread close handle when another thread begin I/O request with this handle, after it was closed ? you or got STATUS_INVALID_HANDLE
from api call, if handle not valid at this time. but possible and more bad case. after you close handle - some another code can create another object. and for new created object will be assigned the last closed handle ( system use stack model for free handles). as result handle can be already valid when you begin I/O operation (after close handle from another thread), but.. this handle already will point to another object. if this is not file object handle (say event or thread handle) - you got STATUS_OBJECT_TYPE_MISMATCH
from I/O request. in whe worst case - this will be file object handle, but for another file. so you can begin I/O operation with another file and unpredicatable result (say write data to another, arbitrary file).
so you need somehow protect or synchronize shared handle usage and closing, between threads. first think here - use reference counting for handle and call CloseHandle
when no more reference. but this is not usable on practic - if say some thread permanent do I/O on file(pipe) (say read from it, when read complete - just call another read and so on) - we have no chance call CloseHandle
from another thread.
for concrete example we call ReadDirectoryChangesW
for file asynchronous. when this call complete - just again call ReadDirectoryChangesW
and so on. gow break this loop ? set some stop flag and call CancelIoEx
? but possible that CancelIoEx
will be called in between 2 calls of ReadDirectoryChangesW
, whe really no any I/O on file. so it nothing cancel.. effective way stop this - call CloseHandle
, but if we do this without protection - exist risk that next call to ReadDirectoryChangesW
will be use arbitrary object (handle value will be the same, but it will be invalid or point to another object). use reference counting - not way here - who permanent call ReadDirectoryChangesW
need have own reference and reference never rich 0. so we never call CloseHandle
and never break loop.
the effective and nice solution here - use Run-Down Protection for handle. unfortunatelly not api in user mode for support this, but not hard yourself implement this
possible usage (if implement exactly how this implemented in kernel)
threads before use m_hFile
, protected by m_RunRef
, do next code
ULONG dwError = ERROR_INVALID_HANDLE;
if (AcquireRundownProtection(&m_RunRef))
{
// use m_hFile in some I/O request
// for example
// dwError = ReadDirectoryChangesW(m_hFile, ..);
ReleaseRundownProtection(&m_RunRef);
}
thread which want close handle do next
WaitForRundownProtectionRelease(&m_RunRef);
CloseHandle(m_hFile);
note that WaitForRundownProtectionRelease
wait not when all I/O complete but only when all another threads exit from AcquireRundownProtection / ReleaseRundownProtection
block. also after call WaitForRundownProtectionRelease
AcquireRundownProtection
always return false - so prevent new acquire and what for release existing.
however i prefer another implementation for begin rundown:
asynchronous call to BeginRundown(&m_RunRef)
(after which all new AcquireRundownProtection
return false) - but not wait at this place. end when rundown completed - callback will be called. and inside this callback we safe call CloseHandle
- no new usage of handle at this place, and all old usage of handle (in io call) already completed. note - usage of handle completed - not mean I/O completed - it in progress maybe. but handle need only for start I/O