Pristine.Adapters.BytesSemaphore.GenServer (Pristine v0.3.1)

Copy Markdown View Source

GenServer-based byte-budget semaphore adapter.

Tracks a shared byte budget across concurrent callers. Acquisitions can push the budget negative to allow in-flight work to complete; new acquisitions block while the budget is negative and resume once releases bring it back to a non-negative value.

Example

# Start a semaphore with 5MB budget
{:ok, sem} = GenServer.start_link(max_bytes: 5_242_880)

# Acquire bytes (blocks if budget is negative)
:ok = GenServer.acquire(sem, 1000, 5_000)

# Do work...

# Release bytes back
:ok = GenServer.release(sem, 1000)

with_bytes/3

For convenience, with_bytes/3 acquires, executes a function, and guarantees release even if the function raises:

result = GenServer.with_bytes(sem, 1000, fn ->
  # Do work with the allocated bytes
  :ok
end)

Summary

Functions

Acquire bytes from the semaphore, blocking while the budget is negative.

Get the number of available bytes.

Returns a specification to start this module under a supervisor.

Release bytes back to the semaphore.

Start a BytesSemaphore with the given byte budget.

Execute fun while holding the requested byte budget.

Types

t()

@type t() :: pid() | atom()

Functions

acquire(semaphore, bytes, timeout)

@spec acquire(t(), non_neg_integer(), timeout()) :: :ok | {:error, :timeout}

Acquire bytes from the semaphore, blocking while the budget is negative.

Returns :ok when the bytes have been acquired. If the current budget is negative, the caller blocks until enough releases bring it back to non-negative or the timeout expires.

Examples

:ok = GenServer.acquire(sem, 1000, 5_000)
{:error, :timeout} = GenServer.acquire(sem, 1000, 100)

available(semaphore)

@spec available(t()) :: non_neg_integer()

Get the number of available bytes.

Returns 0 if the budget is negative.

Examples

available = GenServer.available(sem)

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

release(semaphore, bytes)

@spec release(t(), non_neg_integer()) :: :ok

Release bytes back to the semaphore.

This is an asynchronous operation. If there are blocked waiters and the budget returns to non-negative, they will be woken up.

Examples

:ok = GenServer.release(sem, 1000)

start_link(opts \\ [])

@spec start_link(keyword()) :: GenServer.on_start()

Start a BytesSemaphore with the given byte budget.

Options

  • :max_bytes - Maximum byte budget (default: 5MB)
  • :name - Optional name for registration

Examples

{:ok, sem} = GenServer.start_link(max_bytes: 1_000_000)
{:ok, sem} = GenServer.start_link(name: MyApp.BytesSemaphore)

with_bytes(semaphore, bytes, fun)

@spec with_bytes(t(), non_neg_integer(), (-> result)) :: result when result: any()

Execute fun while holding the requested byte budget.

Acquires the bytes, executes the function, and guarantees release even if the function raises an exception, throws, or exits.

Examples

result = GenServer.with_bytes(sem, 1000, fn ->
  # Work with allocated bytes
  :done
end)