/// <summary> /// Persist all dirty in-memory pages (in all snapshots) and clear local pages list (even clean pages) /// </summary> private int PersistDirtyPages(bool commit) { var dirty = 0; // inner method to get all dirty pages IEnumerable <PageBuffer> source() { // get all dirty pages from all write snapshots // can include (or not) collection pages // update DirtyPagesLog inside transPage for all dirty pages was write on disk var pages = _snapshots.Values .Where(x => x.Mode == LockMode.Write) .SelectMany(x => x.GetWritablePages(true, commit)); // mark last dirty page as confirmed only if there is no header change in commit var markLastAsConfirmed = commit && _transPages.HeaderChanged == false; // neet use "IsLast" method to get when loop are last item foreach (var page in pages.IsLast()) { // update page transactionID page.Item.TransactionID = _transactionID; // if last page, mask as confirm (only if a real commit and no header changes) if (page.IsLast) { page.Item.IsConfirmed = markLastAsConfirmed; } // if current page is last deleted page, point this page to last free if (_transPages.LastDeletedPageID == page.Item.PageID && commit) { ENSURE(_transPages.HeaderChanged, "must header be in lock"); ENSURE(page.Item.PageType == PageType.Empty, "must be marked as deleted page"); // join existing free list pages into new list of deleted pages page.Item.NextPageID = _header.FreeEmptyPageList; // and now, set header free list page to this new list _header.FreeEmptyPageList = _transPages.FirstDeletedPageID; } var buffer = page.Item.UpdateBuffer(); // buffer position will be set at end of file (it´s always log file) yield return(buffer); dirty++; _transPages.DirtyPages[page.Item.PageID] = new PagePosition(page.Item.PageID, buffer.Position); } // in commit with header page change, last page will be header if (commit && _transPages.HeaderChanged) { lock (_header) { // update this confirm page with current transactionID _header.TransactionID = _transactionID; // this header page will be marked as confirmed page in log file _header.IsConfirmed = true; // invoke all header callbacks (new/drop collections) _transPages.OnCommit(_header); // clone header page var buffer = _header.UpdateBuffer(); var clone = _disk.NewPage(); // mem copy from current header to new header clone Buffer.BlockCopy(buffer.Array, buffer.Offset, clone.Array, clone.Offset, clone.Count); // persist header in log file yield return(clone); } } }; // write all dirty pages, in sequence on log-file and store references into log pages on transPages // (works only for Write snapshots) var count = _disk.WriteAsync(source()); // now, discard all clean pages (because those pages are writable and must be readable) // from write snapshots _disk.DiscardCleanPages(_snapshots.Values .Where(x => x.Mode == LockMode.Write) .SelectMany(x => x.GetWritablePages(false, commit)) .Select(x => x.Buffer)); return(count); }