Package: groovy.concurrent

## [Java] Interface Actor<T>

-     public interface Actor<T>
      extends AutoCloseable

  A lightweight message-passing actor for concurrent state management.

  Each actor has a dedicated thread that processes messages sequentially from a queue. This guarantees that the actor's state is never accessed concurrently — no locks needed.

  Two factory patterns provide the common shapes:

  - reactor(Function) — stateless; each message produces a reply
  - stateful(Object, BiFunction) — maintains state; handler receives (state, message) and returns new state
  Each pattern has a context-aware overload (reactor(ReactorHandler) and stateful(Object, StatefulHandler)) whose handler receives an ActorContext as a first argument. The context exposes `self()` so the handler can stop the actor without a captured self-reference.

      // Reactor: stateless message processing
       def doubler = Actor.reactor { msg -> msg * 2
       assert await(doubler.sendAndGet(5)) == 10

       // Stateful: accumulates state across messages
       def counter = Actor.stateful(0) { state, msg ->
           switch (msg) {
               case 'increment': return state + 1
               case 'decrement': return state - 1
               default: return state
           }
       }
       counter.send('increment')
       counter.send('increment')
       assert await(counter.sendAndGet('increment')) == 3

       // Self-stop from a handler via the context
       def bot = Actor.stateful(0) { ctx, count, msg ->
           def next = count + 1
           if (next >= 3) ctx.self().stop()
           next
       }
       }

  For FSM-style actors, the context-aware handler shapes (ReactorHandler / StatefulHandler) receive an ActorContext that supports ctx.become(...) to swap the active handler, and ctx.stash() / ActorContext.unstashAll to defer messages received in the wrong phase and replay them later. See ActorContext for the full semantics.

  Actors use virtual threads on JDK 21+ for efficient scheduling. Millions of actors can coexist without pool tuning.

  Inspired by GPars actors, Erlang processes, and Clojure agents.

  Type Parameters:
    `T` - the message type
  See Also:
    Agent
    AsyncScope
  Since:
    6.0.0

- - ### Methods Summary

    Methods
    Type Params | Return Type | Name and description
    --- | --- | ---
    `` | `public void` | `close()` Stops this actor.
    `<T>` | `public static Actor<T>` | `currentSelf()` Returns the actor whose handler is currently executing on this thread.
    `` | `public boolean` | `isActive()` Returns `true` while the actor is accepting new sends.
    `` | `public boolean` | `isTerminated()` Returns `true` once the worker has fully exited — i.e. the queue and any stashed messages have been processed (or rejected, in the case of stashed `sendAndGet` replies).
    `` | `public Actor<T>` | `onError(BiConsumer<Throwable, ? super T> handler)` Registers a handler invoked when the message processor throws.
    `` | `public Actor<T>` | `onError(TriConsumer<ActorContext<T>, Throwable, ? super T> handler)` Context-aware variant of onError(BiConsumer).
    `<T, R>` | `public static Actor<T>` | `reactor(Function<T, R> handler)` Creates a stateless reactor actor.
    `<T, R>` | `public static Actor<T>` | `reactor(Function<T, R> handler, ActorOptions options)` Creates a stateless reactor actor with explicit ActorOptions controlling the mailbox and executor.
    `<T, R>` | `public static Actor<T>` | `reactor(ReactorHandler<T, R> handler)` Context-aware variant of reactor(Function).
    `<T, R>` | `public static Actor<T>` | `reactor(ReactorHandler<T, R> handler, ActorOptions options)` Context-aware variant of reactor(Function, ActorOptions).
    `` | `public void` | `send(T message)` Sends a message to this actor.
    `<R>` | `public Awaitable<R>` | `sendAndGet(T message)` Sends a message and returns an Awaitable that completes with the reply.
    `<T, S>` | `public static Actor<T>` | `stateful(S initialState, BiFunction<S, T, S> handler)` Creates a stateful actor.
    `<T, S>` | `public static Actor<T>` | `stateful(S initialState, BiFunction<S, T, S> handler, ActorOptions options)` Creates a stateful actor with explicit ActorOptions controlling the mailbox and executor.
    `<T, S>` | `public static Actor<T>` | `stateful(S initialState, StatefulHandler<S, T> handler)` Context-aware variant of stateful(Object, BiFunction).
    `<T, S>` | `public static Actor<T>` | `stateful(S initialState, StatefulHandler<S, T> handler, ActorOptions options)` Context-aware variant of stateful(Object, BiFunction, ActorOptions).
    `` | `public void` | `stop()` Stops this actor gracefully.

  - ### Inherited Methods Summary

    Inherited Methods
    Methods inherited from class | Name
    --- | ---
    `interface AutoCloseable` | `close`

