File: src\libraries\System.Private.CoreLib\src\System\WeakReference.cs
Web Access
Project: src\src\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj (System.Private.CoreLib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
 
using static System.WeakReferenceHandleTags;
 
namespace System
{
    internal static class WeakReferenceHandleTags
    {
        // the lowermost bit is used to indicate whether the handle is tracking resurrection
        // handles are at least 2-byte aligned, so we can use one bit for tagging
        internal const nint TracksResurrectionBit = 1;
 
#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
        // one more bit is used to track whether the handle refers to an instance of ComAwareWeakReference
        // we can use this bit because on COM-supporting platforms a handle is at least 4-byte aligned
        internal const nint ComAwareBit = 2;
        internal const nint HandleTagBits = TracksResurrectionBit | ComAwareBit;
#else
        internal const nint HandleTagBits = TracksResurrectionBit;
#endif
    }
 
    [Serializable]
    [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
    public partial class WeakReference : ISerializable
    {
        // If you fix bugs here, please fix them in WeakReference<T> at the same time.
 
        // Most methods using the handle should use GC.KeepAlive(this) to avoid potential handle recycling
        // attacks (i.e. if the WeakReference instance is finalized away underneath you when you're still
        // handling a cached value of the handle then the handle could be freed and reused).
 
        private nint _taggedHandle;
 
        // Creates a new WeakReference that keeps track of target.
        // Assumes a Short Weak Reference (ie TrackResurrection is false.)
        //
        public WeakReference(object? target)
            : this(target, false)
        {
        }
 
        public WeakReference(object? target, bool trackResurrection)
        {
            Create(target, trackResurrection);
        }
 
        [Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        protected WeakReference(SerializationInfo info, StreamingContext context)
        {
            ArgumentNullException.ThrowIfNull(info);
 
            object? target = info.GetValue("TrackedObject", typeof(object)); // Do not rename (binary serialization)
            bool trackResurrection = info.GetBoolean("TrackResurrection"); // Do not rename (binary serialization)
 
            Create(target, trackResurrection);
        }
 
        [Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            ArgumentNullException.ThrowIfNull(info);
 
            info.AddValue("TrackedObject", Target, typeof(object)); // Do not rename (binary serialization)
            info.AddValue("TrackResurrection", IsTrackResurrection()); // Do not rename (binary serialization)
        }
 
        // Returns a boolean indicating whether or not we're tracking objects until they're collected (true)
        // or just until they're finalized (false).
        public virtual bool TrackResurrection => IsTrackResurrection();
 
        private void Create(object? target, bool trackResurrection)
        {
            nint h = GCHandle.InternalAlloc(target, trackResurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak);
            _taggedHandle = trackResurrection ?
                h | TracksResurrectionBit :
                h;
 
#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
            ComAwareWeakReference.ComInfo? comInfo = ComAwareWeakReference.ComInfo.FromObject(target);
            if (comInfo != null)
            {
                ComAwareWeakReference.SetComInfoInConstructor(ref _taggedHandle, comInfo);
            }
#endif
        }
 
        // Returns a boolean indicating whether or not we're tracking objects until they're collected (true)
        // or just until they're finalized (false).
        private bool IsTrackResurrection() => (_taggedHandle & TracksResurrectionBit) != 0;
 
        internal nint WeakHandle
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                nint th = _taggedHandle;
 
#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
                if ((th & ComAwareBit) != 0)
                    return ComAwareWeakReference.GetWeakHandle(th);
#endif
                return th & ~HandleTagBits;
            }
        }
 
        // Determines whether or not this instance of WeakReference still refers to an object
        // that has not been collected.
        public virtual bool IsAlive
        {
            get
            {
                nint wh = WeakHandle;
 
                // In determining whether it is valid to use this object, we need to at least expose this
                // without throwing an exception.
                if (wh == 0)
                    return false;
 
                bool result = GCHandle.InternalGet(wh) != null;
 
                // must keep the instance alive as long as we use the handle.
                GC.KeepAlive(this);
 
                return result;
            }
        }
 
        //Gets the Object stored in the handle if it's accessible.
        // Or sets it.
        public virtual object? Target
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                nint th = _taggedHandle & ~TracksResurrectionBit;
 
                // Should only happen for corner cases, like using a WeakReference from a finalizer.
                // GC can finalize the instance if it becomes F-Reachable.
                // That, however, cannot happen while we use the instance.
                //
                // A derived class will be finalized with an actual Finalize, but the finalizer queue is single threaded,
                // thus the default implementation will never access Target concurrently with finalizing.
                //
                // There is a possibility that a derived type overrides the default finalizer and arranges concurrent access.
                // There is nothing that we can do about that and a few other exotic ways to break this.
                //
                if (th == 0)
                    return default;
 
                object? target;
 
#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
                if ((th & ComAwareBit) != 0)
                {
                    target = ComAwareWeakReference.GetTarget(th);
 
                    // must keep the instance alive as long as we use the handle.
                    GC.KeepAlive(this);
 
                    return target;
                }
#endif
 
                // unsafe cast is ok as the handle cannot be destroyed and recycled while we keep the instance alive
                target = GCHandle.InternalGet(th);
 
                // must keep the instance alive as long as we use the handle.
                GC.KeepAlive(this);
 
                return target;
            }
 
            set
            {
                nint th = _taggedHandle & ~TracksResurrectionBit;
 
                // Should only happen for corner cases, like using a WeakReference from a finalizer.
                // GC can finalize the instance if it becomes F-Reachable.
                // That, however, cannot happen while we use the instance.
                if (th == 0)
                    throw new InvalidOperationException(SR.InvalidOperation_HandleIsNotInitialized);
 
#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
                var comInfo = ComAwareWeakReference.ComInfo.FromObject(value);
                if ((th & ComAwareBit) != 0 || comInfo != null)
                {
                    ComAwareWeakReference.SetTarget(ref _taggedHandle, value, comInfo);
 
                    // must keep the instance alive as long as we use the handle.
                    GC.KeepAlive(this);
 
                    return;
                }
#endif
 
                GCHandle.InternalSet(th, value);
 
                // must keep the instance alive as long as we use the handle.
                GC.KeepAlive(this);
            }
        }
 
        // Free all system resources associated with this reference.
        ~WeakReference()
        {
            // Note: While WeakReference is formally a finalizable type, the finalizer does not actually run.
            //       Instead the instances are treated specially in GC when scanning for no longer strongly-reachable
            //       finalizable objects.
            //
            // Unlike WeakReference<T> case, the instance could be of a derived type and
            //       in such case it is finalized via a finalizer.
 
            Debug.Assert(this.GetType() != typeof(WeakReference));
 
            nint handle = _taggedHandle & ~HandleTagBits;
            if (handle != 0)
            {
                GCHandle.InternalFree(handle);
 
                // keep the bit that indicates whether this reference was tracking resurrection, clear the rest.
                _taggedHandle &= TracksResurrectionBit;
            }
        }
    }
}