Saturday, May 23, 2026

Class level locking in C++ - serializing multiple threads...


When I studied Android AsyncTask, especially

when Serialexecutor was the norm, I found how

Google engineers have designed to serialize

execution of multiple threads. My original

code simulating similar effects using Java

was done more than a decade ago. Here it is.


Today I did the same effect, but this time with modern C++.


Here we go.


//============================================================================

// Name : ClassLevelLockingThreadSerialization.cpp

// Author : Som

// Version :

// Copyright : som-itsolutions

// Description : Hello World in C++, Ansi-style

//============================================================================


#include <iostream>

#include <future>

#include <chrono>

#include <mutex>


class ExampleClass {

private:

static std::mutex classMutex;


public:


static void testMethod(const std::string& threadName) {


std::lock_guard<std::mutex> lock(classMutex);


for (int i = 0; i < 10; i++) {


std::cout

<< "The testMethod for "

<< threadName

<< std::endl;


std::this_thread::sleep_for(

std::chrono::seconds(1)

);

}

}

};


std::mutex ExampleClass::classMutex;


int main() {


auto f1 = std::async(

std::launch::async,

ExampleClass::testMethod,

"Thread 1"

);


auto f2 = std::async(

std::launch::async,

ExampleClass::testMethod,

"Thread 2"

);


f1.get();

f2.get();

}


Explanation


Understanding the Synchronization

The important part of the program is:

std::lock_guard<std::mutex> lock(classMutex);

This line acquires the mutex before entering the critical section. Since the mutex is declared as static, it is shared across all threads and all invocations of the method.

As a result:

  • only one thread can execute testMethod() at a time
  • the second thread waits until the first thread releases the mutex
  • thread execution becomes serialized

This is conceptually similar to Java’s static synchronized methods.

Why Use std::lock_guard?

Modern C++ prefers RAII-based locking instead of manually calling:

mutex.lock();
mutex.unlock();

std::lock_guard automatically releases the mutex when the object goes out of scope, making the code safer and exception-resistant.

Why Use std::async?

Instead of manually creating and managing std::thread objects, std::async provides a higher-level abstraction for asynchronous task execution.

Benefits include:

  • automatic thread management
  • future-based synchronization
  • cleaner code
  • safer resource handling

Final Thoughts

Modern C++ concurrency has evolved significantly from traditional thread programming. Features such as futures, mutexes, async execution, coroutines, and task systems enable developers to write safer and more scalable concurrent applications.

This small example demonstrates a foundational concept that appears everywhere in modern systems programming:

  • game engines
  • rendering systems
  • simulation frameworks
  • CAD software
  • scientific computing systems

Understanding synchronization primitives like mutexes is an important first step toward mastering advanced concurrent architectures in modern C++.

Thursday, May 14, 2026

Class Level Locking in Java - inspired by Android's Asynctask implementation - serializing multiple threads...




After many months, I have opened my Java workspace and saw my implementation of Class Level locking used for thread serialization done more than a decade ago. This was inspired by Android's AsyncTask source code.

Here is the source code...

package com.somitsolutions.java.training.classlevellockingwithstaticnestedclass;



