Struct DispatchQueue
Double buffered thread-safe queue
Limited by a maximum size that is passed in the constructor. This is an important for performance, to avoid memory reallocation during the work.
In the usual case the queue works in the asynchronous mode:
- One buffer is used for write: several threads can add new LogMessage's to it, no one can read from that buffer. That is done via Enqueue(PayloadHandle, Int64, LogLevel) method that
does that under the read lock[1].
- Second buffer is used as a read-only buffer: several sinks can read from it, without modifying anything. This is done via BeginRead() and EndRead() that
enters/exits the read lock[1].
- Second buffer also can be in an exclusive mode: Cleanup system calls BeginReadExclusive() to enter the exclusive lock[2] to make sure only it can access the read buffer
at the time to release the memory used by the messages. EndReadExclusiveClearAndFlip() at the end of its work, cleaning the read buffer and flipping write/read ones, so
now the write buffer is empty. Exclusive lock is unlocked after this.
- Unity.Logging.DispatchQueue.Sort of the second (read) buffer is done under an exclusive lock[2], since it modifies the list, so it is not safe to read it.
Another use case that is much slower - synchronous access. LockAndSortForSyncAccess(out UnsafeList<LogMessage>, out UnsafeList<LogMessage>) will put the queue under the exclusive lock[2] and return both buffers for read-write access.
When the access is finished EndLockAfterSyncAccess() call will clear both buffers and unlock the data structure.
This is used in the case when full synchronous flush is needed.Lock is used to control the thread-safe access for this data structure:
[1] Read-lock allows several threads to take it, but only if no exclusive lock is taken. Guarantees that no exclusive lock [2] will be taken during it. Read-lock is used in situations when it is ok for multiple threads to do some kind of access, like: add new elements (see Enqueue(PayloadHandle, Int64, LogLevel)), read simultaneously without modifications (see BeginRead(), EndRead()).
[2] Exclusive lock is used when only one thread can enter and no read-locks are taken. This lock is used to cleanup the memory of the messages (see BeginReadExclusive()) and then clear the read and swap the read and write buffers (see EndReadExclusiveClearAndFlip()).Note: While read buffer is in read-only BeginRead() -- EndRead() mode the write buffer still can be used to Enqueue(PayloadHandle, Int64, LogLevel) from multiple threads
Inherited Members
Namespace: Unity.Logging
Syntax
[BurstCompile]
public struct DispatchQueue : IDisposable
Constructors
DispatchQueue(Int32)
Constructor for DispatchQueue.
Declaration
public DispatchQueue(int size)
Parameters
Type | Name | Description |
---|---|---|
Int32 | size | Maximum length of the queue |
Remarks
Creates two lists with the maximum amount of LogMessage's that it can process before it should be flipped.
Properties
IsCreated
Is true if this struct was initialized.
Declaration
public readonly bool IsCreated { get; }
Property Value
Type | Description |
---|---|
Boolean |
TotalLength
Total length of read-only and write buffers. Usually used for testing of internal state of the queue.
Declaration
public readonly int TotalLength { get; }
Property Value
Type | Description |
---|---|
Int32 |
Methods
BeginRead()
Puts DispatchQueue into reading lock mode till EndRead() is called to prevent buffer flipping. Several threads can use this at the same time for reading only.
Declaration
public UnsafeList<LogMessage>.ParallelReader BeginRead()
Returns
Type | Description |
---|---|
UnsafeList.ParallelReader<> | ParallelReader is returned that can be used to get all the LogMessage's from the read buffer |
BeginReadExclusive()
Puts DispatchQueue into exclusive lock mode till EndReadExclusiveClearAndFlip() is called to prevent buffer flipping. Only one thread can access the read buffer, usually to modify it (cleanup system can free the memory that is used by messages).
Declaration
public UnsafeList<LogMessage>.ParallelReader BeginReadExclusive()
Returns
Type | Description |
---|---|
UnsafeList.ParallelReader<> | ParallelReader is returned that can be used to get all the LogMessage's from read buffer, usually to dispose them |
Dispose()
Dispose call that will call Dispose for the lists (under lock) if IsCreated is true. IDisposable
Declaration
public void Dispose()
Implements
EndLockAfterSyncAccess()
Unlocks DispatchQueue exclusive lock that was initiated by LockAndSortForSyncAccess(out UnsafeList<LogMessage>, out UnsafeList<LogMessage>) and clears all the buffers.
Declaration
public void EndLockAfterSyncAccess()
EndRead()
Unlocks DispatchQueue reading lock that was initiated by BeginRead()
Declaration
public void EndRead()
EndReadExclusiveClearAndFlip()
Unlocks DispatchQueue exclusive lock that was initiated by BeginReadExclusive(), clears the read buffer and swaps the buffers, so now the write buffer is empty.
Declaration
public void EndReadExclusiveClearAndFlip()
Enqueue(PayloadHandle, Int64, LogLevel)
Adds new message to the write buffer under read lock. Gets the timestamp just before adding the message to the queue.
Declaration
public void Enqueue(PayloadHandle payload, long stacktraceId, LogLevel logLevel)
Parameters
Type | Name | Description |
---|---|---|
PayloadHandle | payload | PayloadHandle of the log message |
Int64 | stacktraceId | Stacktrace id of the log message or 0 if none |
LogLevel | logLevel | LogLevel of the log message |
LockAndSortForSyncAccess(out UnsafeList<LogMessage>, out UnsafeList<LogMessage>)
Enters the exclusive lock for DispatchQueue to get the access for both buffers till EndLockAfterSyncAccess() is called.
Declaration
public void LockAndSortForSyncAccess(out UnsafeList<LogMessage> olderMessages, out UnsafeList<LogMessage> newerMessages)
Parameters
Type | Name | Description |
---|---|---|
UnsafeList<LogMessage> | olderMessages | Returns the buffer with older messages (read buffer) |
UnsafeList<LogMessage> | newerMessages | Returns the buffer with newer messages (write buffer) |