- - ### Method Detail

    - #### public void close()

      Stops this actor. Equivalent to stop().

    - #### <T> public static Actor<T> currentSelf()

      Returns the actor whose handler is currently executing on this thread.

      This convenience lets callers using the simple Function / BiFunction factories self-stop without restructuring to the context-aware overloads. Prefer the context-aware overloads where possible.

      Support is opt-in per actor: the actor must be configured with ActorOptions.withCurrentSelf. The default options do not publish the thread-local, so this method throws IllegalStateException unless the actor was constructed with the flag enabled.

      throws:
        IllegalStateException if called outside an actor handler, or from an actor not configured with ActorOptions.withCurrentSelf
      Type Parameters:
        `T` - the actor's message type, inferred at the call site
      Returns:
        the actor currently executing a handler on this thread
      Since:
        6.0.0

    - #### public boolean isActive()

      Returns `true` while the actor is accepting new sends.

      The actor lifecycle has three states, expressed via this method and isTerminated():

      - accepting — `isActive() == true`, `isTerminated() == false`: the actor accepts new sends and is processing them.
      - draining — `isActive() == false`, `isTerminated() == false`: entered immediately when stop() is called. Further sends throw IllegalStateException, but messages already queued (or sent in a race with `stop`) continue to run.
      - terminated — `isActive() == false`, `isTerminated() == true`: the worker has exited; the queue and any stash have been processed (or, for stashed `sendAndGet` replies, rejected).

    - #### public boolean isTerminated()

      Returns `true` once the worker has fully exited — i.e. the queue and any stashed messages have been processed (or rejected, in the case of stashed `sendAndGet` replies). Always `false` while isActive() is true; becomes true some time after stop() is called, once draining completes.

      Since:
        6.0.0

    - #### public Actor<T> onError(BiConsumer<Throwable, ? super T> handler)

      Registers a handler invoked when the message processor throws.

      Fire-and-forget send otherwise has no way to surface a handler exception; this hook is the supported way to log, record metrics for, or react to those failures. For sendAndGet the failure is still reported through the returned `Awaitable`; the `onError` handler runs in addition.

      To stop the actor from inside an error handler, prefer the context-aware overload onError(TriConsumer) — `ctx.self().stop()` works on any actor. Alternatively, if the actor was built with ActorOptions.withCurrentSelf, call Actor.<T>; without that opt-in `currentSelf()` throws.

      Exceptions thrown from the handler itself are caught and discarded so the actor's processing loop is not destabilised. Replacing a previously registered handler replaces it wholesale — there is no chaining.

      Register before the first send. An `onError` call that happens after a message is already in flight may not see that message's failure: only handlers visible to the worker by the time it dispatches a given message are invoked for it. To guarantee coverage, chain `onError` into actor construction — as in the example below — and avoid registering it later.

          def actor = Actor.reactor(handler).onError { Throwable t, msg ->
               log.warn("actor failed processing {", msg, t)
           }
           }

      throws:
        UnsupportedOperationException if the implementation does not support custom error handling
      Parameters:
        `handler` - invoked as `(throwable, message)`
      Returns:
        this actor, for chaining
      Since:
        6.0.0

    - #### public Actor<T> onError(TriConsumer<ActorContext<T>, Throwable, ? super T> handler)

      Context-aware variant of onError(BiConsumer). The handler receives an ActorContext that can be used to stop the actor via `ctx.self().stop()`.

      throws:
        UnsupportedOperationException if the implementation does not support custom error handling
      Parameters:
        `handler` - invoked as `(context, throwable, message)`
      Returns:
        this actor, for chaining
      Since:
        6.0.0

    - #### <T, R> public static Actor<T> reactor(Function<T, R> handler)

      Creates a stateless reactor actor. Each message is passed to the handler function, and the return value becomes the reply for sendAndGet callers.

          var doubler = Actor.reactor(n -> (int) n * 2);
           System.out.println(AsyncSupport.await(doubler.sendAndGet(5))); // 10

      Parameters:
        `handler` - the message processing function
      Type Parameters:
        `T` - the message type
        `R` - the reply type
      Returns:
        a started actor

    - #### <T, R> public static Actor<T> reactor(Function<T, R> handler, ActorOptions options)

      Creates a stateless reactor actor with explicit ActorOptions controlling the mailbox and executor.

      Since:
        6.0.0

    - #### <T, R> public static Actor<T> reactor(ReactorHandler<T, R> handler)

      Context-aware variant of reactor(Function). The handler receives an ActorContext alongside the message and can stop the actor via `ctx.self().stop()`.

      Since:
        6.0.0

    - #### <T, R> public static Actor<T> reactor(ReactorHandler<T, R> handler, ActorOptions options)

      Context-aware variant of reactor(Function, ActorOptions).

      Since:
        6.0.0

    - #### public void send(T message)

      Sends a message to this actor. The message is queued and processed asynchronously. Fire-and-forget — no reply is expected.

      Bounded-mailbox interaction (see ActorOptions.Overflow):

      - ActorOptions.Overflow#BLOCK: the calling thread blocks until queue capacity is available, then enqueues.
      - ActorOptions.Overflow#FAIL: throws IllegalStateException when the mailbox is full.
      - ActorOptions.Overflow#DROP_NEWEST: the message is silently dropped — there is no reply to carry the failure, so a fire-and-forget overflow is invisible to the sender. If you need drop visibility, prefer sendAndGet (which binds the dropped reply to IllegalStateException) or a different overflow policy.
      throws:
        IllegalStateException if the actor has been stopped, or if the mailbox is bounded with ActorOptions.Overflow#FAIL and is full
      Parameters:
        `message` - the message to send

    - #### <R> public Awaitable<R> sendAndGet(T message)

      Sends a message and returns an Awaitable that completes with the reply. For reactors, the reply is the handler's return value. For stateful actors, the reply is the new state.

      Bounded-mailbox interaction (see ActorOptions.Overflow):

      - ActorOptions.Overflow#BLOCK: the calling thread blocks until queue capacity is available, then enqueues.
      - ActorOptions.Overflow#FAIL: throws IllegalStateException when the mailbox is full.
      - ActorOptions.Overflow#DROP_NEWEST: returns an `Awaitable` that completes exceptionally with IllegalStateException indicating the message was dropped; the handler is never invoked.
      throws:
        IllegalStateException if the actor has been stopped, or if the mailbox is bounded with ActorOptions.Overflow#FAIL and is full
      Parameters:
        `message` - the message to send
      Type Parameters:
        `R` - the reply type
      Returns:
        an awaitable reply

    - #### <T, S> public static Actor<T> stateful(S initialState, BiFunction<S, T, S> handler)

      Creates a stateful actor. The handler receives the current state and the message, and returns the new state. For sendAndGet callers, the new state is the reply.

          var counter = Actor.stateful(0, (state, msg) -> {
               if ("increment".equals(msg)) return (int) state + 1;
               return state;
           );
           counter.send("increment");
           System.out.println(AsyncSupport.await(counter.sendAndGet("increment"))); // 2
           }

      Note: the state type `S` is fixed at construction; if a later ActorContext.become call swaps in a StatefulHandler expecting an incompatible `S`, the resulting ClassCastException surfaces on the next dispatch rather than at the swap site.

      Parameters:
        `initialState` - the initial state
        `handler` - receives (state, message), returns new state
      Type Parameters:
        `T` - the message type
        `S` - the state type
      Returns:
        a started actor

    - #### <T, S> public static Actor<T> stateful(S initialState, BiFunction<S, T, S> handler, ActorOptions options)

      Creates a stateful actor with explicit ActorOptions controlling the mailbox and executor.

      Since:
        6.0.0

    - #### <T, S> public static Actor<T> stateful(S initialState, StatefulHandler<S, T> handler)

      Context-aware variant of stateful(Object, BiFunction). The handler receives an ActorContext alongside the state and message and can stop the actor via `ctx.self().stop()`.

      Since:
        6.0.0

    - #### <T, S> public static Actor<T> stateful(S initialState, StatefulHandler<S, T> handler, ActorOptions options)

      Context-aware variant of stateful(Object, BiFunction, ActorOptions).

      Since:
        6.0.0

    - #### public void stop()

      Stops this actor gracefully. Messages already in the queue are processed before the actor shuts down. New sends after stop throw IllegalStateException.