public class ExampleClass {

public ExampleClass(){

}

public static InnerNestedClass objInnerNestedClass = new InnerNestedClass();

static class InnerNestedClass{

public synchronized void testMethod(){

try {

for (int i = 0; i<10; i++){

System.out.println ("The testMethod for " + Thread.currentThread().getName() + " Object");

Thread.sleep(2000);

}

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

}




package com.somitsolutions.java.training.classlevellockingwithstaticnestedclass;


public class R implements Runnable{

final ExampleClass exampleClassObject = new ExampleClass();

//final ExampleClass exampleClassObject2 = new ExampleClass();

public void run(){

exampleClassObject.objInnerNestedClass.testMethod();

}

}




package com.somitsolutions.java.training.classlevellockingwithstaticnestedclass;


public class Main {


public static void main(String[] args) {

// TODO Auto-generated method stub

R r1 = new R();

R r2 = new R();

Thread t1 = new Thread(r1);

Thread t2 = new Thread(r2);

t1.setName("Thread 1");

t2.setName("Thread 2");

t1.start();

t2.start();

}

}


1. What Exactly Is Happening Here?

We have:

public static InnerNestedClass objInnerNestedClass = new InnerNestedClass();

This line is the key.

Because the object is declared static, there is only one instance of InnerNestedClass for the entire JVM class ExampleClass.

No matter how many ExampleClass objects you create:

new ExampleClass();
new ExampleClass();
new ExampleClass();

they all share:

ExampleClass.objInnerNestedClass

There is exactly ONE monitor lock associated with that object.


2. Understanding the Synchronization

Inside the nested class:

public synchronized void testMethod()

This means:

synchronized(this)

So the lock is acquired on:

objInnerNestedClass

Since there is only ONE static instance:

public static InnerNestedClass objInnerNestedClass

all threads compete for the SAME monitor lock.


3. Flow of Execution

Step-by-step

You create:

R r1 = new R();
R r2 = new R();

Each R object creates its own:

final ExampleClass exampleClassObject = new ExampleClass();

So now you have:

  • Two different ExampleClass objects

  • BUT both point to the SAME static object

Conceptually:

ExampleClass Object A
        |
        ---> static objInnerNestedClass ----> [ONE OBJECT]

ExampleClass Object B
        |
        ---> static objInnerNestedClass ----> [SAME OBJECT]

4. What Happens When Threads Run?

Thread 1:

exampleClassObject.objInnerNestedClass.testMethod();

Thread 2:

exampleClassObject.objInnerNestedClass.testMethod();

Both are calling:

testMethod()

on the SAME shared object.

Since the method is synchronized:

public synchronized void testMethod()

only ONE thread can enter at a time.


5. Runtime Behavior

Suppose:

  • Thread 1 enters first

  • Thread 2 tries to enter

Then:

Thread 1 acquires monitor lock
Thread 2 BLOCKS

Thread 2 waits until:

Thread 1 exits testMethod()

Only then:

Thread 2 acquires lock

So output becomes SERIALIZED:

Thread 1 messages...
Thread 1 messages...
Thread 1 messages...

THEN

Thread 2 messages...
Thread 2 messages...

instead of interleaving.


6. Why This Is Called "Class-Level Locking"

Strictly speaking, true class-level locking is:

synchronized(ExampleClass.class)

or

public static synchronized void method()

which locks on the Class object itself.

But my example achieves a VERY SIMILAR EFFECT using:

static shared object + synchronized instance method

So effectively:

One shared lock for entire class

Hence, behaviorally, it becomes "class-wide serialization."


7. Why Static Matters

Without static:

public InnerNestedClass objInnerNestedClass

each ExampleClass object would get its OWN nested object.

Then:

Thread 1 -> Lock A
Thread 2 -> Lock B

No contention.

Both threads would run simultaneously.

So static is the entire reason serialization occurs.


8. Relationship to Android AsyncTask

In old Android implementations of Android AsyncTask, Google implemented task serialization using a very similar design.

Internally, AsyncTask had something conceptually similar to:

private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        ...
    }
}

and:

private static final SerialExecutor sDefaultExecutor
        = new SerialExecutor();

Notice the same pattern:


Shared Static Executor

private static final SerialExecutor sDefaultExecutor

ONE executor object for all AsyncTasks.


Synchronized Method

public synchronized void execute(...)

Only one thread could manipulate scheduling state at a time.


Result

Even if you created:

new MyAsyncTask().execute();
new MyAsyncTask().execute();
new MyAsyncTask().execute();

They were serialized through the SAME shared executor.

So tasks are executed one after another.

This was done to avoid:

  • race conditions

  • thread explosion

  • UI instability

  • uncontrolled parallelism

especially on low-memory mobile devices.


9. Why Android Did This

Early Android phones had:

  • very limited RAM

  • single-core CPUs

  • weak scheduling capabilities

If developers accidentally launched many background tasks simultaneously:

UI freezes
Battery drain
ANRs
Memory pressure

could happen.

So Android engineers intentionally serialized AsyncTasks.

Later Android versions introduced:

executeOnExecutor(THREAD_POOL_EXECUTOR)

to allow controlled parallelism.


10. Deeper JVM Insight

Every Java object has an associated:

Monitor Lock

When a synchronized instance method is called:

public synchronized void method()

JVM internally does roughly:

monitorenter(this)
...
monitorexit(this)

Since all threads share the SAME static object:

ONE monitor

becomes the synchronization bottleneck.


11. Why Studying Open Source Code Is Important

This is the most important part.

Reading open-source frameworks teaches things that textbooks usually cannot.

For example, from AsyncTask we learn:

  • real-world concurrency design

  • serialization strategies

  • thread scheduling

  • producer-consumer patterns

  • executor frameworks

