Skip to content

Commit a6b7b43

Browse files
Copilotchrjohn
andauthored
Add strategy comparison table and acceptor example to threading-model.md from #1173
Agent-Logs-Url: https://github.com/quickfix-j/quickfixj/sessions/400b6836-0665-474e-84ba-00cf1944ea0f Co-authored-by: chrjohn <6644028+chrjohn@users.noreply.github.com>
1 parent 23610bb commit a6b7b43

3 files changed

Lines changed: 392 additions & 0 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ $ mvnw clean install -Dmaven.javadoc.skip=true -DskipTests -PskipBundlePlugin,mi
6969

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

72+
## threading model
73+
74+
For a detailed description of the QuickFIX/J threading model — including the single-threaded vs. thread-per-session strategies, the timer thread, heartbeat management, queue back-pressure, and thread-safety implications for application developers — see [docs/threading-model.md](docs/threading-model.md).
75+
7276
## basics
7377

7478
### related projects

docs/threading-developer-guide.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# QuickFIX/J Threading Model — Developer Guide
2+
3+
QuickFIX/J provides two threading strategies for processing FIX messages. The choice of strategy
4+
affects how your application handles concurrent sessions and what thread-safety guarantees you must
5+
provide.
6+
7+
## Threading Strategies
8+
9+
### Single-Threaded Strategy
10+
11+
**Classes:** `SocketAcceptor` / `SocketInitiator`
12+
13+
All sessions share a single message-processing thread (named `QFJ Message Processor`). Incoming
14+
messages from all sessions are placed in a shared queue and dispatched one at a time by this thread.
15+
16+
This means your `Application` callbacks (`fromApp`, `fromAdmin`, etc.) are always invoked from the
17+
same thread, so you do not need to make your application code thread-safe with respect to concurrent
18+
session callbacks. However, a slow callback will delay message processing for all other sessions.
19+
20+
**Use when:**
21+
- You have a small number of sessions.
22+
- Simplicity and predictable, sequential message processing are more important than throughput.
23+
- You want to avoid the complexity of thread-safe application code.
24+
25+
### Thread-Per-Session Strategy
26+
27+
**Classes:** `ThreadedSocketAcceptor` / `ThreadedSocketInitiator`
28+
29+
Each session gets its own dedicated message-dispatching thread. Incoming messages for a session are
30+
queued and processed by that session's thread independently of other sessions.
31+
32+
Because your `Application` callbacks can be invoked concurrently from multiple session threads, your
33+
application code **must be thread-safe**.
34+
35+
**Use when:**
36+
- You have multiple sessions and need them to process messages independently.
37+
- A slow or blocking callback for one session must not impact other sessions.
38+
- You can ensure your application implementation is thread-safe.
39+
40+
## Queue Capacity and Back-pressure
41+
42+
Both strategies support configuring the internal message queue capacity to control back-pressure:
43+
44+
```java
45+
// Fixed-capacity queue (blocks producers when full)
46+
Acceptor acceptor = new ThreadedSocketAcceptor(
47+
application, storeFactory, settings, logFactory, messageFactory,
48+
queueCapacity);
49+
50+
// Watermark-based flow control
51+
Acceptor acceptor = ThreadedSocketAcceptor.newBuilder()
52+
.withApplication(application)
53+
.withMessageStoreFactory(storeFactory)
54+
.withSettings(settings)
55+
.withLogFactory(logFactory)
56+
.withMessageFactory(messageFactory)
57+
.withQueueLowerWatermark(lowerWatermark)
58+
.withQueueUpperWatermark(upperWatermark)
59+
.build();
60+
```
61+
62+
The same constructors and builder options are available on `SocketAcceptor`, `SocketInitiator`, and
63+
`ThreadedSocketInitiator`.
64+
65+
## Choosing a Strategy
66+
67+
| | `SocketAcceptor` / `SocketInitiator` | `ThreadedSocketAcceptor` / `ThreadedSocketInitiator` |
68+
|---|---|---|
69+
| Message processing | Single shared thread | One thread per session |
70+
| Application thread-safety required | No | Yes |
71+
| Session isolation | No | Yes |
72+
| Typical use case | Few sessions, simple apps | Many sessions, independent processing |
73+
74+
## Example: Starting an Acceptor
75+
76+
```java
77+
import quickfix.*;
78+
import java.io.FileInputStream;
79+
80+
public class MyApp {
81+
82+
public static void main(String[] args) throws Exception {
83+
Application application = new MyApplication();
84+
SessionSettings settings = new SessionSettings(new FileInputStream(args[0]));
85+
MessageStoreFactory storeFactory = new FileStoreFactory(settings);
86+
LogFactory logFactory = new FileLogFactory(settings);
87+
MessageFactory messageFactory = new DefaultMessageFactory();
88+
89+
// Single-threaded: all sessions share one message-processing thread
90+
Acceptor acceptor = new SocketAcceptor(
91+
application, storeFactory, settings, logFactory, messageFactory);
92+
93+
// OR thread-per-session: each session has its own message-processing thread
94+
// (application must be thread-safe)
95+
// Acceptor acceptor = new ThreadedSocketAcceptor(
96+
// application, storeFactory, settings, logFactory, messageFactory);
97+
98+
acceptor.start();
99+
// ... run your application ...
100+
acceptor.stop();
101+
}
102+
}
103+
```
104+
105+
## Thread Safety Guidance
106+
107+
Regardless of which strategy you choose, note that `Session.sendToTarget()` is thread-safe and may
108+
be called from any thread to send outgoing messages.
109+
110+
When using `ThreadedSocketAcceptor` or `ThreadedSocketInitiator`, ensure that any shared state
111+
accessed in your `Application` implementation (e.g., order books, maps, counters) is properly
112+
synchronized or uses thread-safe data structures.
113+
114+
---
115+
116+
*For a deep technical reference on the threading internals, see [`threading-model.md`](./threading-model.md).*

0 commit comments

Comments
 (0)