Version 9 (modified by 15 years ago) ( diff ) | ,
---|
Threading and the SystemQueue
Ariba makes it easy to avoid threading issues like synchronization and upcoming problems like deadlocks, race conditions, or access violations. The threading model in Ariba is completely serial, parallelism is avoided whenever possible. If you are using Ariba to write a service or application, two simple guidelines apply:
- Code event driven
- Don't block or wait
- Create no threads
When you comply with these two points, you will never get into any threading problems!
Overview of Threading in Ariba
Ariba runs one main thread, called the SystemQueue. All events in Ariba originate the SystemQueue, that means that all functionality is triggered by events that are placed into the SystemQueue. As all functionality is triggerd through the SystemQueue, only one thread exists in Ariba and you can be sure that all code always runs in sync. The concept of one main queue exists in several software projects, and in gernal is called Event loop:
- MS Windows uses one main thread in GUI applications (also called the main loop) to schedule and execute GUI changes (details on this are available here. The
WinMain
function in Windows applications that are linked against the Windows Subsystem is such an event loop. It uses the Win32 APIGetMessage
,TranslateMessage
, andDispatchMessage
to implement serial message processing in Windows. - OMNeT++ has an event queue that completely handles execution of simulations (called cScheduler. If you compile Ariba with simulation support, the SystemQueue gets synchronized with the OMNeT++ event queue for seamless integration.
- The X Windows System uses event queues in XLib and GLib.
SystemQueue Internals
The SystemQueue class has one main thread that uses boost::condition
with the wait
and signal
functionality to implement a queue. The thread blocks and waits until a new event is inserted into the queue. It then executes this event and calls the event handler that is assigned to this event. The insertion and execution of an event therefore are decoupled in terms of threading and run asynchronous.
One example of events are imcoming network messages. When a packet comes in from the network a new event is generated and the packet attacked to this event. The event is inserted into the SystemQueue and gets processed asynchronous from the SystemQueue thread.
Using the SystemQueue
Every mechanisms that gets triggered by some external event must get synchronized with the main SystemQueue thread. If you e.g. observe the network card and want to execute some stuff on network changes, you must synchronized with the SystemQueue. Why? Because a network packet might get processed and call some code that you would maybe call, too. Therefore producing parallel access to some code and causing threading problems.
Synchronizing with the main Ariba thread is easy, thanks to the SystemQueue. We will not explain step-by-step how to use the SystemQueue in your code:
- In you
.h
file integrate the SystemQueue header and someusing
declerations:
#include "ariba/utility/system/SystemQueue.h" using ariba::utility::SystemQueue; using ariba::utility::SystemEvent; using ariba::utility::SystemEventType; using ariba::utility::SystemEventListener;
- Derive your class from from a
SystemEventListener
to be able to receive events:
class MyClass : public SystemEventListener
- Redefine the
SystemEventListener
interface:
protected: virtual void handleSystemEvent( const SystemEvent& event );
- Next, define a new event type for you in your
.cpp
file. This event will be scheduled into the SystemQueue:
SystemEventType MyEventType("My new event type!");
- In your functionality where you want to synchronized yourself into the main Ariba thread using the SystemQueue, create the event and insert it. You can attach additional information as a
void*
to the event that you can extract (here, calledmyInfo
), once in the event handler function:
SystemQueue::instance().scheduleEvent( SystemEvent( this, MyEventType, myInfo));
- Your event handler will be called asynchronously and in sync with the Ariba main thread. You can also extract the additional information (
myInfo
) from the event. In this handler function you are the only event that is currently executed. Therefore, you can be sure that no other code in executing in parallel and need not to take any care of threading issues or mutexes.
void MyClass::handleSystemEvent(const SystemEvent& event){ MyInfo* info = event.getData<MyInfo>(); .... }
Using the EnterMethod functionality
If using events seems to complicated, Ariba provides an easier method called EnterMethod. It simply interrupts the Ariba thread, puts it into a safe state so you can call whatever function you require on the Ariba interface.
#include ariba/utility/system/EnterMethod.h" using ariba::utility::EnterMethod; ... // other thread EnterMethod e; e.enter(); // ariba thread halted in save state // run your code ...
Using Blocking Code
There may be times, when you wish you could use blocking functionality in your code. This is possible with the help of the BlockingMethod
function provided by Ariba. If you would just block, this would stall the complete Ariba architecture. Therefore, if you need to do blocking functionality, two things have to be taken care of:
- Switching into a new thread where blocking does not affect the main Ariba thread.
- Synchronizing back into the main Ariba thread to perform notification etc. of other components if needed.
Both of these issues are handled by the BlockingMethod
class that you can use to easily implement your blocking functionality. To implement blocking functionality, perform the following steps:
- Include the header and using directives for the
BlockingMethod
in your.h
file:
#include ariba/utility/system/BlockingMethod.h" using ariba::utility::BlockingMethod;
- Derive a new class from
BlockingMethod
:
class MyClass : public BlockingMethod {
- Declare the pure virtual function from
BlockingMethod
in your new class:
virtual void dispatchFunction(); virtual void blockingFunction();
- Finally, implement those two functions in your
.cpp
file
void MyClass::dispatchFunction(){ ... } void MyClass::blockingFunction(){ ... }
- Write the code that blocks execution into the
blockingFunction
. Implement code that you want to execute once synchronized with the main thread after the blocking into thedispatchFunction
. In thedispatchFunction
you can safely call other modules etc. from Ariba, because this function will be executed by the SystemQueue once in sync with the main thread!
- From your
blockingFunction
you can then callMyClass::dispatch
to get synchronized back into the main thread once your blocking functionality is done. This will result inMyClass::dispatchFunction
being called asynchronously and in sync with the main thread.
The mechanism works by running the blocking functionality automatically in a new thread, therefore not blocking execution of the main Ariba thread. Once you call dispatch
an event is generated that results in calling dispatchFunction
from the main thread.