示例#1
0
        private static bool TryGetNextValueAtomic(
            NativeHashSetState *state,
            ref NativeMultiHashSetIterator it)
        {
            int entryIdx = it.NextEntryIndex;

            it.NextEntryIndex = -1;
            if (entryIdx < 0 || entryIdx >= state->ItemCapacity)
            {
                return(false);
            }

            int *nextPtrs = (int *)state->Next;

            while (!UnsafeUtility.ReadArrayElement <T>(
                       state->Items,
                       entryIdx).Equals(it.Item))
            {
                entryIdx = nextPtrs[entryIdx];
                if (entryIdx < 0 || entryIdx >= state->ItemCapacity)
                {
                    return(false);
                }
            }
            it.NextEntryIndex = nextPtrs[entryIdx];

            return(true);
        }
示例#2
0
 /// <summary>
 /// Deallocate the state's contents then the state itself.
 /// </summary>
 private void Deallocate()
 {
     UnsafeUtility.Free(m_State->Items, m_Allocator);
     m_State->Items   = null;
     m_State->Next    = null;
     m_State->Buckets = null;
     UnsafeUtility.Free(m_State, m_Allocator);
     m_State = null;
 }
示例#3
0
            public Enumerator(NativeHashSet <T> set)
            {
                set.RequireReadAccess();

                index  = -1;
                bucket = -1;
                state  = set.m_State;

                bucketArray = (int *)state->Buckets;
                bucketNext  = (int *)state->Next;
            }
示例#4
0
        private static bool TryGetFirstValueAtomic(
            NativeHashSetState *state,
            T item,
            out NativeMultiHashSetIterator it)
        {
            it.Item = item;
            if (state->AllocatedIndexLength <= 0)
            {
                it.NextEntryIndex = -1;
                return(false);
            }
            // First find the slot based on the hash
            int *buckets = (int *)state->Buckets;
            int  bucket  = item.GetHashCode() & state->BucketCapacityMask;

            it.NextEntryIndex = buckets[bucket];
            return(TryGetNextValueAtomic(state, ref it));
        }
示例#5
0
        /// <summary>
        /// Schedule a job to release the set's unmanaged memory after the given
        /// dependency jobs. Do not use it after this job executes. Do
        /// not call <see cref="Dispose()"/> on copies of the set either.
        ///
        /// This operation requires write access.
        ///
        /// This complexity of this operation is O(1) plus the allocator's
        /// deallocation complexity.
        /// </summary>
        public JobHandle Dispose(JobHandle inputDeps)
        {
            RequireWriteAccess();

#if ENABLE_UNITY_COLLECTIONS_CHECKS
            // Clear the dispose sentinel, but don't Dispose it
            DisposeSentinel.Clear(ref m_DisposeSentinel);
#endif

            // Schedule the job
            DisposeJob disposeJob = new DisposeJob {
                Set = this
            };
            JobHandle jobHandle = disposeJob.Schedule(inputDeps);

            // Release the atomic safety handle now that the job is scheduled
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            AtomicSafetyHandle.Release(m_Safety);
#endif

            m_State = null;
            return(jobHandle);
        }