  • synchronization tradeoffs


Theory vs Reality

A textbook may say:

"synchronized prevents race conditions"

But Android source code shows:

WHY engineers used synchronization
WHERE they used it
WHAT problem they were solving
WHAT tradeoffs they accepted

That is real engineering knowledge.


12. Why Great Engineers Read Source Code

Engineers who study frameworks like:

  • OpenJDK

  • Android

  • Linux kernel

  • OpenFOAM

  • FreeCAD

develop:

  • architectural thinking

  • systems intuition

  • debugging maturity

  • performance awareness

  • concurrency understanding

far beyond ordinary programming.


13. What You Are Actually Learning Here

My small example contains concepts from:

  • JVM monitor implementation

  • object lifetime

  • static memory model

  • synchronization semantics

  • concurrent scheduling

  • executor design

  • Android framework architecture

This is exactly why studying framework source code is powerful.

You stop seeing programming as:

"writing syntax"

and begin seeing it as:

"designing systems"

That transition is what separates an average coder from a strong software engineer.

Saturday, May 9, 2026

Walking Alone, Building Bharat: Why Intrinsic Motivation Defines the Future of Innovation


There comes a moment in every meaningful journey when a person realizes that leadership is not about applause, followers, or external validation. It is about conviction. It is about walking forward even when nobody accompanies you.

The timeless line from Ekla Chalo Re captures this spirit perfectly:

“If nobody responds to your call, then walk alone.”

This is not merely poetry. It is a philosophy of leadership.

A true leader does not wait for social approval before pursuing truth, innovation, or transformation. The ability to continue despite isolation is perhaps the deepest form of intrinsic motivation. History repeatedly shows that every major civilizational leap began with individuals willing to walk alone against convention.

Today, this mindset is becoming increasingly visible across India — or as many passionately call it, Bharat.

The Rise of a New Bharat

A profound transformation is underway.

For decades, elite education in India was concentrated in a few urban centers. But now, talent is emerging from Tier-2 and Tier-3 cities at an unprecedented scale. Access to knowledge is no longer geographically restricted.

The digital revolution has changed the equation.

Online education, open-source learning, recorded lectures from institutions like Indian Institutes of Technology, National Institutes of Technology, and other premier institutions are democratizing technical education.

The implications are enormous.

If a classroom of twenty students in a top engineering institution can produce two or three future entrepreneurs or researchers, what happens when world-class technical education reaches millions of students across the country through the internet?

The answer is simple:
India’s innovation capacity multiplies exponentially.

From Examination Factories to Knowledge Civilization

For years, one criticism often directed toward India was that the country produced service-sector engineers but lacked large-scale original research and deep technological innovation.

That narrative is beginning to change.

The future belongs to nations capable of producing:

  • researchers,

  • deep-tech innovators,

  • hardware engineers,

  • graphics programmers,

  • AI scientists,

  • simulation experts,

  • entrepreneurs,

  • and creators of foundational technology.

India’s growing ecosystem of online technical education, digital libraries, open-source software exposure, and accessible mentorship is slowly laying that foundation.

The next generation is not satisfied with merely consuming technology. They want to build it.

A Small Example of a Bigger Transformation

One of the most striking parts of this story is that the transformation is already visible at the school level.

My son, a Class 9 student, who began learning programming at the age of six. Over time, he explored multiple programming languages, including:

  • Java

  • Python

  • C++

Eventually, his interest moved toward advanced graphics programming using OpenGL.

But the remarkable aspect was not simply learning OpenGL.

Ridit reportedly went further by exposing a custom C++ OpenGL engine to Python using pybind11 — a concept normally encountered in advanced software engineering and systems programming.

This is significant because it reflects something deeper:
Young learners are no longer limiting themselves to textbook knowledge. They are beginning to understand interoperability, engine architecture, bindings, and real-world software ecosystems.

This is precisely how technological civilizations evolve.

The Internet Has Changed the Nature of Talent

In previous generations, access to elite knowledge depended heavily on geography, institutional privilege, or economic background.

Today, a motivated student with:

  • a computer,

  • internet access,

  • curiosity,

