Thread Safety

QWFIX Order Manager uses a global lock to achieve thread safety. FIXOrderManager.SyncRoot is the global synchronization object used across all classes.

FIXOrderManager.SyncRoot is automatically acquired during every method call to FIXOrder, FIXOrderManager, FIXSessionOrderManager classes.

FIXOrderManager.SyncRoot is also automatically acquired during the course of all events defined in QWFIX Order Manager classes.

FIXOrderManager.SyncRoot greatly simplifies the synchronization of the entire order handling process. It is recommended that the FIXOrderManager.SyncRoot should be the only synchronization object in the system that uses Order Manager. In that case the possibility of dead-lock will be completely eliminated.

Of course the simplicity is achieved at the cost of performance. Even though the FIX engine enables sending and receiving messages through different FIX sessions from different thread in parallel, if the Order Manager is used, the FIXOrderManager.SyncRoot will become the bottle neck of the system. However, because the QWFIX message handling is so efficient, even with the global lock the system can still achieve a constant throughput of over 10,000 FIX messages per second, which should be more than enough for most applications.

 

Using Global Lock

The application may still need to explicitly acquire the FIXOrderManager.SyncRoot sometimes.

The scenario described below is the most common occasion where the use of global lock is recommended.

Suppose application needs to send a new order by calling FIXSessionOrderManager.AddNewRegularOrder, save the returned instance of FIXRegularOrder somewhere. In the event handler of execution reports, the application needs to retrieve the previously saved FIXRegularOrder and perform some operation.

The potential problem is, QWFIX handles outgoing and incoming messages in two different threads. It's possible that the execution report is received and processed by another thread before the sender thread saves the returned order, which will result in some unexpected behavior.

In that case, the sender thread should acquire FIXOrderManager.SyncRoot, which will prevent the execution event from being triggered before the lock is released.

 

Asynchronous Message Communication and Global Lock

The FIX protocol dictates that the communication of order and execution messages are delivered asynchronously. Consider the scenario below:

Application gets the last execution message from an order. It inspects the "CumQty" and realizes that the order is not completely filled. It then inspects the "Price" and decides that it need to replace the order with a new price. It then create a "replace order" and send it using the order management API.

Some people may raise the questions. What if during the process a new execution is received? Is the above procedure "flawed"?

Although there is no definite answer to the questions above, we believe the procedure above is correct and we highly recommend it. Here is our argument:

Even if we just ignore the new execution and just send the "replace order", the FIX order handling protocol is designed to correctly handle the situation (so is our order management API). The counter-party may jsut send us a "cancel reject".

Even if we don't receive a new execution during the process, it doesn't mean the counter-party did not send it. It is possible that the system is still communicating the message over the wire and we have not received it yet.

So in summary, we recommend NOT to use global lock in dynamicly order modification. We recommend the following steps instead:

  1. Get the last execution message from FIXRegularOrder.LastExecution. Note LastExecution may be null if not execution has been received yet.
  2. Get order information from the last execution message, such as "CumQty", "AvgPx" etc.
  3. Make decision from the above information, if a modification is required (cancel or replace), use order handling API to create the modification message (FIXRegularOrder.CreateOrderCancelRequest or FIXRegularOrder.CreateOrderReplaceRequest) and send it (FIXRegularOrder.AddOrderCancelRequest or FIXRegularOrder.AddOrderReplaceRequest).

Isn't it simple enough?

 

Also Keep in Mind

The status of any order is almost always subject to change made by the session message handler thread or other application created threads. So if the application is trying to change the status of the order by adding message to it (on either buy side or sell side sessions), do keep in mind it may not be thread safe to do so. For example, if the application wants to generate a unique ExecID using FIXOrder.Count, it's always a good practice to do it within the locked FIXOrderManager.SyncRoot context until the execution is added. Or application developer can figure some other way to generate the ID that is independent to the "Count" property.

Nevertheless, we don't intend to teach multi-threading programming in this manual.