示例#6
0
        /// <summary>
        /// Allocate an entry from the free list. The list must not be full.
        /// </summary>
        ///
        /// <param name="state">
        /// State to allocate from.
        /// </param>
        ///
        /// <param name="threadIndex">
        /// Index of the allocating thread.
        /// </param>
        ///
        /// <returns>
        /// The allocated free list entry index
        /// </returns>
        private static int AllocFreeListEntry(
            NativeHashSetState *state,
            int threadIndex)
        {
            int  idx;
            int *nextPtrs = (int *)state->Next;

            do
            {
                idx = state->FirstFreeTLS[threadIndex * NativeHashSetState.IntsPerCacheLine];
                if (idx < 0)
                {
                    // Try to refill local cache
                    Interlocked.Exchange(
                        ref state->FirstFreeTLS[threadIndex * NativeHashSetState.IntsPerCacheLine],
                        -2);

                    // If it failed try to get one from the never-allocated array
                    if (state->AllocatedIndexLength < state->ItemCapacity)
                    {
                        idx = Interlocked.Add(ref state->AllocatedIndexLength, 16) - 16;
                        if (idx < state->ItemCapacity - 1)
                        {
                            int count = Math.Min(16, state->ItemCapacity - idx);
                            for (int i = 1; i < count; ++i)
                            {
                                nextPtrs[idx + i] = idx + i + 1;
                            }

                            nextPtrs[idx + count - 1] = -1;
                            nextPtrs[idx]             = -1;
                            Interlocked.Exchange(
                                ref state->FirstFreeTLS[threadIndex * NativeHashSetState.IntsPerCacheLine],
                                idx + 1);
                            return(idx);
                        }

                        if (idx == state->ItemCapacity - 1)
                        {
                            Interlocked.Exchange(
                                ref state->FirstFreeTLS[threadIndex * NativeHashSetState.IntsPerCacheLine],
                                -1);
                            return(idx);
                        }
                    }
                    Interlocked.Exchange(
                        ref state->FirstFreeTLS[threadIndex * NativeHashSetState.IntsPerCacheLine],
                        -1);
                    // Failed to get any, try to get one from another free list
                    bool again = true;
                    while (again)
                    {
                        again = false;
                        for (int other = (threadIndex + 1) % JobsUtility.MaxJobThreadCount;
                             other != threadIndex;
                             other = (other + 1) % JobsUtility.MaxJobThreadCount)
                        {
                            do
                            {
                                idx = state->FirstFreeTLS[other * NativeHashSetState.IntsPerCacheLine];
                                if (idx < 0)
                                {
                                    break;
                                }
                            } while (Interlocked.CompareExchange(
                                         ref state->FirstFreeTLS[other * NativeHashSetState.IntsPerCacheLine],
                                         nextPtrs[idx], idx) != idx);
                            if (idx == -2)
                            {
                                again = true;
                            }
                            else if (idx >= 0)
                            {
                                nextPtrs[idx] = -1;
                                return(idx);
                            }
                        }
                    }
                    throw new InvalidOperationException("HashSet is full");
                }
                if (idx >= state->ItemCapacity)
                {
                    throw new InvalidOperationException("HashSet is full");
                }
            } while (Interlocked.CompareExchange(
                         ref state->FirstFreeTLS[threadIndex * NativeHashSetState.IntsPerCacheLine],
                         nextPtrs[idx],
                         idx) != idx);
            nextPtrs[idx] = -1;
            return(idx);
        }
示例#7
0
        /// <summary>
        /// Create an empty hash set with a given capacity
        /// </summary>
        ///
        /// <param name="capacity">
        /// Capacity of the hash set. If less than four, four is used.
        /// </param>
        ///
        /// <param name="allocator">
        /// Allocator to allocate unmanaged memory with. Must be valid.
        /// </param>
        public NativeHashSet(int capacity, Allocator allocator)
        {
            // Require a valid allocator
            if (allocator <= Allocator.None)
            {
                throw new ArgumentException(
                          "Allocator must be Temp, TempJob or Persistent",
                          "allocator");
            }

            RequireBlittable();

            // Insist on a minimum capacity
            if (capacity < 4)
            {
                capacity = 4;
            }

            m_Allocator = allocator;

            // Allocate the state
            NativeHashSetState *state = (NativeHashSetState *)UnsafeUtility.Malloc(
                sizeof(NativeHashSetState),
                UnsafeUtility.AlignOf <NativeHashSetState>(),
                allocator);

            state->ItemCapacity = capacity;

            // To reduce collisions, use twice as many buckets
            int bucketLength = capacity * 2;

            bucketLength = NextHigherPowerOfTwo(bucketLength);
            state->BucketCapacityMask = bucketLength - 1;

            // Allocate state arrays
            int nextOffset;
            int bucketOffset;
            int totalSize = CalculateDataLayout(
                capacity,
                bucketLength,
                out nextOffset,
                out bucketOffset);

            state->Items = (byte *)UnsafeUtility.Malloc(
                totalSize,
                JobsUtility.CacheLineSize,
                allocator);
            state->Next    = state->Items + nextOffset;
            state->Buckets = state->Items + bucketOffset;

            m_State = state;

#if ENABLE_UNITY_COLLECTIONS_CHECKS
#if UNITY_2018_3_OR_NEWER
            DisposeSentinel.Create(
                out m_Safety,
                out m_DisposeSentinel,
                1,
                allocator);
#else
            DisposeSentinel.Create(
                out m_Safety,
                out m_DisposeSentinel,
                1);
#endif
#endif

            Clear();
        }