  • and discipline

can study subjects that were once accessible only to specialists.

From graphics programming to AI, from computational physics to simulation engineering, the barriers are steadily falling.

This democratization of technical capability may become one of the defining developments of 21st-century India.

Intrinsic Motivation: The Real National Resource

Natural resources matter.
Infrastructure matters.
Capital matters.

But civilizations ultimately rise through motivated human beings.

Intrinsic motivation — the inner drive to learn, create, solve problems, and pursue truth without external reward — is the invisible force behind every scientific and technological revolution.

A nation that cultivates intrinsically motivated students will eventually produce innovators.

And innovators create industries.
Industries create economic power.
Economic power creates strategic independence.

Walking Alone Before Others Join

Every new idea initially appears isolated.

Every breakthrough begins with a small number of believers.

The message is simple:
do not wait for universal approval before beginning meaningful work.

Whether in science, software, engineering, entrepreneurship, or national development, progress often starts with individuals willing to move forward before the crowd understands the direction.

That is leadership.

And perhaps that is the deeper meaning of “walk alone.”

Because eventually, if the path is true, others follow.

Jai Hind... Jai Bharat...

Thursday, April 30, 2026

Ancient Logic Meets Early AI: The Surprising Parallels Between Prolog and Sanskrit - Engineers of Bharat - wake up and embrace Sanskrit...


When we think about Artificial Intelligence, we usually picture modern neural networks, GPUs, and massive datasets. But the intellectual roots of AI go much deeper—into symbolic reasoning, formal logic, and surprisingly, ancient linguistic traditions. One of the most fascinating comparisons is between early AI systems built using Prolog and the structure of Sanskrit, one of the oldest and most rigorously defined languages in human history.

This is not a superficial analogy. At a structural and philosophical level, both Prolog and Sanskrit share striking similarities in how they represent knowledge, rules, and inference.

1. Rule-Based Systems: Sutras vs Clauses

Early AI systems, especially those built in Prolog, rely heavily on rules and facts. A Prolog program is essentially a knowledge base composed of logical clauses:

  • Facts: Statements that are always true

  • Rules: Conditional relationships that derive new truths

Similarly, Sanskrit—especially as formalized by the ancient grammarian Pāṇini—is built on a system of sutras (rules). These are concise, highly optimized statements that define how words are formed and how grammar operates.

Both systems:

  • Encode knowledge as compact rules

  • Allow complex structures to emerge from simple primitives

  • Depend on rule application rather than procedural steps

In a sense, Pāṇini’s grammar can be viewed as one of the earliest known “programs.”

2. Declarative Nature

Prolog is a declarative language. You don’t tell the system how to solve a problem—you tell it what is true, and the system figures out the rest through logical inference.

Sanskrit grammar operates similarly:

  • It defines what constitutes valid language

  • It does not prescribe step-by-step generation in a procedural sense

  • Instead, valid expressions are derived through rule application

This declarative paradigm is fundamentally different from imperative programming—and both Prolog and Sanskrit embody it elegantly.

3. Pattern Matching and Unification

One of the core mechanisms in Prolog is unification—a process of matching patterns and binding variables to satisfy logical conditions.

Example (conceptually):

parent(X, Y) :- mother(X, Y).

The system tries to match patterns and infer relationships.

In Sanskrit:

  • Word formation and sentence construction involve pattern transformations

  • Roots (dhatus) combine with suffixes following strict matching rules

  • Morphological changes depend on context-sensitive patterns

This resembles a form of linguistic unification, where structures are matched and transformed based on rules.

4. Backtracking and Multiple Interpretations

Prolog uses backtracking to explore multiple possible solutions. If one path fails, it goes back and tries another.

Sanskrit, especially in classical literature:

  • Allows multiple valid interpretations of a sentence

  • Meaning can depend on context, case endings, and word order

  • Ambiguity is resolved through structured inference

While Sanskrit doesn’t “execute” backtracking computationally, its structure supports multi-path interpretation, similar to logical exploration in Prolog.

5. Compositionality and Generative Power

Both systems are highly compositional:

  • In Prolog, small rules combine to solve complex problems

  • In Sanskrit, small grammatical units combine to generate vast expressive possibilities

This compositional nature leads to:

  • Scalability of expression

  • Elegant reuse of rules

  • High generative capacity from limited primitives

6. Knowledge Representation

Prolog was designed for symbolic AI, where knowledge is explicitly represented and reasoned about.

Sanskrit, particularly in philosophical and scientific texts:

  • Encodes knowledge in a structured, rule-based format

  • Maintains clarity and precision in meaning

  • Supports logical discourse in fields like mathematics, astronomy, and philosophy

This makes Sanskrit not just a language, but a knowledge representation system.

7. Minimalism and Compression

Pāṇini’s grammar is famous for its extreme brevity. Rules are compressed using meta-rules, recursion, and symbolic shorthand.

Prolog also encourages:

  • Minimal representations

  • Reusable logic

  • Compact expression of complex relationships

Both systems aim for maximum expressiveness with minimal redundancy—a hallmark of elegant design.

8. Philosophical Foundations

At a deeper level, both Prolog and Sanskrit emerge from traditions that value:

  • Logic over procedure

  • Structure over execution

  • Inference over instruction

Prolog comes from formal logic and computational theory. Sanskrit emerges from a philosophical tradition deeply concerned with language, meaning, and cognition.

The convergence is not accidental—it reflects a shared pursuit of modeling intelligence through structure.

Conclusion: Rediscovering Intelligence Through Structure

Modern AI has largely shifted toward data-driven approaches like deep learning. But the comparison between Prolog and Sanskrit reminds us of an alternative vision of intelligence—one rooted in rules, logic, and symbolic reasoning.

For developers, linguists, and AI researchers, this intersection offers a powerful insight:

Intelligence is not just about learning patterns from data—it is also about representing and manipulating knowledge with precision.

In that sense, ancient Sanskrit and early AI are not distant domains—they are parallel explorations of the same fundamental question:

How can structured rules give rise to intelligent behavior?

If we revisit these ideas with modern tools, we may find that the future of AI is not just in neural networks—but also in rediscovering the elegance of symbolic systems that civilizations mastered thousands of years ago.

Let’s make this concrete with a small Prolog program, and then examine it through the lens of Sanskrit grammar and structure—not as a metaphor, but as a structural comparison.


🔹 A Simple Prolog Program

% Facts
father(ram, shyam).
father(ram, sita).
mother(gita, shyam).
mother(gita, sita).

% Rule
parent(X, Y) :- father(X, Y).
parent(X, Y) :- mother(X, Y).

% Rule for sibling relationship
sibling(X, Y) :- parent(Z, X), parent(Z, Y), X \= Y.

What this program does:

  • Defines facts about family relationships

  • Defines rules to infer:

    • Who is a parent

    • Who are siblings

Example query:

?- sibling(shyam, sita).

Output:

true.

Now: Analysis Through the Lens of Sanskrit

We’ll map key Prolog concepts to structural principles found in Sanskrit, especially in the grammatical system of Pāṇini and his work, the Ashtadhyayi.

1. Facts as “Pratijñā” (Given Truths)

In Prolog:

father(ram, shyam).

This is an atomic truth.

In Sanskrit:

  • This resembles a semantic assertion, like:

    • रामः श्यामस्य पिता अस्ति (Rāma is Shyama’s father)

In the Paninian system:

  • Such statements are not “computed”

  • They are accepted inputs to the system

👉 Parallel:

  • Prolog facts = Given semantic truths (pratijñā-like statements)

2. Rules as Sutras (सूत्र)

Prolog rule:

parent(X, Y) :- father(X, Y).

This reads:

X is a parent of Y if X is a father of Y

In Sanskrit grammar:

  • A sutra defines transformation or classification rules

  • Example idea (not literal):

    • “If a root has property X, apply suffix Y”

👉 Both share:

  • Conditional structure

  • Minimal expression

  • High reuse

👉 Key insight:

  • Prolog rules behave like generative sutras—they don’t store outcomes, they define how to derive them

3. Variables as “Anubandha” (Markers / Placeholders)

In Prolog:

parent(X, Y)
  • X and Y are placeholders

In Sanskrit grammar:

  • Pāṇini uses markers (anubandhas) and abstract symbols

  • These are not actual words but meta-linguistic variables

👉 Parallel:

  • Prolog variables ≈ Paninian symbolic placeholders

They:

  • Do not carry meaning themselves

  • Gain meaning through substitution

4. Unification vs Sandhi / Morphological Matching

Prolog uses unification:

  • It tries to match:

parent(Z, X), parent(Z, Y)

In Sanskrit:

  • Word formation uses rule-based matching

  • Example:

    • Roots + suffixes combine only if conditions match

    • Sandhi rules merge sounds based on patterns

👉 Parallel:

  • Prolog unification ≈ rule-based linguistic matching

Both systems:

  • Depend on pattern compatibility

  • Apply transformations only when constraints are satisfied

5. Backtracking vs Interpretive Flexibility

In Prolog:

  • If one rule fails, it backtracks and tries another

In Sanskrit:

  • A sentence can allow multiple valid parses

  • Meaning emerges from:

    • case endings (vibhakti)

    • context

