/** * Async Mutex Utility * * Provides a simple async mutex implementation for protecting critical sections * from concurrent access. Useful for serializing access to shared resources * like database operations. * * Features: * - FIFO queue ordering for fairness * - withLock() for safe lock/unlock with try/finally * - isLocked getter for status checks * - Optional timeout support to prevent deadlocks */ /** * Async Mutex for serializing access to critical sections * * Ensures only one async operation can hold the lock at a time. * Uses a FIFO queue for fair ordering of waiting operations. * * @example * ```typescript * const mutex = new AsyncMutex(); * * // Using withLock (recommended) * await mutex.withLock(async () => { * await criticalOperation(); * }); * * // Manual acquire/release * await mutex.acquire(); * try { * await criticalOperation(); * } finally { * mutex.release(); * } * ``` */ export declare class AsyncMutex { /** Whether the mutex is currently held */ private locked; /** Queue of functions waiting for the lock */ private queue; /** Optional name for logging purposes */ private readonly name; /** * Create a new AsyncMutex * * @param name - Optional name for logging purposes */ constructor(name?: string); /** * Check if the mutex is currently locked */ get isLocked(): boolean; /** * Get the number of waiters in the queue */ get queueLength(): number; /** * Acquire the mutex lock * * If the mutex is already locked, this will wait until it becomes available. * Waiters are processed in FIFO order. * * @param timeout - Optional timeout in milliseconds. If provided and exceeded, * the promise will reject with an error. * @throws Error if timeout is exceeded */ acquire(timeout?: number): Promise; /** * Release the mutex lock * * If there are waiters in the queue, the next one will be given the lock. * Otherwise, the mutex becomes unlocked. * * If a waiter has already timed out (satisfied flag), we continue to the next waiter. * This prevents deadlock when timeout and release race. * * @throws Error if release is called when the mutex is not locked */ release(): void; /** * Execute a function while holding the lock * * This is the recommended way to use the mutex as it ensures * the lock is always released, even if the function throws. * * @param fn - Async function to execute while holding the lock * @param timeout - Optional timeout for lock acquisition * @returns The result of the function * * @example * ```typescript * const result = await mutex.withLock(async () => { * return await db.query('SELECT * FROM users'); * }); * ``` */ withLock(fn: () => Promise, timeout?: number): Promise; /** * Try to acquire the lock without waiting * * @returns true if lock was acquired, false if already locked */ tryAcquire(): boolean; } /** * Read-Write Lock for concurrent reads, exclusive writes * * Allows multiple readers to hold the lock simultaneously, * but writers require exclusive access. * * BUG #18 DOCUMENTATION: Starvation Potential * * Under extreme contention, reader or writer starvation is possible: * - Current policy gives writers priority when waiting (writerWaiting blocks new readers) * - Continuous writer arrivals can starve readers * - Conversely, if readers are continuously arriving while writerWaiting is false, * a waiting writer might be delayed * * For most use cases in this codebase, contention is low and this is acceptable. * The ReadWriteLock is primarily used for protecting LanceDB operations where * search (read) and indexing (write) operations are typically well-separated. * * If fairness becomes critical in the future, consider implementing: * - Alternating batches: After each write, allow all waiting readers before next write * - FIFO ordering: Process requests strictly in arrival order * - Timestamped priorities: Give priority based on wait time * * @example * ```typescript * const rwlock = new ReadWriteLock(); * * // Multiple reads can happen concurrently * await rwlock.withReadLock(async () => { * return await db.query('SELECT * FROM users'); * }); * * // Writes are exclusive * await rwlock.withWriteLock(async () => { * await db.query('INSERT INTO users VALUES (...)'); * }); * ``` */ export declare class ReadWriteLock { /** Number of active readers */ private readers; /** Whether a writer is active or waiting */ private writerActive; /** Whether there are writers waiting (for write preference) */ private writerWaiting; /** Queue of waiting readers */ private readerQueue; /** Queue of waiting writers */ private writerQueue; /** Optional name for logging purposes */ private readonly name; /** * Create a new ReadWriteLock * * @param name - Optional name for logging purposes */ constructor(name?: string); /** * Get the number of active readers */ get activeReaders(): number; /** * Check if a writer is active */ get isWriterActive(): boolean; /** * Acquire a read lock * * Multiple readers can hold the lock simultaneously. * Readers will wait if a writer is active or waiting. */ acquireRead(): Promise; /** * Release a read lock */ releaseRead(): void; /** * Acquire a write lock * * Writers require exclusive access - no readers or other writers. */ acquireWrite(): Promise; /** * Release a write lock */ releaseWrite(): void; /** * Execute a function while holding a read lock * * @param fn - Async function to execute while holding the read lock * @returns The result of the function */ withReadLock(fn: () => Promise): Promise; /** * Execute a function while holding a write lock * * @param fn - Async function to execute while holding the write lock * @returns The result of the function */ withWriteLock(fn: () => Promise): Promise; } /** * Global lock for preventing concurrent indexing operations * * This is a singleton lock used to prevent concurrent create_index * or reindex_project operations across the entire application. */ export declare class IndexingLock { private static instance; private readonly mutex; private currentProject; private constructor(); /** * Get the singleton instance */ static getInstance(): IndexingLock; /** * Reset the singleton instance (for testing) */ static resetInstance(): void; /** * Check if indexing is currently in progress */ get isIndexing(): boolean; /** * Get the project currently being indexed (if any) */ get indexingProject(): string | null; /** * Acquire the indexing lock for a project * * @param projectPath - Path of the project being indexed * @param timeout - Optional timeout in milliseconds * @throws Error if another indexing operation is in progress */ acquire(projectPath: string, timeout?: number): Promise; /** * Release the indexing lock */ release(): void; /** * Execute a function while holding the indexing lock * * @param projectPath - Path of the project being indexed * @param fn - Async function to execute while holding the lock * @param timeout - Optional timeout in milliseconds * @returns The result of the function */ withLock(projectPath: string, fn: () => Promise, timeout?: number): Promise; } //# sourceMappingURL=asyncMutex.d.ts.map