{"id":4663,"date":"2013-04-15T07:00:00","date_gmt":"2013-04-15T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2013\/04\/15\/using-opportunistic-locks-to-get-out-of-the-way-if-somebody-wants-the-file\/"},"modified":"2013-04-15T07:00:00","modified_gmt":"2013-04-15T07:00:00","slug":"using-opportunistic-locks-to-get-out-of-the-way-if-somebody-wants-the-file","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20130415-00\/?p=4663","title":{"rendered":"Using opportunistic locks to get out of the way if somebody wants the file"},"content":{"rendered":"<p><P>\nOpportunistic locks allow you to be notified when somebody else\ntries to access a file you have open.\nThis is usually done if you want to use a file\nprovided nobody else wants it.\n<\/P>\n<P>\nFor example, you might be a search indexer that wants to extract\ninformation from a file, but if somebody opens the file for writing,\nyou don&#8217;t want them to get <I>Sharing Violation<\/I>.\nInstead, you want to stop indexing the file and let the other person\nget their write access.\n<\/P>\n<P>\nOr you might be a file viewer application\nlike\n<A HREF=\"http:\/\/msdn.microsoft.com\/en-US\/library\/f7dy01k1(v=VS.80).aspx\">\nildasm<\/A>,\nand you want to let the user update the file (in ildasm&#8217;s case,\nrebuild the assembly) even though you&#8217;re viewing it.\n(Otherwise, they will get an error from the compiler saying\n&#8220;Cannot open file for output.&#8221;)\n<\/P>\n<P>\nOr you might be Explorer, and you want to abandon generating\nthe preview for a file\n<A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2012\/09\/07\/10347136.aspx\">\nif somebody tries to delete it<\/A>.\n<\/P>\n<P>\n(Rats I fell into the trap of trying to motivate a Little Program.)\n<\/P>\n<P>\nOkay, enough motivation. Here&#8217;s the program:\n<\/P>\n<PRE>\n#include &lt;windows.h&gt;\n#include &lt;winioctl.h&gt;\n#include &lt;stdio.h&gt;<\/p>\n<p>OVERLAPPED g_o;<\/p>\n<p>REQUEST_OPLOCK_INPUT_BUFFER g_inputBuffer = {\n  REQUEST_OPLOCK_CURRENT_VERSION,\n  sizeof(g_inputBuffer),\n  OPLOCK_LEVEL_CACHE_READ | OPLOCK_LEVEL_CACHE_HANDLE,\n  REQUEST_OPLOCK_INPUT_FLAG_REQUEST,\n};<\/p>\n<p>REQUEST_OPLOCK_OUTPUT_BUFFER g_outputBuffer = {\n  REQUEST_OPLOCK_CURRENT_VERSION,\n  sizeof(g_outputBuffer),\n};<\/p>\n<p>int __cdecl wmain(int argc, wchar_t **argv)\n{\n  g_o.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);<\/p>\n<p>  HANDLE hFile = CreateFileW(argv[1], GENERIC_READ,\n    FILE_SHARE_READ, nullptr, OPEN_EXISTING,\n    FILE_FLAG_OVERLAPPED, nullptr);\n  if (hFile == INVALID_HANDLE_VALUE) {\n    return 0;\n  }<\/p>\n<p>  DeviceIoControl(hFile, FSCTL_REQUEST_OPLOCK,\n      &amp;g_inputBuffer, sizeof(g_inputBuffer),\n      &amp;g_outputBuffer, sizeof(g_outputBuffer),\n      nullptr, &amp;g_o);\n  if (GetLastError() != ERROR_IO_PENDING) {\n    \/\/ oplock failed\n    return 0;\n  }<\/p>\n<p>  DWORD dwBytes;\n  if (!GetOverlappedResult(hFile, &amp;g_o, &amp;dwBytes, TRUE)) {\n    \/\/ oplock failed\n    return 0;\n  }<\/p>\n<p>  printf(&#8220;Cleaning up because somebody wants the file&#8230;\\n&#8221;);\n  Sleep(1000); \/\/ pretend this takes some time<\/p>\n<p>  printf(&#8220;Closing file handle\\n&#8221;);\n  CloseHandle(hFile);<\/p>\n<p>  CloseHandle(g_o.hEvent);<\/p>\n<p>  return 0;\n}\n<\/PRE>\n<P>\nRun this program with the name of an existing file\non the command line,\nsay\n<CODE>scratch x.txt<\/CODE>.\nThe program will wait.\n<\/P>\n<P>\nIn another command window, run the command\n<CODE>type x.txt<\/CODE>.\nThe program keeps waiting.\n<\/P>\n<P>\nNext, run the command\n<CODE>echo hello &gt; x.txt<\/CODE>.\nNow things get interesting.\n<\/P>\n<P>\nWhen the command prompt opens <CODE>x.txt<\/CODE> for writing,\nthe <CODE>Device&shy;Io&shy;Control<\/CODE> call completes.\nAt this point we print the <TT>Cleaning up&#8230;<\/TT> message.\n<\/P>\n<P>\nTo simulate the program taking a little while to clean up,\nwe sleep for one second.\nObserve that the command prompt\n<I>has not yet returned<\/I>.\nInstead of immediately failing the request to open for writing\nwith a sharing violation,\nthe kernel puts the open request on hold to give our program\ntime to clean up and close our handle.\n<\/P>\n<P>\nFinally, our simulated clean-up is complete, and we close\nthe handle.\nAt this point, the kernel allows the command processor to proceed\nand open the file for writing so it can write <TT>hello<\/TT>\ninto it.\n<\/P>\n<P>\nThat&#8217;s the basics of opportunistic locks,\nbut your program will almost certainly not be structured this way.\nYou will probably not wait synchronously on the overlapped I\/O\nbut rather have the completion queued up to a completion function,\nan I\/O completion port,\nor have a thread pool task listen on the event handle.\nWhen you do that, remember that you need to keep\nthe <CODE>OVERLAPPED<\/CODE> structure as well as the\n<CODE>REQUEST_OPLOCK_INPUT_BUFFER<\/CODE>\nand\n<CODE>REQUEST_OPLOCK_OUTUT_BUFFER<\/CODE>\nstructures valid until the I\/O completes.\n<\/P>\n<P>\n(You may find the\n<CODE>Cancel&shy;Io<\/CODE> function handy to try to accelerate\nthe clean-up of the file handle and any other actions that\nare dependent upon it.)\n<\/P>\n<P>\nYou can read more about\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/aa365433.aspx\">\nopportunistic locks on MSDN<\/A>.\nNote that there are limitations on explicitly-managed\nopportunistic locks;\nfor example, they don&#8217;t work across the network.\n<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Opportunistic locks allow you to be notified when somebody else tries to access a file you have open. This is usually done if you want to use a file provided nobody else wants it. For example, you might be a search indexer that wants to extract information from a file, but if somebody opens the [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-4663","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Opportunistic locks allow you to be notified when somebody else tries to access a file you have open. This is usually done if you want to use a file provided nobody else wants it. For example, you might be a search indexer that wants to extract information from a file, but if somebody opens the [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/4663","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=4663"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/4663\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=4663"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=4663"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=4663"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}