Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ $ mvnw clean install -Dmaven.javadoc.skip=true -DskipTests -PskipBundlePlugin,mi

https://quickfix-j.github.io/quickfixj/quickfixj-core/src/main/doc/usermanual/usage/configuration.html

## threading model

[docs/threading-model.md](./docs/threading-model.md)

## basics

### related projects
Expand Down
112 changes: 112 additions & 0 deletions docs/threading-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# QuickFIX/J Threading Model

QuickFIX/J provides two threading strategies for processing FIX messages. The choice of strategy
affects how your application handles concurrent sessions and what thread-safety guarantees you must
provide.

## Threading Strategies

### Single-Threaded Strategy

**Classes:** `SocketAcceptor` / `SocketInitiator`

All sessions share a single message-processing thread (named `QFJ Message Processor`). Incoming
messages from all sessions are placed in a shared queue and dispatched one at a time by this thread.

This means your `Application` callbacks (`fromApp`, `fromAdmin`, etc.) are always invoked from the
same thread, so you do not need to make your application code thread-safe with respect to concurrent
session callbacks. However, a slow callback will delay message processing for all other sessions.

**Use when:**
- You have a small number of sessions.
- Simplicity and predictable, sequential message processing are more important than throughput.
- You want to avoid the complexity of thread-safe application code.

### Thread-Per-Session Strategy

**Classes:** `ThreadedSocketAcceptor` / `ThreadedSocketInitiator`

Each session gets its own dedicated message-dispatching thread. Incoming messages for a session are
queued and processed by that session's thread independently of other sessions.

Because your `Application` callbacks can be invoked concurrently from multiple session threads, your
application code **must be thread-safe**.

**Use when:**
- You have multiple sessions and need them to process messages independently.
- A slow or blocking callback for one session must not impact other sessions.
- You can ensure your application implementation is thread-safe.

## Queue Capacity and Back-pressure

Both strategies support configuring the internal message queue capacity to control back-pressure:

```java
// Fixed-capacity queue (blocks producers when full)
Acceptor acceptor = new ThreadedSocketAcceptor(
application, storeFactory, settings, logFactory, messageFactory,
queueCapacity);

// Watermark-based flow control
Acceptor acceptor = ThreadedSocketAcceptor.newBuilder()
.withApplication(application)
.withMessageStoreFactory(storeFactory)
.withSettings(settings)
.withLogFactory(logFactory)
.withMessageFactory(messageFactory)
.withQueueLowerWatermark(lowerWatermark)
.withQueueUpperWatermark(upperWatermark)
.build();
```

The same constructors and builder options are available on `SocketAcceptor`, `SocketInitiator`, and
`ThreadedSocketInitiator`.

## Choosing a Strategy

| | `SocketAcceptor` / `SocketInitiator` | `ThreadedSocketAcceptor` / `ThreadedSocketInitiator` |
|---|---|---|
| Message processing | Single shared thread | One thread per session |
| Application thread-safety required | No | Yes |
| Session isolation | No | Yes |
| Typical use case | Few sessions, simple apps | Many sessions, independent processing |

## Example: Starting an Acceptor

```java
import quickfix.*;
import java.io.FileInputStream;

public class MyApp {

public static void main(String[] args) throws Exception {
Application application = new MyApplication();
SessionSettings settings = new SessionSettings(new FileInputStream(args[0]));
MessageStoreFactory storeFactory = new FileStoreFactory(settings);
LogFactory logFactory = new FileLogFactory(settings);
MessageFactory messageFactory = new DefaultMessageFactory();

// Single-threaded: all sessions share one message-processing thread
Acceptor acceptor = new SocketAcceptor(
application, storeFactory, settings, logFactory, messageFactory);

// OR thread-per-session: each session has its own message-processing thread
// (application must be thread-safe)
// Acceptor acceptor = new ThreadedSocketAcceptor(
// application, storeFactory, settings, logFactory, messageFactory);

acceptor.start();
// ... run your application ...
acceptor.stop();
}
}
```

## Thread Safety Guidance

Regardless of which strategy you choose, note that `Session.sendToTarget()` is thread-safe and may
be called from any thread to send outgoing messages.

When using `ThreadedSocketAcceptor` or `ThreadedSocketInitiator`, ensure that any shared state
accessed in your `Application` implementation (e.g., order books, maps, counters) is properly
synchronized or uses thread-safe data structures.
1 change: 1 addition & 0 deletions quickfixj-core/src/main/doc/usermanual/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ <h2>Using QuickFIX/J</h2>
<ul>
<li><a href="usage/application.html">Creating Your Application</a></li>
<li><a href="usage/configuration.html">Configuration</a></li>
<li><a href="../../../../../docs/threading-model.md">Threading Model</a></li>
<li><a href="usage/acceptor_failover.html">Acceptor Failover Support</a></li>
<li><a href="usage/acceptor_dynamic.html">Dynamic Acceptor Session Definition</a></li>
<li><a href="usage/receiving_messages.html">Receiving Messages</a></li>
Expand Down
Loading