function createSemaphore({
  signal,
  capacity = 1,
  onAcquire,
  onRelease
} = {}) {
  capacity = Math.max(1, capacity);
  const onDrain = [];
  const queue = [];
  let acquired = 0;
  return {
    get size() {
      return getSize();
    },
    get capacity() {
      return capacity;
    },
    async acquire(owner) {
      signal?.throwIfAborted();
      let active = true;
      return await new Promise((resolve, reject) => {
        if (acquired < capacity) acquire().then(resolve, reject);
        else queue.push(() => void acquire().then(resolve, reject));
      });
      async function acquire() {
        signal?.throwIfAborted();
        ++acquired;
        try {
          onAcquire?.(owner);
        } catch (error) {
          release();
          throw error;
        }
        return {
          get active() {
            return active;
          },
          release
        };
      }
      function release() {
        if (!active) return;
        active = false;
        --acquired;
        try {
          onRelease?.(owner);
        } finally {
          while (acquired < capacity && queue.length > 0) {
            queue.shift()?.();
          }
          while (getSize() === 0 && onDrain.length > 0) {
            onDrain.shift()?.();
          }
        }
      }
    },
    controlled(callback, owner) {
      return async (...args) => {
        const lock = await this.acquire(owner);
        try {
          return await callback(...args);
        } finally {
          lock.release();
        }
      };
    },
    async drain() {
      return this.size > 0 ? await new Promise((callback) => onDrain.push(callback)) : void 0;
    }
  };
  function getSize() {
    return acquired + queue.length;
  }
}
function createMutex(options = {}) {
  return createSemaphore({ ...options, capacity: 1 });
}
export {
  createMutex,
  createSemaphore
};
//# sourceMappingURL=index.mjs.map
