DEV Community

Somenath Mukhopadhyay
Somenath Mukhopadhyay

Posted on • Originally published at som-itsolutions.blogspot.com on

Active Object Design Pattern, implemented using Julia - from Active Object paradigm of Symbian S60 to today's journey in Julia

The Active Object Design Pattern is a concurrency pattern that decouples method invocation from method execution, allowing tasks to run asynchronously without blocking the caller.

At its core, an Active Object introduces a proxy that clients interact with. Instead of executing methods directly, the proxy places requests into a queue. A separate worker thread (or pool) processes these requests in the background. This creates a clean separation between what needs to be done and when/how it gets executed.

A typical Active Object system has four key components:

  • Proxy – exposes the interface to the client

  • Method Request – encapsulates a function call as an object or callable

  • Activation Queue – holds pending requests

  • Scheduler/Worker – executes requests asynchronously

This pattern is especially useful when:

  • You want to avoid blocking the main thread

  • You need controlled concurrency (e.g., limited worker threads)

  • You want to serialize access to shared resources safely

Here's a simple implementation of Active Object Design Pattern in Julia.

function ThreadedActiveObject(nworkers=4)
    ch = Channel{Function}(32)
    tasks = []

    for _ in 1:nworkers
        push!(tasks, Threads.@spawn begin
            for job in ch
                Base.invokelatest(job)
            end
        end)
    end

    return ch, tasks
end

function heavy_compute(n)
    s = 0.0
    for i in 1:n
        s += sin(i) * cos(i)
    end
    println("Computed sum for $n = $s on thread $(Threads.threadid())")
end

ao, tasks = ThreadedActiveObject(4)

for i in 1:10
    put!(ao, () -> heavy_compute(10^7 + i))
end

close(ao)
foreach(wait, tasks)

Enter fullscreen mode Exit fullscreen mode

Sequence Diagram:

Sequence Diagram of Active Object Pattern

Mapping The Code to Active Object Components

Let’s reinterpret the code piece by piece.

Proxy (Client Interface)


put!(ao, () -> heavy\_compute(10^7 + i))

Enter fullscreen mode Exit fullscreen mode

This is the proxy layer.

Why?

  • The caller is not executing the method directly
  • Instead, it:
    • wraps the request as a function (closure)
    • submits it to a queue

In classic Active Object:


proxy.method\_call() → enqueue request

Enter fullscreen mode Exit fullscreen mode

In my code:


put!(ao, job\_function)

Enter fullscreen mode Exit fullscreen mode

So:

The Channel (ao) acts as the proxy interface

Activation Queue


ch = Channel{Function}(32)

Enter fullscreen mode Exit fullscreen mode

This is the Activation Queue.

  • Holds pending method requests
  • Thread-safe
  • Decouples producer and consumer

Classic role:


Queue<Request>

Enter fullscreen mode Exit fullscreen mode

My version:


Channel{Function}

Enter fullscreen mode Exit fullscreen mode

Each Function = a method request object

Method Request


() -> heavy\_compute(10^7 + i)

Enter fullscreen mode Exit fullscreen mode

This is a Method Request object , just expressed as a closure.

In traditional OO:


class PrintTask : MethodRequest {  
void execute() { ... }  
}

Enter fullscreen mode Exit fullscreen mode

In Julia:


() -> heavy\_compute(10^7 + i)

Enter fullscreen mode Exit fullscreen mode

Key idea:

  • Encapsulates:
    • what to do
    • data (i)
    • logic

Scheduler + Servant (Worker Threads)


Threads.@spawn begin  
 for job in ch  
 Base.invokelatest(job)  
 end  
end

Enter fullscreen mode Exit fullscreen mode

This block plays two roles :

Scheduler


for job in ch

Enter fullscreen mode Exit fullscreen mode
  • Pulls requests from the queue
  • Decides execution order (FIFO here)

This is the scheduler

Servant


Base.invokelatest(job)

Enter fullscreen mode Exit fullscreen mode
  • Actually executes the request

This is the servant

So each worker thread is:


[Scheduler + Servant]

Enter fullscreen mode Exit fullscreen mode

Thread Pool (Multiple Active Objects Workers)


for \_ in 1:nworkers  
 Threads.@spawn ...  
end

Enter fullscreen mode Exit fullscreen mode
  • Creates multiple workers
  • All consume from the same queue

This is a multi-threaded Active Object

Classic pattern often has:

  • 1 thread → 1 active object

My version:

N threads → shared activation queue

This is more like:

  • Active Object + Thread Pool hybrid

Lifecycle Control

Closing the queue


close(ao)

Enter fullscreen mode Exit fullscreen mode
  • Signals: no more requests
  • Workers stop after finishing remaining jobs

Waiting for completion


foreach(wait, tasks)

Enter fullscreen mode Exit fullscreen mode
  • Ensures all scheduled work is completed

And here's my journey through the Symbian S60's Active Object paradigm - studied many years ago...

Active Object SymbianActive Object Symbian

Top comments (0)