Usually, this kind of detail is left to the caller, i.e. by making the caller await appropriately and only call methods when they should call methods. However, if you must do this, one simple way is via a semaphore; consider:
class HazProtected { private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); public async Task<OpenResult> OpenAsync(CancellationToken cancellationToken = default) { await _lock.WaitAsync(cancellationToken); try { return await DoOpenAsync(cancellationToken); } finally { _lock.Release(); } } private async Task<OpenResult> DoOpenAsync(CancellationToken cancellationToken) { // ... your real code here } }
The code in OpenAsync ensures that only one concurrent async caller can be attempting to open it at a time. When there is a conflict, callers are held asynchronously until the semaphore can be acquired. There is a complication, though; SempahoreSlim has some known problems on .NET Framework (resolved in .NET Core) when there are both asynchronous and synchronous semaphore acquisitions at the same time - which can lead to a spiral of death.
In more complex scenarios, it is possible to write your own queue of pending callers; this is a very very exotic scenario and should usually not be attempted unless you understand exactly why you're doing it!
openAsynccalled? Is there a missingawaitperhaps? There's no need for any queues, that's how Tasks already work.async; 1) the caller worries about concurrency, and only calls methods appropriately, awaiting between them - usually from a single caller logical flow; 2) the type worries about concurrency, with logical queues (and often multiple parallel caller flows against the same instance); the first is much, much easier to implement and understand, and is by far the majority usage; the second is really quite hard; are you sure you need that? can you explain why you need that? it can be done, but ... it is a huge thing to explain and do