{"id":664,"date":"2011-09-15T17:52:35","date_gmt":"2011-09-15T17:52:35","guid":{"rendered":"https:\/\/ibex.tech\/visualcpp\/?p=664"},"modified":"2022-02-17T06:24:04","modified_gmt":"2022-02-17T06:24:04","slug":"write-file-thread-safe","status":"publish","type":"post","link":"https:\/\/ibex.tech\/visualcpp\/file-input-output-serialization\/write-file-thread-safe","title":{"rendered":"Write File Thread Safe"},"content":{"rendered":"<p>This example is based on writing a file from asynchronously received TCP packets. \u00a0FileStream-&gt;Write is not thread safe, so this approach deals with letting file write occur in the background and waiting for it to complete before writing the next received block of data bytes.<\/p>\n<h4>In your header file<\/h4>\n<pre><code>\r\n\tFileStream ^ClientRxFileStream1;\r\n\tIAsyncResult ^ClientRxFileStreamAsyncResult1;\r\n\tarray&lt;Byte&gt; ^ClientReceiveFileWriteBytes;\r\n<\/code><\/pre>\n<h4>Receiving the first packet of bytes<\/h4>\n<pre><code>\r\n\t\/\/Create the file to write to\r\n\tClientRxFileStream1 = gcnew FileStream(ClientRxFilename, FileMode::Create, FileAccess::Write, FileShare::None);\r\n\r\n\t\/\/Copy rx data to memory so it can be written to the file in the background\r\n\tClientReceiveFileWriteBytes = gcnew array&lt;Byte&gt;(so-&gt;bufSize);\r\n\tArray::Copy(so-&gt;message, DataStart, ClientReceiveFileWriteBytes, 0, so-&gt;bufSize);\r\n\r\n\t\/\/Write it to the file in the background\r\n\tClientRxFileStreamAsyncResult1 = ClientRxFileStream1-&gt;BeginWrite(ClientReceiveFileWriteBytes, 0, so-&gt;bufSize, nullptr, nullptr);\r\n\r\n<\/code><\/pre>\n<h4>Receiving subsequent packets of bytes<\/h4>\n<pre><code>\r\n\tif (ClientRxFileStream1 != nullptr)\r\n\t{\r\n\t\tif (ClientRxFileStreamAsyncResult1 != nullptr)\r\n\t\t{\r\n\t\t\tClientRxFileStreamAsyncResult1-&gt;AsyncWaitHandle-&gt;WaitOne(10000);\t\/\/Block current thread until operation completes, timeout in mS\r\n\r\n\t\t\tClientRxFileStream1-&gt;EndWrite(ClientRxFileStreamAsyncResult1);\t\t\/\/Last step to the write\r\n\r\n\t\t\tif (ClientRxFileStreamAsyncResult1-&gt;IsCompleted)\t\t\t\t\t\/\/Make sure the write really completed.\r\n\t\t\t{\r\n\t\t\t\t\/\/Copy rx data to memory so it can be written to the file in the background\r\n\t\t\t\tClientReceiveFileWriteBytes = gcnew array&lt;Byte&gt;(Convert::ToInt32(Size));\r\n\t\t\t\tArray::Copy(so-&gt;message, 0, ClientReceiveFileWriteBytes, 0, Convert::ToInt32(Size));\r\n\r\n\t\t\t\t\/\/Write it to the file in the background\r\n\t\t\t\tClientRxFileStreamAsyncResult1 = ClientRxFileStream1-&gt;BeginWrite(ClientReceiveFileWriteBytes, 0, Convert::ToInt32(Size), nullptr, nullptr);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t\/\/ERROR HERE!\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n<\/code><\/pre>\n<h4>Once All Bytes Received<\/h4>\n<pre><code>\r\n\tif (ClientRxFileStreamAsyncResult1 != nullptr)\r\n\t{\r\n\t\tClientRxFileStreamAsyncResult1-&gt;AsyncWaitHandle-&gt;WaitOne(10000);\t\/\/Block current thread until operation completes, timeout in mS\r\n\t\tClientRxFileStream1-&gt;EndWrite(ClientRxFileStreamAsyncResult1);\r\n\t}\r\n\r\n\tClientRxFileStream1-&gt;Close();\r\n\tClientRxFileStream1 = nullptr;\r\n\tClientRxFileStreamAsyncResult1 = nullptr;\r\n<\/code><\/pre>\n<h4>Working Example Of Doing It With Multiple Clients<\/h4>\n<h5>In your .h file<\/h5>\n<pre><code>\r\n\tusing namespace System::Collections::Generic;\r\n\r\n\tref class ClientRxFileStreamHandler\r\n\t{\r\n\tpublic:\r\n\t\tproperty String ^IpAddress;\r\n\t\tproperty String ^FileName;\r\n\t\tDateTime ClientLastActivity;\r\n\t\tFileStream ^ClientRxFileStream;\r\n\t\tIAsyncResult ^ClientRxFileStreamAsyncResult;\r\n\t\tarray<Byte> ^ClientReceiveFileWriteBytes;\r\n\t};\r\n\r\n\tList&lt;ClientRxFileStreamHandler^&gt; ^OurClientRxFileStreamHandlers;\r\n<\/code><\/pre>\n<h5>In your .c file<\/h5>\n<pre><code>\r\n\/\/***********************\r\n\/\/***** CONSTRUCTOR *****\r\n\/\/***********************\r\n\tOurClientRxFileStreamHandlers = gcnew List<ClientRxFileStreamHandler^>(0);\r\n\r\n\/\/***********************************\r\n\/\/***** GOT NEXT PACKET OF DATA *****\r\n\/\/***********************************\r\n\t\/\/----- FIND THIS CLIENTS FILE STREAM HANDLER OR CREATE A NEW ONE FOR IT -----\r\n\tIndex = -1;\r\n\tfor (Count = 0; Count &lt; OurClientRxFileStreamHandlers-&gt;Count; Count++)\t\t\/\/If we already have this client then overwrite it\r\n\t{\r\n\t\tif ((OurClientRxFileStreamHandlers[Count]-&gt;IpAddress == ClientIpAddress) && (OurClientRxFileStreamHandlers[Count]-&gt;FileName == FileName))\r\n\t\t{\r\n\t\t\t\/\/----- EXISTING CLIENT -----\r\n\t\t\tIndex = Count;\r\n\r\n\t\t\t\/\/Ensure last write has completed\r\n\t\t\tif ((OurClientRxFileStreamHandlers[Index]-&gt;ClientRxFileStream != nullptr) && (OurClientRxFileStreamHandlers[Index]-&gt;ClientRxFileStreamAsyncResult != nullptr))\r\n\t\t\t{\r\n\t\t\t\tOurClientRxFileStreamHandlers[Index]-&gt;ClientRxFileStreamAsyncResult-&gt;AsyncWaitHandle-&gt;WaitOne(10000);\t\/\/Block current thread until operation completes, timeout in mS\r\n\t\t\t\tOurClientRxFileStreamHandlers[Index]-&gt;ClientRxFileStream-&gt;EndWrite(OurClientRxFileStreamHandlers[Index]-&gt;ClientRxFileStreamAsyncResult);\t\t\/\/Last step to the write\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\tif (Index == -1)\r\n\t{\r\n\t\t\/\/----- NEW CLIENT -----\r\n\t\tOurClientRxFileStreamHandlers-&gt;Add(gcnew ClientRxFileStreamHandler);\r\n\t\tIndex = OurClientRxFileStreamHandlers-&gt;Count - 1;\r\n\r\n\t\tOurClientRxFileStreamHandlers[Index]-&gt;IpAddress = ClientIpAddress;\r\n\t\tOurClientRxFileStreamHandlers[Index]-&gt;FileName = FileName;\r\n\t}\r\n\tOurClientRxFileStreamHandlers[Index]-&gt;ClientLastActivity = DateTime::Now;\r\n\r\n\t\/\/----- QUICKLY CHECK FOR REMOVING ANY OLD CLIENTS THAT HAVE VANISHED -----\r\n\tDateTime RemoveBeforeDateTime = DateTime::Now - TimeSpan(0, 5, 0);\t\t\/\/We delete clients &gt; 5 mins since last activity\r\n\tfor (Count = 0; Count &lt; OurClientRxFileStreamHandlers-&gt;Count; Count++)\t\t\/\/If we already have this client then overwrite it\r\n\t{\r\n\t\tif (OurClientRxFileStreamHandlers[Count]-&gt;ClientLastActivity &lt; DeleteBeforeDateTime)\r\n\t\t{\r\n\t\t\t\/\/REMOVE THIS CLIENT FROM THE ARRAY\r\n\t\t\tOurClientRxFileStreamHandlers[Count]-&gt;ClientRxFileStream-&gt;Close();\r\n\t\t\tOurClientRxFileStreamHandlers[Count]-&gt;ClientRxFileStream = nullptr;\r\n\t\t\tOurClientRxFileStreamHandlers[Count]-&gt;ClientRxFileStreamAsyncResult = nullptr;\r\n\r\n\t\t\tOurClientRxFileStreamHandlers-&gt;RemoveAt(Count);\r\n\t\t\tCount--;\r\n\t\t}\r\n\t}\r\n\r\n\tif ((ClientRxFilename != \"\") && (DataLength &gt; 0))\r\n\t{\r\n\t\tif (File::Exists(ClientRxFilename))\r\n\t\t{\r\n\t\t\tSystem::IO::FileInfo ^FileInfo1 = gcnew System::IO::FileInfo(ClientRxFilename);\r\n\t\t\tFileSize = FileInfo1-&gt;Length;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tFileSize = 0;\r\n\t\t}\r\n\r\n\t\tif (DataStart == FileSize)\t\t\/\/Data start must match the current file size\r\n\t\t{\r\n\t\t\t\/\/----- WRITE THE FILE DATA -----\r\n\t\t\tif (OurClientRxFileStreamHandlers[Index]-&gt;ClientRxFileStream == nullptr)\r\n\t\t\t{\r\n\t\t\t\tif (FileSize == 0)\r\n\t\t\t\t\tOurClientRxFileStreamHandlers[Index]-&gt;ClientRxFileStream = gcnew FileStream(ClientRxFilename, FileMode::Create, FileAccess::Write, FileShare::None);\r\n\t\t\t\telse\r\n\t\t\t\t\tOurClientRxFileStreamHandlers[Index]-&gt;ClientRxFileStream = gcnew FileStream(ClientRxFilename, FileMode::Append, FileAccess::Write, FileShare::None);\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tOurClientRxFileStreamHandlers[Index]-&gt;ClientReceiveFileWriteBytes = gcnew array&lt;Byte&gt;(RxLength - PacketDataStart);\r\n\t\t\tArray::Copy(RxData, PacketDataStart, OurClientRxFileStreamHandlers[Index]-&gt;ClientReceiveFileWriteBytes, 0, (RxLength - PacketDataStart));\r\n\r\n\t\t\tOurClientRxFileStreamHandlers[Index]-&gt;ClientRxFileStreamAsyncResult = OurClientRxFileStreamHandlers[Index]-&gt;ClientRxFileStream-&gt;BeginWrite(OurClientRxFileStreamHandlers[Index]-&gt;ClientReceiveFileWriteBytes, 0, (RxLength - PacketDataStart), nullptr, nullptr);\r\n\r\n\t\t\tSuccess = true;\r\n\t\t}\r\n\t}\r\n\telse if ((FileName != \"\") && (DataLength == 0))\r\n\t{\r\n\t\t\/\/----- END OF FILE -----\r\n\t\t\/\/Close it and remove client\r\n\t\tif (OurClientRxFileStreamHandlers[Index]-&gt;ClientRxFileStream != nullptr)\r\n\t\t{\r\n\t\t\tOurClientRxFileStreamHandlers[Index]-&gt;ClientRxFileStream-&gt;Close();\r\n\t\t\tOurClientRxFileStreamHandlers[Index]-&gt;ClientRxFileStream = nullptr;\r\n\t\t\tOurClientRxFileStreamHandlers[Index]-&gt;ClientRxFileStreamAsyncResult = nullptr;\r\n\t\t\tOurClientRxFileStreamHandlers-&gt;RemoveAt(Index);\r\n\t\t}\r\n\t\tSuccess = true;\r\n\t}\r\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>This example is based on writing a file from asynchronously received TCP packets. \u00a0FileStream-&gt;Write is not thread safe, so this approach deals with letting file write occur in the background and waiting for it to complete before writing the next received block of data bytes. In your header file FileStream ^ClientRxFileStream1; IAsyncResult ^ClientRxFileStreamAsyncResult1; array&lt;Byte&gt; ^ClientReceiveFileWriteBytes; [&hellip;]<\/p>\n","protected":false},"author":5,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[29],"tags":[],"class_list":["post-664","post","type-post","status-publish","format-standard","hentry","category-file-input-output-serialization"],"_links":{"self":[{"href":"https:\/\/ibex.tech\/visualcpp\/wp-json\/wp\/v2\/posts\/664","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ibex.tech\/visualcpp\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ibex.tech\/visualcpp\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ibex.tech\/visualcpp\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/ibex.tech\/visualcpp\/wp-json\/wp\/v2\/comments?post=664"}],"version-history":[{"count":9,"href":"https:\/\/ibex.tech\/visualcpp\/wp-json\/wp\/v2\/posts\/664\/revisions"}],"predecessor-version":[{"id":1042,"href":"https:\/\/ibex.tech\/visualcpp\/wp-json\/wp\/v2\/posts\/664\/revisions\/1042"}],"wp:attachment":[{"href":"https:\/\/ibex.tech\/visualcpp\/wp-json\/wp\/v2\/media?parent=664"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ibex.tech\/visualcpp\/wp-json\/wp\/v2\/categories?post=664"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ibex.tech\/visualcpp\/wp-json\/wp\/v2\/tags?post=664"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}