    • syntactic relations

Example:

  • Word order is flexible, but meaning is preserved via rules

👉 Parallel:

  • Prolog backtracking ≈ multi-path interpretation in Sanskrit parsing

6. The Sibling Rule as a Composite Sutra

sibling(X, Y) :- parent(Z, X), parent(Z, Y), X \= Y.

This is powerful:

  • It composes multiple rules

  • Introduces a constraint

In Sanskrit:

  • Complex constructions emerge from:

    • multiple interacting sutras

    • constraint rules (like “not equal” conditions in morphology)

👉 This resembles:

  • compound rule application (samāsa-like compositionality)

7. Negation Constraint (X = Y)

This part:

X \= Y

Means:

  • X and Y must be different

In Sanskrit:

  • There are blocking rules (नियम / प्रतिबंध)

  • Certain forms are prevented under specific conditions

👉 Parallel:

  • Logical negation ≈ grammatical restriction rules

8. Knowledge Emergence

Important insight:

  • Nowhere did we explicitly define:

sibling(shyam, sita).

Yet it emerges.

In Sanskrit:

  • Infinite valid sentences are generated from:

    • finite rules (sutras)

👉 Both systems:

  • Are generative, not enumerative

Deep Insight: Computation vs Derivation

ConceptPrologSanskrit
KnowledgeStored as factsEncoded via roots & meanings
RulesLogical clausesSutras
ExecutionQuery resolutionDerivation (prakriya)
EngineBacktracking searchRule ordering + constraints
OutputLogical truthValid linguistic expression

Final Thought

If you look carefully, this Prolog program is not “code” in the modern imperative sense.

It is closer to a derivation system—and that is exactly what Sanskrit grammar is.

👉 Both answer the same deep question:

How can a finite set of rules generate an infinite space of valid structures?

That’s why many researchers—from early AI pioneers to modern computational linguists—have seen Sanskrit not just as a language, but as a formal system of knowledge representation, remarkably aligned with symbolic AI like Prolog.

Read here...


Monday, April 27, 2026

Parents are the best Guru - the reason why I rediscovered myself as the Guru of my young son...

 Taken from a X post...


They called it caste.

Look again.

A child sits beside his craftsman father.

Not in a classroom. Not with a certificate.

But inside a living workshop.

Hands learning before language does.

Skill transferring without textbooks.

No degree. No loan. No placement cell.

Just immersion.

This is apprenticeship.

Generation to generation.

Precision built through repetition, not exams.

And then we reframed it.

From *knowledge system* → to *social problem*.

From *skill inheritance* → to *rigid label*.

Yes, hierarchies existed. Yes, distortions happened.

But pause before flattening everything into one word.

Because something else was happening here too-

A self-sustaining skill economy.

No HR. No résumé. No unemployment portal.

Today?

We spend ₹10–20 lakh on degrees…

to still “learn on the job.”

So ask-

Did we reform a system…

or replace it with a costlier, slower one?

And more importantly-

Who lost more in that transition?

Wake up... the Hindu community of Bharat... The clock is ticking...

You will have to touch the ground running if you want to survive...

Here's what it takes to create a software engineer.

My young son, Ridit's tech blog.


Friday, April 17, 2026

Active Object Design Pattern - and a simple implementation using Julia - from Active Object paradigm of Symbian S60 to today's journey in Julia - a checkered career...

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)


Sequence Diagram:



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))

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

In my code:

put!(ao, job_function)

So:

The Channel (ao) acts as the proxy interface

Activation Queue

ch = Channel{Function}(32)

This is the Activation Queue.

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

Classic role:

Queue<Request>

My version:

Channel{Function}

Each Function = a method request object

Method Request

() -> heavy_compute(10^7 + i)

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

In traditional OO:

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

In Julia:

() -> heavy_compute(10^7 + i)

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

This block plays two roles:

Scheduler

for job in ch
  • Pulls requests from the queue
  • Decides execution order (FIFO here)

This is the scheduler

Servant

Base.invokelatest(job)
  • Actually executes the request

This is the servant

So each worker thread is:

[ Scheduler + Servant ]

Thread Pool (Multiple Active Objects Workers)

for _ in 1:nworkers
Threads.@spawn ...
end
  • 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)
  • Signals: no more requests
  • Workers stop after finishing remaining jobs

Waiting for completion

foreach(wait, tasks)
  • Ensures all scheduled work completes
And here's my journey through the Symbian S60's Active Object paradigm - studied many years ago...