OmniThreadLibrary internals - OtlComm
Today I'll describe the inner workings of the OmniThreadLibrary communication subsystem. It lives in the OtlComm unit and is used extensively by the task control/task worker interfaces (described in the previous installment). Its use is not limited to the OTL as it has no knowledge of tasks and threads. Feel free to use it in your own non-OTL-related code. At the surface, messaging subsystem looks deceptively simple. IOmniTaskControl interface exposes property Comm: IOmniCommunicationEndpoint. IOmniCommunicationEndpoint = interface ['{910D329C-D049-48B9-B0C0-9434D2E57870}'] This simple interface allows the owner to send messages, receive messages and wait on a new message. A property with a same name lives on the worker side, too (in the IOmniTask interface). Both endpoints are connected so that IOmniTaskControl.Comm.Send sends message to IOmniTask.Comm and vice versa. Simple. But when you look under the surface ... Here be dragons!This is the real picture of objects and interfaces living inside the OtlComm unit. Although the surface view may give you an impression that the IOmniTaskCommunication is the most important part of the communication system, in reality everything revolves around the IOmniTwoWayChannel.
To understand this picture, it's best to start at the bottom left (IOW, in the rectangle just above this line). The IOmniTaskControl interface is the one that is returned from the CreateTask procedure. IOmniTaskControl is implemented by the TOmniTaskControl object, which owns an IOmniTwoWayChannel interface, created during TOmniTaskControl initialization. procedure TOmniTaskControl.Initialize; This interface is also passed to the TOmniTask object (which implements IOmniTask interface) when task is run. function TOmniTaskControl.Run: IOmniTaskControl; TOmniTwoWayChannel owns two ring buffers of type TOmniRingBuffer (either the locking version, which is the default at the moment, or lock-free version if you compile the unit with /dOTL_LockFreBuffer). Those buffers are used as unidirectional message queues that store TOmniMessage records. TOmniMessage = record TOmniTwoWayChannel also owns two IOmniCommunicationEndpoint interfaces (implement by the TOmniCommunicationEndpoint class). One of them is exposed via the Enpoint1 property and another via Endpoint2. IOmniTwoWayChannel = interface ['{3ED1AB88-4209-4E01-AA79-A577AD719520}'] Both endpoints are connected to both ring buffers. If we name those ring buffers A and B, endpoint 1 writes to A and reads from B while endpoint 2 writes to B and reads from A. TOmniTaskControl.Comm and TOmniTask.Comm are simple mappers that return different endpoints of the same IOmniTwoWayChannel interface. function TOmniTaskControl.GetComm: IOmniCommunicationEndpoint; And that's all folks ... AutovivificationWell, that's almost all. The last trick in the OtlComm is creation of communication infrastructure on as-needed basis. As you can see from the diagram, the whole system is quite "heavy". If you're running a simple fire-and-forget tasks, you may not need it at all. That's why the ring buffers and communication endpoints are not created until they are used. The TOmniTwoWayChannel object is created whenever a task is created (see TOmniTaskControl.Initialize, above), but it does not create other parts of the infrastructure. This is only done in the endpoint accessor. procedure TOmniTwoWayChannel.CreateBuffers; The code to create Endpoint2 is similar except that it uses twcEndpoint[2] and reverses parameters passed to the TOmniCommunicationEndpoint.Create. The Endpoint1 method uses some tricks that may not be obvious.
That concludes the OtlComm tour. The only remaining part (until the thread pool is implemented) is the TOmniTaskEventDispatch component, which I'll cover in a day or two. Labels: Delphi, multithreading, OmniThreadLibrary, open source, programming, source code, work in progress |
3 Comments:
Hi Gabr,
Been following your posts with interest, and am learning a lot of new things with your latest OmniThreadLibrary!
Thanks again!
> DWORD-aligned ... atomic on the Intel
Is this absolutely true even for multicore/multiprocessor computers ? (Just to be sure ...)
absolutely
Post a Comment
Links to this post:
Create a Link
<< Home