private static void RemovePeer(CommitLogMetadata metadata, Guid peerID)
 {
     metadata.Peers.Remove(peerID);
     if (metadata.Master == peerID)
     {
         metadata.Master = Guid.Empty;
     }
 }
        protected override int StartupInternal(int currentModelVersion)
        {
            m_commitLogMutex = new GlobalMutex(MutexName);
            bool createdNew;

            using (m_commitLogMutex.InitializeAndLock(out createdNew))
            {
                CreateSharedMemory(createdNew);
                using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream())
                {
                    CommitLogMetadata metadata = null;
                    if (!createdNew)
                    {
                        if (TryGetMetadata(stream, out metadata))
                        {
                            CheckExitedPeerProcesses(metadata);
                            if (m_peerProcesses.Count == 0)
                            {
                                createdNew = true;
                            }
                        }
                        else
                        {
                            createdNew = true;
                        }
                    }

                    if (createdNew)
                    {
                        metadata = new CommitLogMetadata();
                    }

                    using (Process curProcess = Process.GetCurrentProcess())
                        metadata.Peers[m_peerID] = new CommitLogPeer {
                            ProcessID = curProcess.Id, Generation = metadata.FileGeneration
                        };

                    if (metadata.Master == Guid.Empty)
                    {
                        base.LockProject();
                        metadata.Master = m_peerID;
                    }

                    int startupModelVersion = ReadInSurrogates(currentModelVersion);
                    // non-master peers cannot migrate the XML file
                    if (startupModelVersion < currentModelVersion && metadata.Master != m_peerID)
                    {
                        throw new FdoDataMigrationForbiddenException();
                    }

                    SaveMetadata(stream, metadata);

                    return(startupModelVersion);
                }
            }
        }
        protected override void WriteCommitWork(CommitWork workItem)
        {
            using (m_commitLogMutex.Lock())
            {
                base.WriteCommitWork(workItem);

                using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream())
                {
                    CommitLogMetadata metadata = GetMetadata(stream);
                    metadata.FileGeneration = metadata.Peers[m_peerID].Generation;
                    SaveMetadata(stream, metadata);
                }
            }
        }
        private static bool TryGetMetadata(MemoryMappedViewStream stream, out CommitLogMetadata metadata)
        {
            stream.Seek(0, SeekOrigin.Begin);
            int length;

            if (Serializer.TryReadLengthPrefix(stream, PrefixStyle.Base128, out length) && length > 0)
            {
                stream.Seek(0, SeekOrigin.Begin);
                metadata = Serializer.DeserializeWithLengthPrefix <CommitLogMetadata>(stream, PrefixStyle.Base128, 1);
                return(true);
            }

            metadata = null;
            return(false);
        }
 internal override void LockProject()
 {
     using (m_commitLogMutex.Lock())
     {
         base.LockProject();
         using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream())
         {
             CommitLogMetadata metadata = GetMetadata(stream);
             if (metadata.Master == Guid.Empty)
             {
                 metadata.Master = m_peerID;
                 SaveMetadata(stream, metadata);
             }
         }
     }
 }
        private bool CheckExitedPeerProcesses(CommitLogMetadata metadata)
        {
            bool changed           = false;
            var  processesToRemove = new HashSet <Process>(m_peerProcesses.Values);

            foreach (KeyValuePair <Guid, CommitLogPeer> kvp in metadata.Peers.ToArray())
            {
                if (kvp.Key == m_peerID)
                {
                    continue;
                }

                Process process;
                if (m_peerProcesses.TryGetValue(kvp.Value.ProcessID, out process))
                {
                    if (process.HasExited)
                    {
                        RemovePeer(metadata, kvp.Key);
                        changed = true;
                    }
                    else
                    {
                        processesToRemove.Remove(process);
                    }
                }
                else
                {
                    try
                    {
                        process = Process.GetProcessById(kvp.Value.ProcessID);
                        m_peerProcesses[kvp.Value.ProcessID] = process;
                    }
                    catch (ArgumentException)
                    {
                        RemovePeer(metadata, kvp.Key);
                        changed = true;
                    }
                }
            }

            foreach (Process process in processesToRemove)
            {
                m_peerProcesses.Remove(process.Id);
                process.Close();
            }
            return(changed);
        }
        private int ReadUnseenCommitRecords(CommitLogMetadata metadata, int minPeerGeneration, int startOffset, int length, List <CommitLogRecord> unseenCommits)
        {
            if (length == 0)
            {
                return(0);
            }

            int generation = metadata.Peers[m_peerID].Generation;

            using (MemoryMappedViewStream stream = m_commitLog.CreateViewStream(startOffset, length))
            {
                while (stream.Position < length)
                {
                    long startPos = stream.Position;
                    var  rec      = Serializer.DeserializeWithLengthPrefix <CommitLogRecord>(stream, PrefixStyle.Base128, 1);
                    if (rec.WriteGeneration > generation && rec.Source != m_peerID)
                    {
                        unseenCommits.Add(rec);
                    }
                    // remove the record from the commit log once all peers have seen it and it has been written to disk
                    if (rec.WriteGeneration <= minPeerGeneration && rec.WriteGeneration <= metadata.FileGeneration)
                    {
                        metadata.LogOffset  = startOffset + (int)stream.Position;
                        metadata.LogLength -= (int)(stream.Position - startPos);
                    }
                }
            }

            // if we have read everything to the end of the file, add padding to read length
            if (startOffset + length == m_settings.SharedXMLBackendCommitLogSize - metadata.Padding)
            {
                length += metadata.Padding;
            }

            // check if we've purged all records up to the end of the file. If so, wrap around to the beginning.
            if (metadata.LogOffset == m_settings.SharedXMLBackendCommitLogSize - metadata.Padding)
            {
                metadata.LogOffset  = 0;
                metadata.LogLength -= metadata.Padding;
                metadata.Padding    = 0;
            }

            return(length);
        }
		protected override int StartupInternal(int currentModelVersion)
		{
			bool createdNew;
			m_commitLogMutex = new Mutex(true, MutexName, out createdNew);
			if (!createdNew)
				m_commitLogMutex.WaitOne();
			try
			{
				CreateSharedMemory(createdNew);
				using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream())
				{
					CommitLogMetadata metadata;
					if (!createdNew && TryGetMetadata(stream, out metadata))
						CheckExitedPeerProcesses(metadata);
					else
						metadata = new CommitLogMetadata();

					using (Process curProcess = Process.GetCurrentProcess())
						metadata.Peers[m_peerID] = new CommitLogPeer { ProcessID = curProcess.Id, Generation = metadata.FileGeneration };

					if (metadata.Master == Guid.Empty)
					{
						base.LockProject();
						metadata.Master = m_peerID;
					}

					int startupModelVersion = ReadInSurrogates(currentModelVersion);
					// non-master peers cannot migrate the XML file
					if (startupModelVersion < currentModelVersion && metadata.Master != m_peerID)
						throw new FdoDataMigrationForbiddenException();

					SaveMetadata(stream, metadata);

					return startupModelVersion;
				}
			}
			finally
			{
				m_commitLogMutex.ReleaseMutex();
			}
		}
        protected override void CreateInternal()
        {
            m_commitLogMutex = new GlobalMutex(MutexName);

            using (m_commitLogMutex.InitializeAndLock())
            {
                CreateSharedMemory(true);
                var metadata = new CommitLogMetadata {
                    Master = m_peerID
                };
                using (Process curProcess = Process.GetCurrentProcess())
                    metadata.Peers[m_peerID] = new CommitLogPeer {
                        ProcessID = curProcess.Id, Generation = metadata.FileGeneration
                    };
                using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream())
                {
                    SaveMetadata(stream, metadata);
                }

                base.CreateInternal();
            }
        }
		protected override void CreateInternal()
		{
			m_commitLogMutex = new GlobalMutex(MutexName);

			using (m_commitLogMutex.InitializeAndLock())
			{
				CreateSharedMemory(true);
				var metadata = new CommitLogMetadata { Master = m_peerID };
				using (Process curProcess = Process.GetCurrentProcess())
					metadata.Peers[m_peerID] = new CommitLogPeer { ProcessID = curProcess.Id, Generation = metadata.FileGeneration };
				using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream())
				{
					SaveMetadata(stream, metadata);
				}

				base.CreateInternal();
			}
		}
		protected override void CreateInternal()
		{
			bool createdNew;
			m_commitLogMutex = new Mutex(true, MutexName, out createdNew);
			if (!createdNew)
			{
				// Mono does not close the named mutex handle until the process exits, this can cause problems
				// for some unit tests, so we disable the following check for Mono
#if !__MonoCS__
				throw new InvalidOperationException("Cannot create shared XML backend.");
#else
				m_commitLogMutex.WaitOne();
#endif
			}
			try
			{
				CreateSharedMemory(true);
				var metadata = new CommitLogMetadata { Master = m_peerID };
				using (Process curProcess = Process.GetCurrentProcess())
					metadata.Peers[m_peerID] = new CommitLogPeer { ProcessID = curProcess.Id, Generation = metadata.FileGeneration };
				using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream())
				{
					SaveMetadata(stream, metadata);
				}

				base.CreateInternal();
			}
			finally
			{
				m_commitLogMutex.ReleaseMutex();
			}
		}
		private static bool TryGetMetadata(MemoryMappedViewStream stream, out CommitLogMetadata metadata)
		{
			stream.Seek(0, SeekOrigin.Begin);
			int length;
			if (Serializer.TryReadLengthPrefix(stream, PrefixStyle.Base128, out length) && length > 0)
			{
				stream.Seek(0, SeekOrigin.Begin);
				metadata = Serializer.DeserializeWithLengthPrefix<CommitLogMetadata>(stream, PrefixStyle.Base128, 1);
				return true;
			}

			metadata = null;
			return false;
		}
        /// <summary>
        /// Gets all unseen foreign changes from the commit log. The metadata should be saved after calling this method,
        /// because inactive records might have been purged.
        /// </summary>
        private bool GetUnseenForeignChanges(CommitLogMetadata metadata,
                                             out List <ICmObjectSurrogate> foreignNewbies,
                                             out List <ICmObjectSurrogate> foreignDirtballs,
                                             out List <ICmObjectId> foreignGoners)
        {
            foreignNewbies   = new List <ICmObjectSurrogate>();
            foreignDirtballs = new List <ICmObjectSurrogate>();
            foreignGoners    = new List <ICmObjectId>();

            int minPeerGeneration = metadata.Peers.Select(p => p.Key == m_peerID ? metadata.CurrentGeneration : p.Value.Generation).Min();
            var unseenCommitRecs  = new List <CommitLogRecord>();

            int bytesRemaining = metadata.LogLength;
            // read all records up to the end of the file or the end of the log, whichever comes first
            int length = Math.Min(metadata.LogLength, m_settings.SharedXMLBackendCommitLogSize - metadata.LogOffset - metadata.Padding);

            bytesRemaining -= ReadUnseenCommitRecords(metadata, minPeerGeneration, metadata.LogOffset, length, unseenCommitRecs);
            // if there are bytes remaining, it means that we hit the end of the file, so we need to wrap around to the beginning
            if (bytesRemaining > 0)
            {
                bytesRemaining -= ReadUnseenCommitRecords(metadata, minPeerGeneration, 0, bytesRemaining, unseenCommitRecs);
            }
            Debug.Assert(bytesRemaining == 0);

            if (unseenCommitRecs.Count == 0)
            {
                return(false);
            }

            // check if there was enough room in the commit log for the last peer to write its commit
            // if it was not able, then we cannot continue, because we will be out-of-sync
            if (unseenCommitRecs[unseenCommitRecs.Count - 1].WriteGeneration < metadata.CurrentGeneration)
            {
                throw new InvalidOperationException("The most recent unseen commit could not be found.");
            }

            var idFactory = m_cache.ServiceLocator.GetInstance <ICmObjectIdFactory>();

            var newbies   = new Dictionary <Guid, ICmObjectSurrogate>();
            var dirtballs = new Dictionary <Guid, ICmObjectSurrogate>();
            var goners    = new HashSet <Guid>();

            var surrogateFactory = m_cache.ServiceLocator.GetInstance <ICmObjectSurrogateFactory>();

            foreach (CommitLogRecord commitRec in unseenCommitRecs)
            {
                if (commitRec.ObjectsDeleted != null)
                {
                    foreach (Guid goner in commitRec.ObjectsDeleted)
                    {
                        // If it was created by a previous foreign change we haven't seen, we can just forget it.
                        if (newbies.Remove(goner))
                        {
                            continue;
                        }
                        // If it was modified by a previous foreign change we haven't seen, we can forget the modification.
                        // (but we still need to know it's gone).
                        dirtballs.Remove(goner);
                        goners.Add(goner);
                    }
                }
                if (commitRec.ObjectsUpdated != null)
                {
                    foreach (byte[] dirtballXml in commitRec.ObjectsUpdated)
                    {
                        ICmObjectSurrogate dirtballSurrogate = surrogateFactory.Create(dirtballXml);
                        // This shouldn't be necessary; if a previous foreign transaction deleted it, it
                        // should not show up as a dirtball in a later transaction until it has shown up as a newby.
                        // goners.Remove(dirtball);
                        // If this was previously known as a newby or modified, then to us it still is.
                        if (newbies.ContainsKey(dirtballSurrogate.Guid) || dirtballs.ContainsKey(dirtballSurrogate.Guid))
                        {
                            continue;
                        }
                        dirtballs[dirtballSurrogate.Guid] = dirtballSurrogate;
                    }
                }
                if (commitRec.ObjectsAdded != null)
                {
                    foreach (byte[] newbyXml in commitRec.ObjectsAdded)
                    {
                        ICmObjectSurrogate newObj = surrogateFactory.Create(newbyXml);
                        if (goners.Remove(newObj.Guid))
                        {
                            // an object which an earlier transaction deleted is being re-created.
                            // This means that to us, it is a dirtball.
                            dirtballs[newObj.Guid] = newObj;
                            continue;
                        }
                        // It shouldn't be in dirtballs; can't be new in one transaction without having been deleted previously.
                        // So it really is new.
                        newbies[newObj.Guid] = newObj;
                    }
                }
                foreignNewbies.AddRange(newbies.Values);
                foreignDirtballs.AddRange(dirtballs.Values);
                foreignGoners.AddRange(from guid in goners select idFactory.FromGuid(guid));
            }
            return(true);
        }
		private void SaveMetadata(CommitLogMetadata metadata)
		{
			using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream())
				SaveMetadata(stream, metadata);
		}
		private static void SaveMetadata(MemoryMappedViewStream stream, CommitLogMetadata metadata)
		{
			stream.Seek(0, SeekOrigin.Begin);
			Serializer.SerializeWithLengthPrefix(stream, metadata, PrefixStyle.Base128, 1);
		}
 private void SaveMetadata(CommitLogMetadata metadata)
 {
     using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream())
         SaveMetadata(stream, metadata);
 }
		private bool CheckExitedPeerProcesses(CommitLogMetadata metadata)
		{
			bool changed = false;
			var processesToRemove = new HashSet<Process>(m_peerProcesses.Values);
			foreach (KeyValuePair<Guid, CommitLogPeer> kvp in metadata.Peers.ToArray())
			{
				if (kvp.Key == m_peerID)
					continue;

				Process process;
				if (m_peerProcesses.TryGetValue(kvp.Value.ProcessID, out process))
				{
					if (process.HasExited)
					{
						RemovePeer(metadata, kvp.Key);
						changed = true;
					}
					else
					{
						processesToRemove.Remove(process);
					}
				}
				else
				{
					try
					{
						process = Process.GetProcessById(kvp.Value.ProcessID);
						m_peerProcesses[kvp.Value.ProcessID] = process;
					}
					catch (ArgumentException)
					{
						RemovePeer(metadata, kvp.Key);
						changed = true;
					}
				}
			}

			foreach (Process process in processesToRemove)
			{
				m_peerProcesses.Remove(process.Id);
				process.Close();
			}
			return changed;
		}
 private static void SaveMetadata(MemoryMappedViewStream stream, CommitLogMetadata metadata)
 {
     stream.Seek(0, SeekOrigin.Begin);
     Serializer.SerializeWithLengthPrefix(stream, metadata, PrefixStyle.Base128, 1);
 }
		private static void RemovePeer(CommitLogMetadata metadata, Guid peerID)
		{
			metadata.Peers.Remove(peerID);
			if (metadata.Master == peerID)
				metadata.Master = Guid.Empty;
		}
		/// <summary>
		/// Gets all unseen foreign changes from the commit log. The metadata should be saved after calling this method,
		/// because inactive records might have been purged.
		/// </summary>
		private bool GetUnseenForeignChanges(CommitLogMetadata metadata,
			out List<ICmObjectSurrogate> foreignNewbies,
			out List<ICmObjectSurrogate> foreignDirtballs,
			out List<ICmObjectId> foreignGoners)
		{
			foreignNewbies = new List<ICmObjectSurrogate>();
			foreignDirtballs = new List<ICmObjectSurrogate>();
			foreignGoners = new List<ICmObjectId>();

			int minPeerGeneration = metadata.Peers.Select(p => p.Key == m_peerID ? metadata.CurrentGeneration : p.Value.Generation).Min();
			var unseenCommitRecs = new List<CommitLogRecord>();

			int bytesRemaining = metadata.LogLength;
			// read all records up to the end of the file or the end of the log, whichever comes first
			int length = Math.Min(metadata.LogLength, m_settings.SharedXMLBackendCommitLogSize - metadata.LogOffset - metadata.Padding);
			bytesRemaining -= ReadUnseenCommitRecords(metadata, minPeerGeneration, metadata.LogOffset, length, unseenCommitRecs);
			// if there are bytes remaining, it means that we hit the end of the file, so we need to wrap around to the beginning
			if (bytesRemaining > 0)
				bytesRemaining -= ReadUnseenCommitRecords(metadata, minPeerGeneration, 0, bytesRemaining, unseenCommitRecs);
			Debug.Assert(bytesRemaining == 0);

			if (unseenCommitRecs.Count == 0)
				return false;

			// check if there was enough room in the commit log for the last peer to write its commit
			// if it was not able, then we cannot continue, because we will be out-of-sync
			if (unseenCommitRecs[unseenCommitRecs.Count - 1].WriteGeneration < metadata.CurrentGeneration)
				throw new InvalidOperationException("The most recent unseen commit could not be found.");

			var idFactory = m_cache.ServiceLocator.GetInstance<ICmObjectIdFactory>();

			var newbies = new Dictionary<Guid, ICmObjectSurrogate>();
			var dirtballs = new Dictionary<Guid, ICmObjectSurrogate>();
			var goners = new HashSet<Guid>();

			var surrogateFactory = m_cache.ServiceLocator.GetInstance<ICmObjectSurrogateFactory>();

			foreach (CommitLogRecord commitRec in unseenCommitRecs)
			{
				if (commitRec.ObjectsDeleted != null)
				{
					foreach (Guid goner in commitRec.ObjectsDeleted)
					{
						// If it was created by a previous foreign change we haven't seen, we can just forget it.
						if (newbies.Remove(goner))
							continue;
						// If it was modified by a previous foreign change we haven't seen, we can forget the modification.
						// (but we still need to know it's gone).
						dirtballs.Remove(goner);
						goners.Add(goner);
					}
				}
				if (commitRec.ObjectsUpdated != null)
				{
					foreach (byte[] dirtballXml in commitRec.ObjectsUpdated)
					{
						ICmObjectSurrogate dirtballSurrogate = surrogateFactory.Create(dirtballXml);
						// This shouldn't be necessary; if a previous foreign transaction deleted it, it
						// should not show up as a dirtball in a later transaction until it has shown up as a newby.
						// goners.Remove(dirtball);
						// If this was previously known as a newby or modified, then to us it still is.
						if (newbies.ContainsKey(dirtballSurrogate.Guid) || dirtballs.ContainsKey(dirtballSurrogate.Guid))
							continue;
						dirtballs[dirtballSurrogate.Guid] = dirtballSurrogate;
					}
				}
				if (commitRec.ObjectsAdded != null)
				{
					foreach (byte[] newbyXml in commitRec.ObjectsAdded)
					{
						ICmObjectSurrogate newObj = surrogateFactory.Create(newbyXml);
						if (goners.Remove(newObj.Guid))
						{
							// an object which an earlier transaction deleted is being re-created.
							// This means that to us, it is a dirtball.
							dirtballs[newObj.Guid] = newObj;
							continue;
						}
						// It shouldn't be in dirtballs; can't be new in one transaction without having been deleted previously.
						// So it really is new.
						newbies[newObj.Guid] = newObj;
					}
				}
				foreignNewbies.AddRange(newbies.Values);
				foreignDirtballs.AddRange(dirtballs.Values);
				foreignGoners.AddRange(from guid in goners select idFactory.FromGuid(guid));
			}
			return true;
		}
		protected override int StartupInternal(int currentModelVersion)
		{
			m_commitLogMutex = new GlobalMutex(MutexName);
			bool createdNew;
			using (m_commitLogMutex.InitializeAndLock(out createdNew))
			{
				CreateSharedMemory(createdNew);
				using (MemoryMappedViewStream stream = m_commitLogMetadata.CreateViewStream())
				{
					CommitLogMetadata metadata = null;
					if (!createdNew)
					{
						if (TryGetMetadata(stream, out metadata))
						{
							CheckExitedPeerProcesses(metadata);
							if (m_peerProcesses.Count == 0)
								createdNew = true;
						}
						else
						{
							createdNew = true;
						}
					}

					if (createdNew)
						metadata = new CommitLogMetadata();

					using (Process curProcess = Process.GetCurrentProcess())
						metadata.Peers[m_peerID] = new CommitLogPeer { ProcessID = curProcess.Id, Generation = metadata.FileGeneration };

					if (metadata.Master == Guid.Empty)
					{
						base.LockProject();
						metadata.Master = m_peerID;
					}

					int startupModelVersion = ReadInSurrogates(currentModelVersion);
					// non-master peers cannot migrate the XML file
					if (startupModelVersion < currentModelVersion && metadata.Master != m_peerID)
						throw new FdoDataMigrationForbiddenException();

					SaveMetadata(stream, metadata);

					return startupModelVersion;
				}
			}
		}
		private int ReadUnseenCommitRecords(CommitLogMetadata metadata, int minPeerGeneration, int startOffset, int length, List<CommitLogRecord> unseenCommits)
		{
			if (length == 0)
				return 0;

			int generation = metadata.Peers[m_peerID].Generation;
			using (MemoryMappedViewStream stream = m_commitLog.CreateViewStream(startOffset, length))
			{
				while (stream.Position < length)
				{
					long startPos = stream.Position;
					var rec = Serializer.DeserializeWithLengthPrefix<CommitLogRecord>(stream, PrefixStyle.Base128, 1);
					if (rec.WriteGeneration > generation && rec.Source != m_peerID)
						unseenCommits.Add(rec);
					// remove the record from the commit log once all peers have seen it and it has been written to disk
					if (rec.WriteGeneration <= minPeerGeneration && rec.WriteGeneration <= metadata.FileGeneration)
					{
						metadata.LogOffset = startOffset + (int) stream.Position;
						metadata.LogLength -= (int) (stream.Position - startPos);
					}
				}
			}

			// if we have read everything to the end of the file, add padding to read length
			if (startOffset + length == m_settings.SharedXMLBackendCommitLogSize - metadata.Padding)
				length += metadata.Padding;

			// check if we've purged all records up to the end of the file. If so, wrap around to the beginning.
			if (metadata.LogOffset == m_settings.SharedXMLBackendCommitLogSize - metadata.Padding)
			{
				metadata.LogOffset = 0;
				metadata.LogLength -= metadata.Padding;
				metadata.Padding = 0;
			}

			return length;
		}