¿ªÔÆÌåÓý

ctrl + shift + ? for shortcuts
© 2025 Groups.io

questions about the two thread design (TWS c++ api)


 

I'm trying to reason about the c++ TWS API, and according to the documentation

One thread is used for sending messages to TWS, and another thread is used for reading returned messages. The second thread uses the API EReader class to read from the socket and add messages to a queue. Everytime a new message is added to the message queue, a notification flag is triggered to let other threads now that there is a message waiting to be processed. In the two-thread design of an API program, the message queue is also processed by the first thread.
I'm trying to modify variables in callbacks that read information, and read them to send messages in other methods. Here are my questions:

1. Each callback is only controlled by one and only one thread, I hope. Is that right?
2. std::unique_ptr<EReader> m_pReader is the subordinate thread that triggers all these pure virtual methods that report information from TWS, right? The main thread calls methods explicitly in TestCppClient's definition, and I can see those.
3.? EClientSocket * const m_pClient sends information *to* TWS, but uses the same shared data structure as m_pReader (they both look at EReaderOSSignal m_osSignal, for example)? Is all the shared stuff written by IBKR thread safe? I think I would've heard about it by now they weren't. That would be pretty crazy.


 

The call to EReader->start() creates a thread that listens for messages coming in from TWS. When a message comes in it is added to a queue. That is all that thread does.

In your application you create a thread that loops waiting for the signal that something is on the queue. It is this thread that dispatches to your EWrapperImpl callbacks.?

So in C++ the message reading and the callbacks are on separate threads.?


 

... I should add that the clientS octet which sends messages to TWS will be in your main thread. Could be your GUI thread if you have a GUI app. Those calls are immediate and don't have overhead.


 

Oh, so I don't need to worry about race conditions at all do I? I'm just going through TestCppClient::processMessages() over and over again? Do something based on m_state, then receive information from callbacks. Got it.



 

You don't need to worry about contention between the message reader and sender but you do have to care about possible race conditions between your message dispatch loop and your main thread.

i.e. The callbacks return data in one thread which you will likely use in your main thread.

However that type of contention is related to your own app design rather than the API.

On Sun, 8 Oct 2023, 22:14 , <tbrown122387@...> wrote:
Oh, so I don't need to worry about race conditions at all do I? I'm just going through TestCppClient::processMessages() over and over again? Do something based on m_state, then receive information from callbacks. Got it.



 

Thank you for following up because I actually did *not* understand.

¡±It is this [the main] thread that dispatches to your EWrapperImpl callbacks.¡± This makes it sound like all reading and writing to state variables I care about is done in one thread, the main thread.?


¡°
So in C++ the message reading and the callbacks are on separate threads.¡± This makes it sound like two threads are calling functions that read/write important state variables, which means I have to be more careful.?

so to confirm: callbacks like execDetails and orderStatus and tickByTickLast can get called by a separate thread, possibly leading to race conditions, which necessitates synchronization of state variables such as current position, etc.


 

No
Assuming you use a basic implementation, as in the supplied example. You do not have more default processing thread than your number of eReader, typically a single one.
So then all callbacks are processed in same thread as the one that process messages. IB hide for you the multithread queuing of messages in your processing thread, you don't have to worry about it.

Hence they are processed sequentially, This could be in a very different order than request messages, typically "orders" messages are processed faster than "data request" messages)

Typically the reader loop is made of
?? ???? while( client.isConnected()) {
?? ???? ??? client.processMessages();
?? ???? }

and zooming in client.processMessages(); you typically find
??? m_osSignal.waitForSignal();
?? ?errno = 0;
?? ?m_pReader->processMsgs();

How it work:
In same thread (and in the supplied example in main() thread )

on an Inbound Message=>Free m_osSignal.waitForSignal(),
then typical next step is to run a m_pReader->processMsgs(); => which loop on all existing messages (Normally one but could be more that arrived since you get there, this depend upon how slow you process a previous salvo of messages) ,
Messages? are process in the order they arrived using a EDecoder processMsgsDecoder_;

Look code in
void EReader::processMsgs(void)

and? in
int EDecoder::parseAndProcessMsg(const char*& beginPtr, const char* endPtr)

No mystery.

If you want to see how message queue is thread safe, look at EReader::getMsg(void)? you will see a scope lock method using m_csMsgQueue as lock


 

To add to Gordon's answer:
One thread does I/O, parsing of network messages and formatting of network messages.? The other thread gets callbacks, does your application logic and makes requests to the API.

Hunter


On Tuesday, October 10, 2023 at 12:11:09 PM PDT, Gordon Eldest via groups.io <hymagik@...> wrote:


No
Assuming you use a basic implementation, as in the supplied example. You do not have more default processing thread than your number of eReader, typically a single one.
So then all callbacks are processed in same thread as the one that process messages. IB hide for you the multithread queuing of messages in your processing thread, you don't have to worry about it.

Hence they are processed sequentially, This could be in a very different order than request messages, typically "orders" messages are processed faster than "data request" messages)

Typically the reader loop is made of
?? ???? while( client.isConnected()) {
?? ???? ??? client.processMessages();
?? ???? }

and zooming in client.processMessages(); you typically find
??? m_osSignal.waitForSignal();
?? ?errno = 0;
?? ?m_pReader->processMsgs();

How it work:
In same thread (and in the supplied example in main() thread )

on an Inbound Message=>Free m_osSignal.waitForSignal(),
then typical next step is to run a m_pReader->processMsgs(); => which loop on all existing messages (Normally one but could be more that arrived since you get there, this depend upon how slow you process a previous salvo of messages) ,
Messages? are process in the order they arrived using a EDecoder processMsgsDecoder_;

Look code in
void EReader::processMsgs(void)

and? in
int EDecoder::parseAndProcessMsg(const char*& beginPtr, const char* endPtr)

No mystery.

If you want to see how message queue is thread safe, look at EReader::getMsg(void)? you will see a scope lock method using m_csMsgQueue as lock


 

Yes, I was able to find those callback invocations in all?EDecoder.cpp as well. However, this just proves that calling *is* done in the main thread, not that it *isn't done in the other thread*. Apologies for being pedantic.

The reasons I ask are? a.) some threads on this forum mention calls can be done from either thread, and b.) it's difficult to grep every single callback of my EWrapper implementation in the source code.

I only grepped the ones I'm interested in and indeed they're all *only* being called in EDecoder.cpp, so you're probably right.


 

It proves that callbacks CANNOT be process anywhere else.
It proves that your callbacks will be processed in same thread and sequentially. (if you have a single eReader which is the typical and the default implementation)

Not sure I follow your concern or understand the case you mention, "isn't done in the other thread*. " ? Which other thread ? Launched by what ?
Message are thrown away once processed and 'callbacked'. Even if a dormant thread exist somewhere (how was it launched ?) it won't catch anything.

Note:
Don't try to create multiple eReader, it's touchy and not the smartest way to parallelize processing.
Massive but blind multi-threading is not the solution. You are serialized by the recv from the connection.
If speed is of the essence you will rapidly see that you need to sort urgent processing from lazy one, and this factored by long processing vs short.
So prioritizing will most likely lead to a message queue dispatching implementation to a thread pool of your own.? Make sense but this is NOT something to start with when "onboarding" the API and anyway it better be piggybacked on existing IB code
Trust a simple implementation, the IB Client codes sample are safe and well designed to make your life simple.

Note:
VisualStudio Code (work Mac/Linux) is pretty efficient to track calls of methods/procs, goes way beyond plain old 'grep', with a pretty developer display of results (clickable) Not tried on Linux.